Index: LMDZ6/branches/Amaury_dev/tools/fcm/.github/workflows/tests.yaml
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/.github/workflows/tests.yaml	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/.github/workflows/tests.yaml	(revision 5095)
@@ -0,0 +1,44 @@
+on: [pull_request, workflow_dispatch]
+
+jobs:
+  test:
+    strategy:
+      fail-fast: False
+      matrix:
+        ubuntu: ['ubuntu-20.04', 'ubuntu-22.04']
+    name: FCM Tests ${{matrix.ubuntu}}
+    runs-on: ${{matrix.ubuntu}}
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      - name: Install
+        run: |
+          sudo apt update
+          sudo apt install -y \
+            gfortran-9 \
+            libconfig-inifiles-perl \
+            libxml-parser-perl \
+            libdbi-perl \
+            libdbd-sqlite3-perl \
+            libsvn-perl \
+            s-nail \
+            subversion
+          [[ ${{matrix.ubuntu}} != "ubuntu-22.04" ]] && sudo apt install -y python-subversion || true
+          [[ ${{matrix.ubuntu}} == "ubuntu-22.04" ]] && sudo apt install -y python2 || true
+          echo "#!/bin/bash" >"${PWD}/bin/gfortran"
+          echo 'exec gfortran-9 "$@"' >>"${PWD}/bin/gfortran"
+          chmod +x "${PWD}/bin/gfortran"
+
+      - name: Check Perl Version
+        run: perl --version
+
+      - name: Check SVN Version
+        run: svn --version
+
+      - name: Run tests
+        run: |
+          export LOGNAME="${USER}"
+          bin/fcm test-battery -j8
Index: LMDZ6/branches/Amaury_dev/tools/fcm/.gitignore
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/.gitignore	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/.gitignore	(revision 5095)
@@ -0,0 +1,2 @@
+*.swp
+etc/fcm/*.cfg
Index: LMDZ6/branches/Amaury_dev/tools/fcm/.mailmap
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/.mailmap	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/.mailmap	(revision 5095)
@@ -0,0 +1,11 @@
+Ben Fitzpatrick      <ben.fitzpatrick@metoffice.gov.uk>   benfitzpatrick      <ben.fitzpatrick@metoffice.gov.uk>
+David Matthews       <david.matthews@metoffice.gov.uk>    dpmatthews          <david.matthews@metoffice.gov.uk>
+Matt Pryor           <matt.pryor@metoffice.gov.uk>        Matt Pryor          <mkjpryor@gmail.com>
+Matt Pryor           <matt.pryor@metoffice.gov.uk>        mkjpryor            <mkjpryor@gmail.com>
+Matt Shin            <matthew.shin@metoffice.gov.uk>      matthew.shin        <matthew.shin@metoffice.gov.uk>
+Matt Shin            <matthew.shin@metoffice.gov.uk>      matthewrmshin       <matthew.shin@metoffice.gov.uk>
+Matt Shin            <matthew.shin@metoffice.gov.uk>      matthewrmshin       <matthewrmshin@yahoo.co.uk>
+Roddy Sharp          <roddy.sharp@metoffice.gov.uk>       r-sharp             <r-sharp@users.noreply.github.com>
+Stuart Whitehouse    <stuart.whitehouse@metoffice.gov.uk> scwhitehouse        <stuart.whitehouse@metoffice.gov.uk>
+Martin Dix           <martin.dix@csiro.au>                MartinDix           <martin.dix@csiro.au>
+Kerry Day            <kerry.day@metoffice.gov.uk>         Kerry Day           <kaday@users.noreply.github.com>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/ACKNOWLEDGEMENT.md
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/ACKNOWLEDGEMENT.md	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/ACKNOWLEDGEMENT.md	(revision 5095)
@@ -0,0 +1,19 @@
+# Acknowledgement for non-FCM Work
+
+Licences for non-FCM works included in this distribution can be
+found in the licences/ directory.
+
+Non-FCM works included in this distribution are listed below:
+
+doc/etc/bootstrap/:
+* Unmodified external software library copyright 2011-2015 Twitter, Inc
+  released under the MIT license.
+  See [Bootstrap](http://getbootstrap.com/).
+
+svn-hooks/svnperms.py:
+* Subversion repository pre-commit path-based permission checking utility,
+  written by [Gustavo Niemeyer](mailto:gustavo@niemeyer.net) and released under
+  Apache 2.0 license.
+  Original source downloaded from r1295006 at:
+  https://svn.apache.org/viewvc/subversion/trunk/tools/hook-scripts/svnperms.py
+  This version is modified to allow custom permission message per repository.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/CHANGES.md
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/CHANGES.md	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/CHANGES.md	(revision 5095)
@@ -0,0 +1,761 @@
+# FCM Changes
+
+Go to https://github.com/metomi/fcm/milestones?state=closed
+for a full listing of issues for each release.
+
+--------------------------------------------------------------------------------
+
+## 2021.05.0 (2021-05-20)
+
+FCM release 43.
+
+### Noteworthy Changes
+
+[260](https://github.com/metomi/fcm/pull/260)
+Fix bug where fcm would ignore modules that begin with function/subroutine.
+
+--------------------------------------------------------------------------------
+
+## 2019.09.0 (2019-09-30)
+
+FCM release 42.
+
+### Noteworthy Changes
+
+[#256](https://github.com/metomi/fcm/pull/256):
+fcm make: build: correctly support Fortran submodule. A submodule depends on
+its parent module at compile time, but a parent module depends on its
+submodules at link time. This change also ensures that other modern syntax
+used in module/submodule definitions are handled correctly.
+
+--------------------------------------------------------------------------------
+
+## 2019.05.0 (2019-05-03)
+
+FCM release 41.
+
+### Noteworthy Changes
+
+[#253](https://github.com/metomi/fcm/pull/253):
+fcm make: build: fix handling of Fortran program unit tail comment. Fortran
+dependency analysis was not handling tail comment of a program unit correctly.
+This change fixes the issue.
+
+--------------------------------------------------------------------------------
+
+## 2017.10.0 (2017-10-03)
+
+FCM release 40.
+
+### Noteworthy Changes
+
+[#247](https://github.com/metomi/fcm/pull/247):
+fcm make: build: partially revert [#245](https://github.com/metomi/fcm/pull/245)
+so the link task will, as before, create a temporary dependent object library
+before linking the main object to it. However, users can now specify the
+`build.prop{link-without-ar}` setting in the configuration to prevent it from
+creating the temporary dependent object library, and put the full list of
+dependent object files in the linker command line instead.
+
+--------------------------------------------------------------------------------
+
+## 2017.09.0 (2017-09-27)
+
+FCM release 39.
+
+### Noteworthy Changes
+
+[#246](https://github.com/metomi/fcm/pull/246):
+fcm make: new `description` setting to allow users to specify any string to
+describe the current make.
+
+[#245](https://github.com/metomi/fcm/pull/245):
+fcm make: build: a link task for creating an executable will no longer create
+an archive library with the `ar` command before calling the linker. The linker
+command will contain the full list of object files instead.
+
+--------------------------------------------------------------------------------
+
+## 2017.02.0 (2017-02-24)
+
+FCM release 38.
+
+### Noteworthy Changes
+
+[#243](https://github.com/metomi/fcm/pull/243):
+fcm switch: fix Perl warning. This happened on an unexpected usage. A user has
+checked out an entire project tree, and then run `fcm switch trunk` on it.
+Clearly it did not work, but the added Perl warning was not helpful either.
+
+--------------------------------------------------------------------------------
+
+## 2016.12.0 (2016-12-06)
+
+FCM release 37.
+
+### Noteworthy Changes
+
+[#241](https://github.com/metomi/fcm/pull/241):
+fcm branch-delete: fix Perl warning on branches in a Rosie repository.
+
+[#240](https://github.com/metomi/fcm/pull/240):
+fcm make: fix implementation of `build.prop{ignore-missing-dep-ns}`.
+
+--------------------------------------------------------------------------------
+
+## 2016.10.0 (2016-10-07)
+
+FCM release 36.
+
+### Noteworthy Changes
+
+[#239](https://github.com/metomi/fcm/pull/239):
+fcm conflicts: handle tree conflict:
+*local file edit, incoming file replace upon merge*.
+
+[#238](https://github.com/metomi/fcm/pull/238):
+fcm make: use `TMPDIR` (if defined in the environment) for temporary files on
+load/dump of `ctx.gz`, instead of `/tmp/`.
+
+--------------------------------------------------------------------------------
+
+## 2016.09.0 (2016-09-19)
+
+FCM release 35. Minor bug fix, and minor update to admin utilities.
+
+### Noteworthy Changes
+
+[#235](https://github.com/metomi/fcm/pull/235):
+fcm make: build: fix `sprintf` redundant argument warning on Perl 5.22+.
+
+--------------------------------------------------------------------------------
+
+## 2016.05.1 (2016-05-19)
+
+FCM release 34. Minor update to admin utilities, nothing noteworthy for users.
+
+--------------------------------------------------------------------------------
+
+## 2016.05.0 (2016-05-05)
+
+FCM release 33. Minor bug fix and update.
+
+### Noteworthy Changes
+
+[#229](https://github.com/metomi/fcm/pull/229):
+fcm commit, branch-create, branch-delete, etc: the default graphical editor for
+commit message is modified from `gedit` to `gvim -f`, as `gedit` is not
+reliable for running in the foreground.
+
+[#227](https://github.com/metomi/fcm/pull/227):
+fcm make: on gunzip of the context file, pass a file handle rather than a path
+to the `gunzip` function to work around problems when files are made read-only
+via ACL.
+
+--------------------------------------------------------------------------------
+
+## 2016.02.0 (2016-02-11)
+
+FCM release 32. Minor bug fix and update.
+
+### Noteworthy Changes
+
+[#222](https://github.com/metomi/fcm/pull/222):
+Configuration file: fix variable substitution of value 0. A (environment)
+variable value of `0` was previously lost on substitution. This fixes the
+problem.
+
+[#219](https://github.com/metomi/fcm/pull/219):
+fcm make: extract: fix target status on conflict. On conflict, the target
+status was recorded incorrectly as unchanged. This caused incremental extracts
+to succeed in error. This change fixes the problem.
+
+[#215](https://github.com/metomi/fcm/pull/215):
+Improvements to version related functionality:
+* fcm version: now displays FCM_HOME location.
+* fcm make: new `require-version=MIN-VERSION [MAX-VERSION]` declaration.
+  If this declaration is specified and the current version is below
+  `MIN-VERSION` or if a `MAX-VERSION` is specified and the current version is
+  above it, the command will fail immediately.
+
+--------------------------------------------------------------------------------
+
+## 2015.12.0 (2015-12-09)
+
+FCM release 31. Minor update.
+
+### Noteworthy Changes
+
+[#214](https://github.com/metomi/fcm/pull/214):
+fcm make: `build.prop` declarations: modifiers such as `dep.include`, `dep.o`,
+`no-dep.include` and `no-dep.o` are now supported with namespaces that are
+target names.
+
+--------------------------------------------------------------------------------
+
+## 2015.11.0 (2015-11-17)
+
+FCM release 30. Minor update.
+
+### Noteworthy Changes
+
+[#208](https://github.com/metomi/fcm/pull/208):
+fcm make: build: now recognises more patterns as indicators of the existence of
+a main program in a C source file.
+
+[#207](https://github.com/metomi/rose/pull/207):
+fcm test-battery: now runs correctly in the absence of site/user configuration
+on recent Ubuntu distros.
+
+--------------------------------------------------------------------------------
+
+## 2015.10.0 (2015-10-15)
+
+FCM release 29. Minor update.
+
+### Noteworthy Changes
+
+[#205](https://github.com/metomi/fcm/pull/205):
+fcm make: build: new setting `build.prop{checksum-method}=md5|sha1|...` allows
+users to choose MD5 or one of SHA algorithms in Perls' `Digest::SHA` to
+calculate the checksums of source and target files.
+
+[#204](https://github.com/metomi/fcm/pull/204):
+fcm branch-create: allow `--bob` as a synonym of `--branch-of-branch`.
+
+--------------------------------------------------------------------------------
+
+## 2015.09.0 (2015-09-28)
+
+FCM release 28. Minor update to admin utilities, nothing noteworthy for users.
+
+--------------------------------------------------------------------------------
+
+## 2015.08.0 (2015-08-19)
+
+FCM release 27. Minor update to admin utilities, nothing noteworthy for users.
+
+--------------------------------------------------------------------------------
+
+## 2015.07.0 (2015-07-09)
+
+FCM release 26.
+
+### Noteworthy Changes
+
+[#197](https://github.com/metomi/fcm/pull/197):
+fcm make: build: fix target select modifier.
+* The `category` modifier should now work.
+* The `ns` modifier never worked, and is removed.
+  Instead `task` and `category` selection can now be filtered by name-space.
+
+[#196](https://github.com/metomi/fcm/pull/196):
+pre/post commit: improve changeset size diagnostic
+* pre-commit: log (but don't email) >1MB transactions
+* post-commit: report pre-commit size threshold (normally 10MB) for any >1MB
+  changesets
+
+[#192](https://github.com/metomi/fcm/pull/192):
+Primary location keyword: trailing slashes in values of primary location
+keywords will now removed automatically.
+
+[#191](https://github.com/metomi/fcm/pull/191):
+fcm (branch-)diff --graphical: will now use the option
+`--config-option config:working-copy:exclusive-locking-clients=` to prevent the
+client from being locked.
+
+--------------------------------------------------------------------------------
+
+## 2015.05.0 (2015-05-28)
+
+FCM release 25.
+
+### Highlighted Changes
+
+[#188](https://github.com/metomi/fcm/pull/188):
+fcm make: allow multiple runs in same directory. A make can now be named, so
+multiple non-overlapping makes can work in the same directory. It is worth
+noting that once you have used this version of `fcm make` on a directory, older
+version of FCM will no longer work in incremental mode on the same directory.
+See also [metomi/rose#1604](https://github.com/metomi/rose/pull/1604).
+
+### Noteworthy Changes
+
+[#190](https://github.com/metomi/fcm/pull/190):
+fcm make: new `--archive` option. If archive mode is specified, TAR-GZIP these
+items by default:
+* `.fcm-make/cache/extract/`
+* `build/include/`
+* `build/o/`
+
+where `extract` and `build` are names of extract and build steps.
+Users can configure what to TAR-GZIP under `build/`.
+
+[#189](https://github.com/metomi/fcm/pull/189):
+fcm make: correctly support relocation of a make.
+
+--------------------------------------------------------------------------------
+
+## 2015.03.0 (2015-03-26)
+
+FCM release 24.
+
+### Noteworthy Changes
+
+[#183](https://github.com/metomi/fcm/pull/183):
+Use of Subversion 1.6 with FCM is no longer recommended.
+
+[#181](https://github.com/metomi/fcm/pull/181):
+fcm make: source file find: ignore hidden paths only if paths are under
+the source root directory, i.e. it is now OK for the source root directory to
+be a hidden path itself.
+
+[#178](https://github.com/metomi/fcm/pull/178):
+fcm make: `.fcm-make/log` symbolic links are now relative. This should make it
+easier to relocate a build.
+
+--------------------------------------------------------------------------------
+
+## 2015.02.0 (2015-02-10)
+
+FCM release 23.
+
+### Noteworthy Changes
+
+[#176](https://github.com/metomi/fcm/pull/176):
+fcm make: build: fix creation of Fortran interface files where procedure
+arguments declared with `EXTERNAL` statements were incorrectly matched in
+case-sensitive mode.
+
+[#170](https://github.com/metomi/fcm/pull/170):
+fcm commit: now works in a working copy with *externals*.
+
+[#169](https://github.com/metomi/fcm/pull/169):
+fcm branch-delete: now fails if bad arguments specified.
+
+[#168](https://github.com/metomi/fcm/pull/168):
+fcm branch-diff: `--trac` now works correctly. The `/intertrac/` syntax did not
+appear to work any more. Use `/search?q=` syntax instead.
+
+--------------------------------------------------------------------------------
+
+## 2014.12.0 (2014-12-08)
+
+FCM release 22.
+
+### Noteworthy Changes
+
+[#164](https://github.com/metomi/fcm/pull/164):
+fcm build: fix fortran interface generation, broken by
+[#156](https://github.com/metomi/fcm/pull/156).
+
+[#163](https://github.com/metomi/fcm/pull/163):
+fcm merge --reverse: improve logic. It now works with non-standard layout. The
+`--revision=[M:]N` option is no longer compulsory. It now uses the last changed
+revision of the working copy by default.
+
+[#162](https://github.com/metomi/fcm/pull/162):
+fcm-manage-trac-env-session: fix session logic. If a user already has some
+entries in the `session_attribute` table, the old logic will not insert a new
+`name` or `email` for the user. The new logic will only skip the `name` or
+`email` attribute if already set correctly.
+
+--------------------------------------------------------------------------------
+
+## 2014.11.0 (2014-11-25)
+
+FCM release 21.
+
+### Noteworthy Changes
+
+[#161](https://github.com/metomi/fcm/pull/161):
+post-commit-bg-notify-who: use branch creator as branch owner if it cannot be
+determined by the branch name.
+
+[#159](https://github.com/metomi/fcm/pull/159),
+[#160](https://github.com/metomi/fcm/pull/160):
+FCM's Subversion wrappers: display error messages when `svn info` commands
+fail.
+
+[#158](https://github.com/metomi/fcm/pull/158):
+fcm make: extract: improve checking of primary location in inherited mode.
+Compare old value with normalised version of new value. E.g. use of location
+keywords and extra slashes at the end of the path will no longer result in a
+configuration conflict.
+
+[#156](https://github.com/metomi/fcm/pull/156):
+fcm extract and fcm build: fix warnings from Perl 5.12+ when parsing
+configuration file.
+
+[#155](https://github.com/metomi/fcm/pull/155):
+fcm make: fix report of no such config file.
+
+[#150](https://github.com/metomi/fcm/pull/150):
+fcm-manage-trac-env-session: improve removal logic.
+
+[#149](https://github.com/metomi/fcm/pull/149):
+svnperms.py: fix bug, alter message override, and add tests.
+
+[#148](https://github.com/metomi/fcm/pull/148):
+Reverse `commit.conf` logic. To verify branch owner, specify
+`verify-branch-owner`.  To notify branch owner, specify `notify-owner`.
+`post-commit-bg` now supports owner notification on trunk commit.
+
+[#147](https://github.com/metomi/fcm/pull/147):
+Use `dd conv=fsync` to create backup and dump to ensure that backup and dump
+data is written to disk before returning a good status.
+
+[#146](https://github.com/metomi/fcm/pull/146):
+fcm merge: basic support for `meld`.
+
+--------------------------------------------------------------------------------
+
+## 2014.09.0 (2014-09-17)
+
+FCM release 20.
+
+### Highlighted Changes
+
+[#138](https://github.com/metomi/fcm/pull/138):
+fcm make: build: continue on failure.
+* The build system will continue as much as possible after a failure, and
+  only repeat failed tasks in incremental modes.
+* This change also fixes a problem where the system could lose information
+  after a failure. Tasks that would be run after the failed task would not get
+  their context recorded correctly. In a subsequent incremental build, the
+  system would end up doing more work than necessary.
+
+[#135](https://github.com/metomi/fcm/pull/135):
+fcm make: multiple config files and search paths.
+* You can now specify multiple `-F PATH` options to specify the search paths
+  for locating configuration files specified as relative paths.
+* You can now specify multiple `-f FILE` options.
+* New `include-path` configuration declaration for specifying the search path
+  for configuration files specified as relative paths.
+* Improve CLI argument diagnostics.
+  * The command dies if an argument is missing an equal sign.
+  * Suggest command line syntax if argument ends with `.cfg`.
+
+[#129](https://github.com/metomi/fcm/pull/129),
+[#136](https://github.com/metomi/fcm/pull/136),
+[#143](https://github.com/metomi/fcm/pull/143),
+[#144](https://github.com/metomi/fcm/pull/144):
+Major improvements to the admin sub-system:
+* Improve hook installation.
+  Write, store and housekeep hook logs at `$REPOS/log/`.
+  Clean options for hook installation.
+  Install `svnperms.conf` from repository root.
+  `TZ=UTC` for all hook scripts.
+* Improve diagnostics for hooks.
+  Custom configuations per repositories.
+  Configurable `pre-revprop-change` permissions.
+  Hooks to work best under Subversion 1.8+.
+  Add modified `svnperms.py` in distribution.
+  Trac 0.12+ changeset added and modified notification.
+* Trac URL template.
+* `fcm-add-trac-env`: add Trac comment edit permission.
+* Separate `InterTrac` configurations from `trac.ini` into `intertrac.ini`.
+* Fix usage of `FCM_CONF_PATH` for admin.
+* Improve documentation and logic for admin configuration.
+* Get user info via LDAP or traditional Unix password file.
+* New admin commands:
+  * `fcm-add-svn-repos-and-trac-env`
+  * `fcm-add-svn-repos`
+  * `fcm-manage-trac-env-session`
+* `pre-commit`: optionally block branch create with bad owner.
+* `post-commit-bg`: rename repository dump.
+* `post-commit-bg`: optionally notify branch owner if author is not owner.
+* `post-*` hooks: configurable notification `From:` field.
+* Test batteries for hooks, and selected admin utilities.
+
+### Noteworthy Changes
+
+[#140](https://github.com/metomi/fcm/pull/140):
+fcm mkpatch: Changes required for use with svn 1.8 + other minor bug fixes.
+
+[#139](https://github.com/metomi/fcm/pull/139):
+fcm commit: fail a commit if it includes the `#commit_message#` file.
+
+[#137](https://github.com/metomi/fcm/pull/137):
+fcm merge: basic support for `kdiff3`.
+
+[#129](https://github.com/metomi/fcm/pull/129):
+`fcm commit`/`fcm branch-rm`: fix branch owner test to use correct user ID.
+
+--------------------------------------------------------------------------------
+
+## 2014.06.0 (2014-06-10)
+
+### Highlighted Changes
+
+-none-
+
+### Noteworthy Changes
+
+[#125](https://github.com/metomi/fcm/pull/125):
+fcm make: build: handle adjacent cyclic dependency correctly.
+
+[#128](https://github.com/metomi/fcm/pull/128):
+Remove unnecessary `-r`, `-w` and `-x` tests to avoid ACL problems.
+Use Perl's `filetest` pragma where necessary to correctly handle ACL.
+
+--------------------------------------------------------------------------------
+
+## 2014-04 (2014-04-23)
+
+### Highlighted Changes
+
+[#114](https://github.com/metomi/fcm/pull/#114),
+[#117](https://github.com/metomi/fcm/pull/#117),
+[#118](https://github.com/metomi/fcm/pull/#118):
+fcm make: build: now recognises statements with Fortran
+OpenMP sentinels that affect build dependencies.
+These dependencies are normally ignored.
+However, if a relevant `build.prop{fc.flag-omp}` property is specified, the
+build system will treat these statements as normal dependency statements.
+
+### Noteworthy Changes
+
+[#121](https://github.com/metomi/fcm/pull/#121):
+fcm make: extract via SSH: improve performance by using `find -printf`
+instead of `find -exec stat`.
+
+[#120](https://github.com/metomi/fcm/pull/#120):
+fcm make: build will now correctly handle C source files that has camel
+case names and `main` functions.
+
+[#111](https://github.com/metomi/fcm/pull/#111):
+fcm make: build in inherit mode: fix incorrect success in repeated
+incremental mode.
+
+[#105](https://github.com/metomi/fcm/pull/#105):
+`FCM_CONF_PATH`: new environment variable that can be used to override
+site/user configuration paths.
+
+[#103](https://github.com/metomi/fcm/pull/#103):
+fcm make: extract: detect diff trees that are the same as the base tree.
+
+--------------------------------------------------------------------------------
+
+## 2014-03 (2014-03-03)
+
+### Highlighted Changes
+
+[#96](https://github.com/metomi/fcm/pull/#96):
+fcm make: arguments as extra configurations. This change allows the
+`fcm make` command to accept command line arguments. Each argument will be
+appended in order as a new line in the current `fcm-make.cfg`. This allows
+users to override the configuration on the command line.
+
+### Noteworthy Changes
+
+[#101](https://github.com/metomi/fcm/pull/#101):
+fcm make: do not inherit `steps` if it is already set in the current
+configuration. This allows `steps=` to be declared before `use=`.
+
+[#100](https://github.com/metomi/fcm/pull/#100):
+fcm make: reduce memory usage in incremental mode. Invoking `fcm make`
+with many steps was causing Perl to exit with SIGSEGV previously.
+
+[#98](https://github.com/metomi/fcm/pull/#98):
+fcm make: extract: fix ssh location efficiency.
+
+[#93](https://github.com/metomi/fcm/pull/#93):
+fcm make: fix `use=` properties override. This change allows `use=`
+declarations to be placed anywhere in an `fcm-make.cfg` without interfering
+other `*.prop` declarations.
+
+[#92](https://github.com/metomi/fcm/pull/#92):
+fcm branch-create/list: support alternate username using information in
+users' `~/.subversion/servers` file.
+
+[#91](https://github.com/metomi/fcm/pull/#91):
+fcm make: remove config-on-success on failure.
+
+--------------------------------------------------------------------------------
+
+## 2014-02 (2014-02-03)
+
+### Highlighted Changes
+
+[#83](https://github.com/metomi/fcm/pull/#83):
+fcm make: build: an initial attempt to support some Fortran 2K features.
+* Recognise `iso_fortran_env` as an intrinsic module.
+* Recognise `use, intrinsic ::` statements.
+* Recognise `class`, `double complex` and `procedure` as types.
+* Recognise new type declaration attributes.
+* Recognise `abstract interface` blocks.
+* Recognise `impure elemental` as a valid function or subroutine attribute.
+* Recognise `submodule` blocks.
+
+### Noteworthy Changes
+
+[#89](https://github.com/metomi/fcm/pull/#89):
+fcm merge, fcm switch, etc: Subversion 1.8 `svn upgrade` command may
+not write a `.svn/entries` file at the working copy root. Several FCM wrappers
+were failing because they were unable to determine the working copy root. This
+is fixed by using the new entry available in Subversion 1.8 `svn info` to
+determine the working copy root.
+
+[#87](https://github.com/metomi/fcm/pull/#87):
+fcm make: build: print sources to targets diagnostics on `-vv` mode and
+in the log.
+
+--------------------------------------------------------------------------------
+
+## 2014-01 (2014-01-20)
+
+### Highlighted Changes
+
+-none-
+
+### Noteworthy Changes
+
+[#81](https://github.com/metomi/fcm/pull/#81):
+fcm make: build: fix cyclic dependency logic.
+
+[#80](https://github.com/metomi/fcm/pull/#80):
+fcm make: extract: support `extract.location` declarations reset.
+
+[#79](https://github.com/metomi/fcm/pull/#79):
+fcm make: extract: SSH location: ignore dot files.
+
+--------------------------------------------------------------------------------
+
+## 2013-12 (2013-12-02)
+
+### Highlighted Changes
+
+-none-
+
+### Noteworthy Changes
+
+[#77](https://github.com/metomi/fcm/pull/#77):
+fcm make: mirror and build: fix etc files install. This was broken by
+[#65](https://github.com/metomi/fcm/pull/#65)
+which causes etc files to be installed to `bin/`.
+
+[#74](https://github.com/metomi/fcm/pull/#74):
+Handle date in `svn log --xml`, which may have trailing spaces and lines.
+
+--------------------------------------------------------------------------------
+
+## 2013-11 (2013-11-22)
+
+### Highlighted Changes
+
+[#65](https://github.com/metomi/fcm/pull/#65):
+fcm make: support declaration of class default properties using the
+syntax e.g. `build.prop{class,fc}=my-fc`.
+
+[#65](https://github.com/metomi/fcm/pull/#65):
+fcm make: build: support target name as name-space for target properties,
+e.g. `build.prop{fc}[myprog.exe]=my-fc`. N.B. Dependency properties are
+regarded as source properties, and so are not supported by this change.
+
+### Noteworthy Changes
+
+[#73](https://github.com/metomi/fcm/pull/#73):
+fcm mkpatch: use `/usr/bin/env bash` in generated scripts.
+
+[#72](https://github.com/metomi/fcm/pull/#72):
+fcm conflicts: fix incompatibility with SVN 1.8.
+
+[#70](https://github.com/metomi/fcm/pull/#70):
+fcm CLI: support new SVN 1.8 commands.
+
+[#68](https://github.com/metomi/fcm/pull/#68):
+sbin/fcm-backup-\*: hotcopy before verifying the hotcopy.
+
+[#63](https://github.com/metomi/fcm/pull/#63):
+fcm make: log file improvements. Print FCM version in beginning of log
+file.
+
+[#63](https://github.com/metomi/fcm/pull/#63):
+fcm --version: new command to print FCM version.
+
+[#63](https://github.com/metomi/fcm/pull/#63):
+FCM is no longer dependent on the `HTTP::Date` Perl module.
+
+--------------------------------------------------------------------------------
+
+## 2013-10 (2013-10-30)
+
+### Highlighted Changes
+
+Changes that have significant impact on user experience.
+
+[#52](https://github.com/metomi/fcm/pull/#52):
+fcm make: build: new properties for C++ source files, separated from
+C source files. File extension for C and C++ source files is rationalised to
+follow what is documented in the GCC manual.
+
+[#50](https://github.com/metomi/fcm/pull/#50),
+[#54](https://github.com/metomi/fcm/pull/#54):
+fcm make: build/preprocess.prop: include-paths/lib-paths/libs:
+New build properties to specify a list of include paths for compile
+tasks, and library paths and libraries for link tasks.
+
+### Noteworthy Changes
+
+Bug fixes and minor enhancements:
+
+[#59](https://github.com/metomi/fcm/pull/#59):
+fcm make: fix invalid cyclic dependency error when `build.prop{dep.o}` is
+declared on the root name-space.
+
+[#58](https://github.com/metomi/fcm/pull/#58):
+fcm make: build: improve diagnostics for duplicated targets and bad values
+in `build.prop{ns-dep.o}` declarations.
+
+[#55](https://github.com/metomi/fcm/pull/#55):
+fcm make: extract: can now extract from a location that is accessible via
+`ssh` and `rsync`.
+
+[#53](https://github.com/metomi/fcm/pull/#53):
+fcm make: `.fcm-make/log` can now be accessed as `fcm-make.log`.
+
+[#51](https://github.com/metomi/fcm/pull/#51):
+FCM documentation: style updated using Bootstrap.
+
+--------------------------------------------------------------------------------
+
+## 2013-09 (2013-09-26)
+
+### Highlighted Changes
+
+Changes that have significant impact on user experience.
+
+-None-
+
+### Noteworthy Changes
+
+Bug fixes and minor enhancements:
+
+[#45](https://github.com/metomi/fcm/pull/#45):
+An attempt to allow FCM to work under a case insensitive file system.
+
+[#39](https://github.com/metomi/fcm/pull/#39),
+[#40](https://github.com/metomi/fcm/pull/#40),
+[#41](https://github.com/metomi/fcm/pull/#41):
+CM commands are now tested under Subversion 1.8.
+
+[#37](https://github.com/metomi/fcm/pull/#37):
+fcm make: build: fixed hanging of `ext-iface` tasks when there is an
+unbalanced quote or bracket in a relevant Fortran source file.
+
+[#20](https://github.com/metomi/fcm/pull/#20):
+fcm make: build: allow separate linker command and add ability to keep
+the intermediate library archive while linking an executable.
+
+[#19](https://github.com/metomi/fcm/pull/#19):
+added test suite for code management commands to the distribution.
+
+r4955: fcm extract: fix failure caused by the checking of latest version of a
+deleted branch.
+
+--------------------------------------------------------------------------------
+
+## FCM-2-3-1 and Prior Releases
+
+See <http://metomi.github.io/fcm/doc/release_notes/>.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/CONTRIBUTING.md
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/CONTRIBUTING.md	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/CONTRIBUTING.md	(revision 5095)
@@ -0,0 +1,84 @@
+# FCM: How to Contribute
+
+## Report Bugs
+
+Report bugs and request enhancement by opening an issue on
+[FCM issues @ Github](https://github.com/metomi/fcm/issues). If reporting a
+bug, add a recipe for repeating it. If requesting an enhancement,
+describe the use case in detail.
+
+## Contribute Code
+
+All contributions to FCM are made via pull requests against the *master*
+branch of [metomi/fcm](https://github.com/metomi/fcm). New contributors
+should add their details to the [Code Contributors](#code-contributors)
+section of this file as part of their first request. The developer who
+reviews each pull request is responsible for checking that the
+contributor's name is listed in this file before merging the pull request
+into *master*.
+
+## Code Contributors
+
+The following people have contributed to this code under the terms of
+the Contributor Licence Agreement and Certificate of Origin detailed
+below:
+
+* Jim Bolton (Met Office, UK)
+* Andrew Clark (Met Office, UK)
+* Paul Cresswell (Met Office, UK)
+* Martin Dix (CSIRO, Australia)
+* Ben Fitzpatrick (Met Office, UK)
+* Dave Matthews (Met Office, UK)
+* Stephen Oxley (Met Office, UK)
+* Matt Pryor (Met Office, UK)
+* Roddy Sharp (Met Office, UK)
+* Matt Shin (Met Office, UK)
+* Stuart Whitehouse (Met Office, UK)
+* Tim Pillinger (Met Office, UK)
+* Joe Mancell (Met Office, UK)
+
+(All contributors are identifiable with email addresses in the version
+control logs or otherwise.)
+
+## Contributor Licence Agreement and Certificate of Origin
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I have
+    the right to submit it, either on my behalf or on behalf of my
+    employer, under the terms and conditions as described by this file;
+    or
+
+(b) The contribution is based upon previous work that, to the best of
+    my knowledge, is covered under an appropriate licence and I have
+    the right or permission from the copyright owner under that licence
+    to submit that work with modifications, whether created in whole or
+    in part by me, under the terms and conditions as described by
+    this file; or
+
+(c) The contribution was provided directly to me by some other person
+    who certified (a) or (b) and I have not modified it.
+
+(d) I understand and agree that this project and the contribution
+    are public and that a record of the contribution (including my
+    name and email address) is retained for the full term of
+    the copyright and may be redistributed consistent with this project
+    or the licence(s) involved.
+
+(e) I, or my employer, grant to the UK Met Office and all recipients of
+    this software a perpetual, worldwide, non-exclusive, no-charge,
+    royalty-free, irrevocable copyright licence to reproduce, modify,
+    prepare derivative works of, publicly display, publicly perform,
+    sub-licence, and distribute this contribution and such modifications
+    and derivative works consistent with this project or the licence(s)
+    involved or other appropriate open source licence(s) specified by
+    the project and approved by the
+    [Open Source Initiative (OSI)](http://www.opensource.org/).
+
+(f) If I become aware of anything that would make any of the above
+    inaccurate, in any way, I will let the UK Met Office know as soon as
+    I become aware.
+
+(The FCM Contributor Licence Agreement and Certificate of Origin is
+inspired by the Certificate of Origin used by Enyo and the Linux
+Kernel.)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/COPYING
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/COPYING	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/COPYING	(revision 5095)
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/README.md
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/README.md	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/README.md	(revision 5095)
@@ -0,0 +1,38 @@
+# FCM
+
+[![Build Status](https://travis-ci.org/metomi/fcm.svg?branch=master)](https://travis-ci.org/metomi/fcm)
+[![DOI](https://zenodo.org/badge/6402932.svg)](https://zenodo.org/badge/latestdoi/6402932)
+
+FCM: a modern Fortran build system,
+and wrappers to Subversion for scientific software development
+
+[Installation](http://metomi.github.io/fcm/doc/installation/) |
+[User Guide](http://metomi.github.io/fcm/doc/user_guide/) |
+[How to Contribute](CONTRIBUTING.md)
+
+## Copyright and Terms of Use
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+FCM is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+FCM is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with FCM. If not, see <http://www.gnu.org/licenses/>.
+
+FCM documentation is licensed under the British Open Government
+Licence. See doc/etc/fcm-terms-of-use.html and
+<http://www.nationalarchives.gov.uk/doc/open-government-licence/>
+
+See <http://metomi.github.io/fcm/doc/etc/fcm-terms-of-use.html>.
+
+## Acknowledgement for Non-FCM Work
+
+See [Acknowledgement for Non-FCM Work](ACKNOWLEDGEMENT.md).
Index: LMDZ6/branches/Amaury_dev/tools/fcm/bin/fcm_graphic_merge
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/bin/fcm_graphic_merge	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/bin/fcm_graphic_merge	(revision 5095)
@@ -0,0 +1,151 @@
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+my %IMPL = ('fcm-dummy-diff' => \&_fcm_dummy_diff,
+            'xxdiff' => \&_xxdiff,
+            'meld'   => \&_meld,
+            'kdiff3' => \&_kdiff3);
+
+my %UNRESOLVED = (
+    'nodecision' => "You have made no decision.\n",
+    'merged'     => "You have not resolved all the conflicts.\n",
+);
+
+if (!caller()) {
+    # 0 - no diff
+    # 1 - diff
+    # other return code - fatal
+    exit main(@ARGV);
+}
+
+sub main {
+    my $command = 'xxdiff';
+    if (exists($ENV{FCM_GRAPHIC_MERGE}) && $ENV{FCM_GRAPHIC_MERGE}) {
+        $command = $ENV{FCM_GRAPHIC_MERGE};
+    }
+    if (!exists($IMPL{$command})) {
+        die("$command: merge tool not yet supported.\n");
+    }
+    $IMPL{$command}->(@_);
+}
+
+sub _fcm_dummy_diff {
+    my ($base, $mine, $older, $yours) = @_;
+    my @command = (qw{diff3}, $mine, $older, $yours);
+    print(join(" ", @command) . "\n");
+    my @out_lines = qx{@command};
+    for my $line (@out_lines) {
+        print($line);
+    }
+    return 0;
+}
+
+sub _xxdiff {
+    my ($base, $mine, $older, $yours) = @_;
+    my @command = (qw{xxdiff -m -M}, $base, qw{-O -X}, $mine, $older, $yours);
+    my @out_lines = qx{@command};
+    my $rc = $?;
+    if (!@out_lines) {
+        return 2;
+    }
+    my ($decision) = map {chomp($_); lc($_);} @out_lines;
+    if ($rc && exists($UNRESOLVED{$decision})) {
+        print($UNRESOLVED{$decision});
+        return 1;
+    }
+    printf("You %s all the changes.\n", $decision);
+    return 0;
+}
+
+sub _kdiff3 {
+    my ($base, $mine, $older, $yours) = @_;
+    my @command = (qw{kdiff3 -o}, $base, $mine, $older, $yours);
+    my @out_lines = qx{@command};
+    my $rc = $?;
+    # kdiff3 produces a file called $base.orig, so we delete that
+    unlink $base . ".orig";
+    # kdiff3 doesn't produce any output, so we just assume a non-zero
+    # exit code means unresolved merges
+    if ($rc) {
+        print($UNRESOLVED{'merged'});
+        return 1;
+    }
+    printf("You merged all the changes.\n");
+    return 0;
+}
+
+sub _meld {
+    my ($base, $mine, $older, $yours) = @_;
+    # Not using --auto-merge option as it alters the middle pane before user
+    # can review changes.
+    my @command = (qw{meld -a -o}, $base, $mine, $older, $yours);
+    my @out_lines = qx{@command};
+    my $rc = $?;
+    # meld doesn't produce any output, so we just assume a non-zero
+    # exit code means unresolved merges
+    if ($rc) {
+        print($UNRESOLVED{'merged'});
+        return 1;
+    }
+    printf("You merged all the changes.\n");
+    return 0;
+}
+
+__END__
+
+=head1 NAME
+
+fcm_graphic_merge
+
+=head1 SYNOPSIS
+
+    fcm_graphic_merge BASE MINE OLDER YOURS
+
+=head1 DESCRIPTION
+
+Invokes L<xxdiff|xxdiff> (or the command specified in the FCM_GRAPHIC_MERGE
+environment variable) to compare the CURRENT, ANCESTOR and NEW files, where possible.
+
+Wrap L<xxdiff|xxdiff>. Invoke L<xxdiff|xxdiff> as:
+
+    xxdiff -m -M BASE -O -X MINE OLDER YOURS
+
+Print friendlier decision messages.
+
+Return 0 if no diff remains, 1 if any diff remains, and 2 for fatal errors.
+
+=head1 ARGUMENTS
+
+BASE is the file you want to save the merge result into.
+
+MINE is the original file.
+
+YOURS is the file you want MINE to merge with.
+
+OLDER is the common ancestor of MINE and YOURS.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/bin/fcm_test_battery
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/bin/fcm_test_battery	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/bin/fcm_test_battery	(revision 5095)
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+cd $(dirname $0)/..
+if [[ "$PWD" != "$OLDPWD" ]]; then
+    echo "[INFO] cd $PWD"
+fi
+if [[ -f '/proc/cpuinfo' ]]; then
+    NPROC="$(grep -ic 'processor' '/proc/cpuinfo')"
+fi
+exec prove -j "${NPROC:-9}" -s -r "${@:-t}"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.css
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.css	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.css	(revision 5095)
@@ -0,0 +1,587 @@
+/*!
+ * Bootstrap v3.3.6 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+.btn-default,
+.btn-primary,
+.btn-success,
+.btn-info,
+.btn-warning,
+.btn-danger {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+}
+.btn-default:active,
+.btn-primary:active,
+.btn-success:active,
+.btn-info:active,
+.btn-warning:active,
+.btn-danger:active,
+.btn-default.active,
+.btn-primary.active,
+.btn-success.active,
+.btn-info.active,
+.btn-warning.active,
+.btn-danger.active {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-default.disabled,
+.btn-primary.disabled,
+.btn-success.disabled,
+.btn-info.disabled,
+.btn-warning.disabled,
+.btn-danger.disabled,
+.btn-default[disabled],
+.btn-primary[disabled],
+.btn-success[disabled],
+.btn-info[disabled],
+.btn-warning[disabled],
+.btn-danger[disabled],
+fieldset[disabled] .btn-default,
+fieldset[disabled] .btn-primary,
+fieldset[disabled] .btn-success,
+fieldset[disabled] .btn-info,
+fieldset[disabled] .btn-warning,
+fieldset[disabled] .btn-danger {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-default .badge,
+.btn-primary .badge,
+.btn-success .badge,
+.btn-info .badge,
+.btn-warning .badge,
+.btn-danger .badge {
+  text-shadow: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+}
+.btn-default {
+  text-shadow: 0 1px 0 #fff;
+  background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+  background-image:      -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
+  background-image:         linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #dbdbdb;
+  border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus {
+  background-color: #e0e0e0;
+  background-position: 0 -15px;
+}
+.btn-default:active,
+.btn-default.active {
+  background-color: #e0e0e0;
+  border-color: #dbdbdb;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+  background-color: #e0e0e0;
+  background-image: none;
+}
+.btn-primary {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #245580;
+}
+.btn-primary:hover,
+.btn-primary:focus {
+  background-color: #265a88;
+  background-position: 0 -15px;
+}
+.btn-primary:active,
+.btn-primary.active {
+  background-color: #265a88;
+  border-color: #245580;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+  background-color: #265a88;
+  background-image: none;
+}
+.btn-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
+  background-image:      -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #3e8f3e;
+}
+.btn-success:hover,
+.btn-success:focus {
+  background-color: #419641;
+  background-position: 0 -15px;
+}
+.btn-success:active,
+.btn-success.active {
+  background-color: #419641;
+  border-color: #3e8f3e;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+  background-color: #419641;
+  background-image: none;
+}
+.btn-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+  background-image:      -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #28a4c9;
+}
+.btn-info:hover,
+.btn-info:focus {
+  background-color: #2aabd2;
+  background-position: 0 -15px;
+}
+.btn-info:active,
+.btn-info.active {
+  background-color: #2aabd2;
+  border-color: #28a4c9;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+  background-color: #2aabd2;
+  background-image: none;
+}
+.btn-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #e38d13;
+}
+.btn-warning:hover,
+.btn-warning:focus {
+  background-color: #eb9316;
+  background-position: 0 -15px;
+}
+.btn-warning:active,
+.btn-warning.active {
+  background-color: #eb9316;
+  border-color: #e38d13;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+  background-color: #eb9316;
+  background-image: none;
+}
+.btn-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+  background-image:      -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #b92c28;
+}
+.btn-danger:hover,
+.btn-danger:focus {
+  background-color: #c12e2a;
+  background-position: 0 -15px;
+}
+.btn-danger:active,
+.btn-danger.active {
+  background-color: #c12e2a;
+  border-color: #b92c28;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+  background-color: #c12e2a;
+  background-image: none;
+}
+.thumbnail,
+.img-thumbnail {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  background-color: #e8e8e8;
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  background-color: #2e6da4;
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+  background-repeat: repeat-x;
+}
+.navbar-default {
+  background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+  background-image:      -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
+  background-image:         linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+  background-image:      -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
+  background-image:         linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+}
+.navbar-brand,
+.navbar-nav > li > a {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
+}
+.navbar-inverse {
+  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
+  background-image:      -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
+  background-image:         linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-radius: 4px;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+  background-image:      -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
+  background-image:         linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+}
+.navbar-inverse .navbar-brand,
+.navbar-inverse .navbar-nav > li > a {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
+}
+.navbar-static-top,
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  border-radius: 0;
+}
+@media (max-width: 767px) {
+  .navbar .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #fff;
+    background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+    background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+    background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+    background-repeat: repeat-x;
+  }
+}
+.alert {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+}
+.alert-success {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+  background-image:      -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #b2dba1;
+}
+.alert-info {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+  background-image:      -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #9acfea;
+}
+.alert-warning {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #f5e79e;
+}
+.alert-danger {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+  background-image:      -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dca7a7;
+}
+.progress {
+  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+  background-image:      -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
+  background-image:         linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #286090 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #286090 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+  background-image:      -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+  background-image:      -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+  background-image:      -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-striped {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.list-group {
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+  text-shadow: 0 -1px 0 #286090;
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #2b669a;
+}
+.list-group-item.active .badge,
+.list-group-item.active:hover .badge,
+.list-group-item.active:focus .badge {
+  text-shadow: none;
+}
+.panel {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+}
+.panel-default > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-primary > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-success > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+  background-image:      -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-info > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+  background-image:      -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-warning > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-danger > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+  background-image:      -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.well {
+  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+  background-image:      -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
+  background-image:         linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dcdcdc;
+  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+}
+/*# sourceMappingURL=bootstrap-theme.css.map */
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.css.map
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.css.map	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.css.map	(revision 5095)
@@ -0,0 +1,1 @@
+{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.6 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n  text-shadow: none;\n}\n.btn:active,\n.btn.active {\n  background-image: none;\n}\n.btn-default {\n  background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n  background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n  background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #dbdbdb;\n  text-shadow: 0 1px 0 #fff;\n  border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n  background-color: #e0e0e0;\n  background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n  background-color: #e0e0e0;\n  border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n  background-color: #e0e0e0;\n  background-image: none;\n}\n.btn-primary {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n  background-color: #265a88;\n  background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n  background-color: #265a88;\n  border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n  background-color: #265a88;\n  background-image: none;\n}\n.btn-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n  background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n  background-color: #419641;\n  background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n  background-color: #419641;\n  border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n  background-color: #419641;\n  background-image: none;\n}\n.btn-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n  background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n  background-color: #2aabd2;\n  background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n  background-color: #2aabd2;\n  border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n  background-color: #2aabd2;\n  background-image: none;\n}\n.btn-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n  background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n  background-color: #eb9316;\n  background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n  background-color: #eb9316;\n  border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n  background-color: #eb9316;\n  background-image: none;\n}\n.btn-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n  background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n  background-color: #c12e2a;\n  background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n  background-color: #c12e2a;\n  border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n  background-color: #c12e2a;\n  background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n  background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n  background-color: #2e6da4;\n}\n.navbar-default {\n  background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n  background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n  background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n  background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n  background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n  background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n  background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n  background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n  background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n  box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n@media (max-width: 767px) {\n  .navbar .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #fff;\n    background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n    background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n    background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n    background-repeat: repeat-x;\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n  }\n}\n.alert {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n  border-color: #b2dba1;\n}\n.alert-info {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n  border-color: #9acfea;\n}\n.alert-warning {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n  border-color: #f5e79e;\n}\n.alert-danger {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n  border-color: #dca7a7;\n}\n.progress {\n  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 #286090;\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n  border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n  text-shadow: none;\n}\n.panel {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n  border-color: #dcdcdc;\n  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.6 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  // Reset the shadow\n  &:active,\n  &.active {\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    .box-shadow(none);\n  }\n\n  .badge {\n    text-shadow: none;\n  }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n  #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n  .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n  background-repeat: repeat-x;\n  border-color: darken(@btn-color, 14%);\n\n  &:hover,\n  &:focus  {\n    background-color: darken(@btn-color, 12%);\n    background-position: 0 -15px;\n  }\n\n  &:active,\n  &.active {\n    background-color: darken(@btn-color, 12%);\n    border-color: darken(@btn-color, 14%);\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &,\n    &:hover,\n    &:focus,\n    &.focus,\n    &:active,\n    &.active {\n      background-color: darken(@btn-color, 12%);\n      background-image: none;\n    }\n  }\n}\n\n// Common styles\n.btn {\n  // Remove the gradient for the pressed/active state\n  &:active,\n  &.active {\n    background-image: none;\n  }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info    { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger  { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n  background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n  background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n  border-radius: @navbar-border-radius;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  .navbar-nav > .open > a,\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n  }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n  #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n  border-radius: @navbar-border-radius;\n  .navbar-nav > .open > a,\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n  }\n\n  .navbar-brand,\n  .navbar-nav > li > a {\n    text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n  }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n  .navbar .navbar-nav .open .dropdown-menu > .active > a {\n    &,\n    &:hover,\n    &:focus {\n      color: #fff;\n      #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n    }\n  }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n  text-shadow: 0 1px 0 rgba(255,255,255,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n  .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n  border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success    { .alert-styles(@alert-success-bg); }\n.alert-info       { .alert-styles(@alert-info-bg); }\n.alert-warning    { .alert-styles(@alert-warning-bg); }\n.alert-danger     { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n  #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar            { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success    { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info       { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning    { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger     { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n  #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n  border-radius: @border-radius-base;\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n  #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n  border-color: darken(@list-group-active-border, 7.5%);\n\n  .badge {\n    text-shadow: none;\n  }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n  .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading   { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading   { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading   { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading      { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading   { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading    { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n  #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n  border-color: darken(@well-bg, 10%);\n  @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n       -o-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n  -webkit-animation-fill-mode: @fill-mode;\n          animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  // Firefox\n  &::-moz-placeholder {\n    color: @color;\n    opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n  }\n  &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n  -webkit-transform: scale(@ratio);\n      -ms-transform: scale(@ratio); // IE9 only\n       -o-transform: scale(@ratio);\n          transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n  -webkit-transform: scale(@ratioX, @ratioY);\n      -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n       -o-transform: scale(@ratioX, @ratioY);\n          transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n  -webkit-transform: scaleX(@ratio);\n      -ms-transform: scaleX(@ratio); // IE9 only\n       -o-transform: scaleX(@ratio);\n          transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n  -webkit-transform: scaleY(@ratio);\n      -ms-transform: scaleY(@ratio); // IE9 only\n       -o-transform: scaleY(@ratio);\n          transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n  -webkit-transform: skewX(@x) skewY(@y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n       -o-transform: skewX(@x) skewY(@y);\n          transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n       -o-transform: translate(@x, @y);\n          transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n       -o-transform: rotate(@degrees);\n          transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n       -o-transform: rotateX(@degrees);\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n       -o-transform: rotateY(@degrees);\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n  -webkit-transition: @transition;\n       -o-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n  -webkit-transition-timing-function: @timing-function;\n          transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n    background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Opera 12\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.min.css
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.min.css	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.min.css	(revision 5095)
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap v3.3.6 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
+/*# sourceMappingURL=bootstrap-theme.min.css.map */
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.min.css.map
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.min.css.map	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap-theme.min.css.map	(revision 5095)
@@ -0,0 +1,1 @@
+{"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA"}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.css
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.css	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.css	(revision 5095)
@@ -0,0 +1,6760 @@
+/*!
+ * Bootstrap v3.3.6 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+html {
+  font-family: sans-serif;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+body {
+  margin: 0;
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+  display: block;
+}
+audio,
+canvas,
+progress,
+video {
+  display: inline-block;
+  vertical-align: baseline;
+}
+audio:not([controls]) {
+  display: none;
+  height: 0;
+}
+[hidden],
+template {
+  display: none;
+}
+a {
+  background-color: transparent;
+}
+a:active,
+a:hover {
+  outline: 0;
+}
+abbr[title] {
+  border-bottom: 1px dotted;
+}
+b,
+strong {
+  font-weight: bold;
+}
+dfn {
+  font-style: italic;
+}
+h1 {
+  margin: .67em 0;
+  font-size: 2em;
+}
+mark {
+  color: #000;
+  background: #ff0;
+}
+small {
+  font-size: 80%;
+}
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+sup {
+  top: -.5em;
+}
+sub {
+  bottom: -.25em;
+}
+img {
+  border: 0;
+}
+svg:not(:root) {
+  overflow: hidden;
+}
+figure {
+  margin: 1em 40px;
+}
+hr {
+  height: 0;
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+}
+pre {
+  overflow: auto;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+  margin: 0;
+  font: inherit;
+  color: inherit;
+}
+button {
+  overflow: visible;
+}
+button,
+select {
+  text-transform: none;
+}
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  -webkit-appearance: button;
+  cursor: pointer;
+}
+button[disabled],
+html input[disabled] {
+  cursor: default;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+input {
+  line-height: normal;
+}
+input[type="checkbox"],
+input[type="radio"] {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+  padding: 0;
+}
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+  -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+fieldset {
+  padding: .35em .625em .75em;
+  margin: 0 2px;
+  border: 1px solid #c0c0c0;
+}
+legend {
+  padding: 0;
+  border: 0;
+}
+textarea {
+  overflow: auto;
+}
+optgroup {
+  font-weight: bold;
+}
+table {
+  border-spacing: 0;
+  border-collapse: collapse;
+}
+td,
+th {
+  padding: 0;
+}
+/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
+@media print {
+  *,
+  *:before,
+  *:after {
+    color: #000 !important;
+    text-shadow: none !important;
+    background: transparent !important;
+    -webkit-box-shadow: none !important;
+            box-shadow: none !important;
+  }
+  a,
+  a:visited {
+    text-decoration: underline;
+  }
+  a[href]:after {
+    content: " (" attr(href) ")";
+  }
+  abbr[title]:after {
+    content: " (" attr(title) ")";
+  }
+  a[href^="#"]:after,
+  a[href^="javascript:"]:after {
+    content: "";
+  }
+  pre,
+  blockquote {
+    border: 1px solid #999;
+
+    page-break-inside: avoid;
+  }
+  thead {
+    display: table-header-group;
+  }
+  tr,
+  img {
+    page-break-inside: avoid;
+  }
+  img {
+    max-width: 100% !important;
+  }
+  p,
+  h2,
+  h3 {
+    orphans: 3;
+    widows: 3;
+  }
+  h2,
+  h3 {
+    page-break-after: avoid;
+  }
+  .navbar {
+    display: none;
+  }
+  .btn > .caret,
+  .dropup > .btn > .caret {
+    border-top-color: #000 !important;
+  }
+  .label {
+    border: 1px solid #000;
+  }
+  .table {
+    border-collapse: collapse !important;
+  }
+  .table td,
+  .table th {
+    background-color: #fff !important;
+  }
+  .table-bordered th,
+  .table-bordered td {
+    border: 1px solid #ddd !important;
+  }
+}
+@font-face {
+  font-family: 'Glyphicons Halflings';
+
+  src: url('../fonts/glyphicons-halflings-regular.eot');
+  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+.glyphicon {
+  position: relative;
+  top: 1px;
+  display: inline-block;
+  font-family: 'Glyphicons Halflings';
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1;
+
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+.glyphicon-asterisk:before {
+  content: "\002a";
+}
+.glyphicon-plus:before {
+  content: "\002b";
+}
+.glyphicon-euro:before,
+.glyphicon-eur:before {
+  content: "\20ac";
+}
+.glyphicon-minus:before {
+  content: "\2212";
+}
+.glyphicon-cloud:before {
+  content: "\2601";
+}
+.glyphicon-envelope:before {
+  content: "\2709";
+}
+.glyphicon-pencil:before {
+  content: "\270f";
+}
+.glyphicon-glass:before {
+  content: "\e001";
+}
+.glyphicon-music:before {
+  content: "\e002";
+}
+.glyphicon-search:before {
+  content: "\e003";
+}
+.glyphicon-heart:before {
+  content: "\e005";
+}
+.glyphicon-star:before {
+  content: "\e006";
+}
+.glyphicon-star-empty:before {
+  content: "\e007";
+}
+.glyphicon-user:before {
+  content: "\e008";
+}
+.glyphicon-film:before {
+  content: "\e009";
+}
+.glyphicon-th-large:before {
+  content: "\e010";
+}
+.glyphicon-th:before {
+  content: "\e011";
+}
+.glyphicon-th-list:before {
+  content: "\e012";
+}
+.glyphicon-ok:before {
+  content: "\e013";
+}
+.glyphicon-remove:before {
+  content: "\e014";
+}
+.glyphicon-zoom-in:before {
+  content: "\e015";
+}
+.glyphicon-zoom-out:before {
+  content: "\e016";
+}
+.glyphicon-off:before {
+  content: "\e017";
+}
+.glyphicon-signal:before {
+  content: "\e018";
+}
+.glyphicon-cog:before {
+  content: "\e019";
+}
+.glyphicon-trash:before {
+  content: "\e020";
+}
+.glyphicon-home:before {
+  content: "\e021";
+}
+.glyphicon-file:before {
+  content: "\e022";
+}
+.glyphicon-time:before {
+  content: "\e023";
+}
+.glyphicon-road:before {
+  content: "\e024";
+}
+.glyphicon-download-alt:before {
+  content: "\e025";
+}
+.glyphicon-download:before {
+  content: "\e026";
+}
+.glyphicon-upload:before {
+  content: "\e027";
+}
+.glyphicon-inbox:before {
+  content: "\e028";
+}
+.glyphicon-play-circle:before {
+  content: "\e029";
+}
+.glyphicon-repeat:before {
+  content: "\e030";
+}
+.glyphicon-refresh:before {
+  content: "\e031";
+}
+.glyphicon-list-alt:before {
+  content: "\e032";
+}
+.glyphicon-lock:before {
+  content: "\e033";
+}
+.glyphicon-flag:before {
+  content: "\e034";
+}
+.glyphicon-headphones:before {
+  content: "\e035";
+}
+.glyphicon-volume-off:before {
+  content: "\e036";
+}
+.glyphicon-volume-down:before {
+  content: "\e037";
+}
+.glyphicon-volume-up:before {
+  content: "\e038";
+}
+.glyphicon-qrcode:before {
+  content: "\e039";
+}
+.glyphicon-barcode:before {
+  content: "\e040";
+}
+.glyphicon-tag:before {
+  content: "\e041";
+}
+.glyphicon-tags:before {
+  content: "\e042";
+}
+.glyphicon-book:before {
+  content: "\e043";
+}
+.glyphicon-bookmark:before {
+  content: "\e044";
+}
+.glyphicon-print:before {
+  content: "\e045";
+}
+.glyphicon-camera:before {
+  content: "\e046";
+}
+.glyphicon-font:before {
+  content: "\e047";
+}
+.glyphicon-bold:before {
+  content: "\e048";
+}
+.glyphicon-italic:before {
+  content: "\e049";
+}
+.glyphicon-text-height:before {
+  content: "\e050";
+}
+.glyphicon-text-width:before {
+  content: "\e051";
+}
+.glyphicon-align-left:before {
+  content: "\e052";
+}
+.glyphicon-align-center:before {
+  content: "\e053";
+}
+.glyphicon-align-right:before {
+  content: "\e054";
+}
+.glyphicon-align-justify:before {
+  content: "\e055";
+}
+.glyphicon-list:before {
+  content: "\e056";
+}
+.glyphicon-indent-left:before {
+  content: "\e057";
+}
+.glyphicon-indent-right:before {
+  content: "\e058";
+}
+.glyphicon-facetime-video:before {
+  content: "\e059";
+}
+.glyphicon-picture:before {
+  content: "\e060";
+}
+.glyphicon-map-marker:before {
+  content: "\e062";
+}
+.glyphicon-adjust:before {
+  content: "\e063";
+}
+.glyphicon-tint:before {
+  content: "\e064";
+}
+.glyphicon-edit:before {
+  content: "\e065";
+}
+.glyphicon-share:before {
+  content: "\e066";
+}
+.glyphicon-check:before {
+  content: "\e067";
+}
+.glyphicon-move:before {
+  content: "\e068";
+}
+.glyphicon-step-backward:before {
+  content: "\e069";
+}
+.glyphicon-fast-backward:before {
+  content: "\e070";
+}
+.glyphicon-backward:before {
+  content: "\e071";
+}
+.glyphicon-play:before {
+  content: "\e072";
+}
+.glyphicon-pause:before {
+  content: "\e073";
+}
+.glyphicon-stop:before {
+  content: "\e074";
+}
+.glyphicon-forward:before {
+  content: "\e075";
+}
+.glyphicon-fast-forward:before {
+  content: "\e076";
+}
+.glyphicon-step-forward:before {
+  content: "\e077";
+}
+.glyphicon-eject:before {
+  content: "\e078";
+}
+.glyphicon-chevron-left:before {
+  content: "\e079";
+}
+.glyphicon-chevron-right:before {
+  content: "\e080";
+}
+.glyphicon-plus-sign:before {
+  content: "\e081";
+}
+.glyphicon-minus-sign:before {
+  content: "\e082";
+}
+.glyphicon-remove-sign:before {
+  content: "\e083";
+}
+.glyphicon-ok-sign:before {
+  content: "\e084";
+}
+.glyphicon-question-sign:before {
+  content: "\e085";
+}
+.glyphicon-info-sign:before {
+  content: "\e086";
+}
+.glyphicon-screenshot:before {
+  content: "\e087";
+}
+.glyphicon-remove-circle:before {
+  content: "\e088";
+}
+.glyphicon-ok-circle:before {
+  content: "\e089";
+}
+.glyphicon-ban-circle:before {
+  content: "\e090";
+}
+.glyphicon-arrow-left:before {
+  content: "\e091";
+}
+.glyphicon-arrow-right:before {
+  content: "\e092";
+}
+.glyphicon-arrow-up:before {
+  content: "\e093";
+}
+.glyphicon-arrow-down:before {
+  content: "\e094";
+}
+.glyphicon-share-alt:before {
+  content: "\e095";
+}
+.glyphicon-resize-full:before {
+  content: "\e096";
+}
+.glyphicon-resize-small:before {
+  content: "\e097";
+}
+.glyphicon-exclamation-sign:before {
+  content: "\e101";
+}
+.glyphicon-gift:before {
+  content: "\e102";
+}
+.glyphicon-leaf:before {
+  content: "\e103";
+}
+.glyphicon-fire:before {
+  content: "\e104";
+}
+.glyphicon-eye-open:before {
+  content: "\e105";
+}
+.glyphicon-eye-close:before {
+  content: "\e106";
+}
+.glyphicon-warning-sign:before {
+  content: "\e107";
+}
+.glyphicon-plane:before {
+  content: "\e108";
+}
+.glyphicon-calendar:before {
+  content: "\e109";
+}
+.glyphicon-random:before {
+  content: "\e110";
+}
+.glyphicon-comment:before {
+  content: "\e111";
+}
+.glyphicon-magnet:before {
+  content: "\e112";
+}
+.glyphicon-chevron-up:before {
+  content: "\e113";
+}
+.glyphicon-chevron-down:before {
+  content: "\e114";
+}
+.glyphicon-retweet:before {
+  content: "\e115";
+}
+.glyphicon-shopping-cart:before {
+  content: "\e116";
+}
+.glyphicon-folder-close:before {
+  content: "\e117";
+}
+.glyphicon-folder-open:before {
+  content: "\e118";
+}
+.glyphicon-resize-vertical:before {
+  content: "\e119";
+}
+.glyphicon-resize-horizontal:before {
+  content: "\e120";
+}
+.glyphicon-hdd:before {
+  content: "\e121";
+}
+.glyphicon-bullhorn:before {
+  content: "\e122";
+}
+.glyphicon-bell:before {
+  content: "\e123";
+}
+.glyphicon-certificate:before {
+  content: "\e124";
+}
+.glyphicon-thumbs-up:before {
+  content: "\e125";
+}
+.glyphicon-thumbs-down:before {
+  content: "\e126";
+}
+.glyphicon-hand-right:before {
+  content: "\e127";
+}
+.glyphicon-hand-left:before {
+  content: "\e128";
+}
+.glyphicon-hand-up:before {
+  content: "\e129";
+}
+.glyphicon-hand-down:before {
+  content: "\e130";
+}
+.glyphicon-circle-arrow-right:before {
+  content: "\e131";
+}
+.glyphicon-circle-arrow-left:before {
+  content: "\e132";
+}
+.glyphicon-circle-arrow-up:before {
+  content: "\e133";
+}
+.glyphicon-circle-arrow-down:before {
+  content: "\e134";
+}
+.glyphicon-globe:before {
+  content: "\e135";
+}
+.glyphicon-wrench:before {
+  content: "\e136";
+}
+.glyphicon-tasks:before {
+  content: "\e137";
+}
+.glyphicon-filter:before {
+  content: "\e138";
+}
+.glyphicon-briefcase:before {
+  content: "\e139";
+}
+.glyphicon-fullscreen:before {
+  content: "\e140";
+}
+.glyphicon-dashboard:before {
+  content: "\e141";
+}
+.glyphicon-paperclip:before {
+  content: "\e142";
+}
+.glyphicon-heart-empty:before {
+  content: "\e143";
+}
+.glyphicon-link:before {
+  content: "\e144";
+}
+.glyphicon-phone:before {
+  content: "\e145";
+}
+.glyphicon-pushpin:before {
+  content: "\e146";
+}
+.glyphicon-usd:before {
+  content: "\e148";
+}
+.glyphicon-gbp:before {
+  content: "\e149";
+}
+.glyphicon-sort:before {
+  content: "\e150";
+}
+.glyphicon-sort-by-alphabet:before {
+  content: "\e151";
+}
+.glyphicon-sort-by-alphabet-alt:before {
+  content: "\e152";
+}
+.glyphicon-sort-by-order:before {
+  content: "\e153";
+}
+.glyphicon-sort-by-order-alt:before {
+  content: "\e154";
+}
+.glyphicon-sort-by-attributes:before {
+  content: "\e155";
+}
+.glyphicon-sort-by-attributes-alt:before {
+  content: "\e156";
+}
+.glyphicon-unchecked:before {
+  content: "\e157";
+}
+.glyphicon-expand:before {
+  content: "\e158";
+}
+.glyphicon-collapse-down:before {
+  content: "\e159";
+}
+.glyphicon-collapse-up:before {
+  content: "\e160";
+}
+.glyphicon-log-in:before {
+  content: "\e161";
+}
+.glyphicon-flash:before {
+  content: "\e162";
+}
+.glyphicon-log-out:before {
+  content: "\e163";
+}
+.glyphicon-new-window:before {
+  content: "\e164";
+}
+.glyphicon-record:before {
+  content: "\e165";
+}
+.glyphicon-save:before {
+  content: "\e166";
+}
+.glyphicon-open:before {
+  content: "\e167";
+}
+.glyphicon-saved:before {
+  content: "\e168";
+}
+.glyphicon-import:before {
+  content: "\e169";
+}
+.glyphicon-export:before {
+  content: "\e170";
+}
+.glyphicon-send:before {
+  content: "\e171";
+}
+.glyphicon-floppy-disk:before {
+  content: "\e172";
+}
+.glyphicon-floppy-saved:before {
+  content: "\e173";
+}
+.glyphicon-floppy-remove:before {
+  content: "\e174";
+}
+.glyphicon-floppy-save:before {
+  content: "\e175";
+}
+.glyphicon-floppy-open:before {
+  content: "\e176";
+}
+.glyphicon-credit-card:before {
+  content: "\e177";
+}
+.glyphicon-transfer:before {
+  content: "\e178";
+}
+.glyphicon-cutlery:before {
+  content: "\e179";
+}
+.glyphicon-header:before {
+  content: "\e180";
+}
+.glyphicon-compressed:before {
+  content: "\e181";
+}
+.glyphicon-earphone:before {
+  content: "\e182";
+}
+.glyphicon-phone-alt:before {
+  content: "\e183";
+}
+.glyphicon-tower:before {
+  content: "\e184";
+}
+.glyphicon-stats:before {
+  content: "\e185";
+}
+.glyphicon-sd-video:before {
+  content: "\e186";
+}
+.glyphicon-hd-video:before {
+  content: "\e187";
+}
+.glyphicon-subtitles:before {
+  content: "\e188";
+}
+.glyphicon-sound-stereo:before {
+  content: "\e189";
+}
+.glyphicon-sound-dolby:before {
+  content: "\e190";
+}
+.glyphicon-sound-5-1:before {
+  content: "\e191";
+}
+.glyphicon-sound-6-1:before {
+  content: "\e192";
+}
+.glyphicon-sound-7-1:before {
+  content: "\e193";
+}
+.glyphicon-copyright-mark:before {
+  content: "\e194";
+}
+.glyphicon-registration-mark:before {
+  content: "\e195";
+}
+.glyphicon-cloud-download:before {
+  content: "\e197";
+}
+.glyphicon-cloud-upload:before {
+  content: "\e198";
+}
+.glyphicon-tree-conifer:before {
+  content: "\e199";
+}
+.glyphicon-tree-deciduous:before {
+  content: "\e200";
+}
+.glyphicon-cd:before {
+  content: "\e201";
+}
+.glyphicon-save-file:before {
+  content: "\e202";
+}
+.glyphicon-open-file:before {
+  content: "\e203";
+}
+.glyphicon-level-up:before {
+  content: "\e204";
+}
+.glyphicon-copy:before {
+  content: "\e205";
+}
+.glyphicon-paste:before {
+  content: "\e206";
+}
+.glyphicon-alert:before {
+  content: "\e209";
+}
+.glyphicon-equalizer:before {
+  content: "\e210";
+}
+.glyphicon-king:before {
+  content: "\e211";
+}
+.glyphicon-queen:before {
+  content: "\e212";
+}
+.glyphicon-pawn:before {
+  content: "\e213";
+}
+.glyphicon-bishop:before {
+  content: "\e214";
+}
+.glyphicon-knight:before {
+  content: "\e215";
+}
+.glyphicon-baby-formula:before {
+  content: "\e216";
+}
+.glyphicon-tent:before {
+  content: "\26fa";
+}
+.glyphicon-blackboard:before {
+  content: "\e218";
+}
+.glyphicon-bed:before {
+  content: "\e219";
+}
+.glyphicon-apple:before {
+  content: "\f8ff";
+}
+.glyphicon-erase:before {
+  content: "\e221";
+}
+.glyphicon-hourglass:before {
+  content: "\231b";
+}
+.glyphicon-lamp:before {
+  content: "\e223";
+}
+.glyphicon-duplicate:before {
+  content: "\e224";
+}
+.glyphicon-piggy-bank:before {
+  content: "\e225";
+}
+.glyphicon-scissors:before {
+  content: "\e226";
+}
+.glyphicon-bitcoin:before {
+  content: "\e227";
+}
+.glyphicon-btc:before {
+  content: "\e227";
+}
+.glyphicon-xbt:before {
+  content: "\e227";
+}
+.glyphicon-yen:before {
+  content: "\00a5";
+}
+.glyphicon-jpy:before {
+  content: "\00a5";
+}
+.glyphicon-ruble:before {
+  content: "\20bd";
+}
+.glyphicon-rub:before {
+  content: "\20bd";
+}
+.glyphicon-scale:before {
+  content: "\e230";
+}
+.glyphicon-ice-lolly:before {
+  content: "\e231";
+}
+.glyphicon-ice-lolly-tasted:before {
+  content: "\e232";
+}
+.glyphicon-education:before {
+  content: "\e233";
+}
+.glyphicon-option-horizontal:before {
+  content: "\e234";
+}
+.glyphicon-option-vertical:before {
+  content: "\e235";
+}
+.glyphicon-menu-hamburger:before {
+  content: "\e236";
+}
+.glyphicon-modal-window:before {
+  content: "\e237";
+}
+.glyphicon-oil:before {
+  content: "\e238";
+}
+.glyphicon-grain:before {
+  content: "\e239";
+}
+.glyphicon-sunglasses:before {
+  content: "\e240";
+}
+.glyphicon-text-size:before {
+  content: "\e241";
+}
+.glyphicon-text-color:before {
+  content: "\e242";
+}
+.glyphicon-text-background:before {
+  content: "\e243";
+}
+.glyphicon-object-align-top:before {
+  content: "\e244";
+}
+.glyphicon-object-align-bottom:before {
+  content: "\e245";
+}
+.glyphicon-object-align-horizontal:before {
+  content: "\e246";
+}
+.glyphicon-object-align-left:before {
+  content: "\e247";
+}
+.glyphicon-object-align-vertical:before {
+  content: "\e248";
+}
+.glyphicon-object-align-right:before {
+  content: "\e249";
+}
+.glyphicon-triangle-right:before {
+  content: "\e250";
+}
+.glyphicon-triangle-left:before {
+  content: "\e251";
+}
+.glyphicon-triangle-bottom:before {
+  content: "\e252";
+}
+.glyphicon-triangle-top:before {
+  content: "\e253";
+}
+.glyphicon-console:before {
+  content: "\e254";
+}
+.glyphicon-superscript:before {
+  content: "\e255";
+}
+.glyphicon-subscript:before {
+  content: "\e256";
+}
+.glyphicon-menu-left:before {
+  content: "\e257";
+}
+.glyphicon-menu-right:before {
+  content: "\e258";
+}
+.glyphicon-menu-down:before {
+  content: "\e259";
+}
+.glyphicon-menu-up:before {
+  content: "\e260";
+}
+* {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+*:before,
+*:after {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+html {
+  font-size: 10px;
+
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+body {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #333;
+  background-color: #fff;
+}
+input,
+button,
+select,
+textarea {
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+}
+a {
+  color: #337ab7;
+  text-decoration: none;
+}
+a:hover,
+a:focus {
+  color: #23527c;
+  text-decoration: underline;
+}
+a:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+figure {
+  margin: 0;
+}
+img {
+  vertical-align: middle;
+}
+.img-responsive,
+.thumbnail > img,
+.thumbnail a > img,
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  display: block;
+  max-width: 100%;
+  height: auto;
+}
+.img-rounded {
+  border-radius: 6px;
+}
+.img-thumbnail {
+  display: inline-block;
+  max-width: 100%;
+  height: auto;
+  padding: 4px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: all .2s ease-in-out;
+       -o-transition: all .2s ease-in-out;
+          transition: all .2s ease-in-out;
+}
+.img-circle {
+  border-radius: 50%;
+}
+hr {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  border: 0;
+  border-top: 1px solid #eee;
+}
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  border: 0;
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+  position: static;
+  width: auto;
+  height: auto;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+}
+[role="button"] {
+  cursor: pointer;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small,
+h1 .small,
+h2 .small,
+h3 .small,
+h4 .small,
+h5 .small,
+h6 .small,
+.h1 .small,
+.h2 .small,
+.h3 .small,
+.h4 .small,
+.h5 .small,
+.h6 .small {
+  font-weight: normal;
+  line-height: 1;
+  color: #777;
+}
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.h3 {
+  margin-top: 20px;
+  margin-bottom: 10px;
+}
+h1 small,
+.h1 small,
+h2 small,
+.h2 small,
+h3 small,
+.h3 small,
+h1 .small,
+.h1 .small,
+h2 .small,
+.h2 .small,
+h3 .small,
+.h3 .small {
+  font-size: 65%;
+}
+h4,
+.h4,
+h5,
+.h5,
+h6,
+.h6 {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+h4 small,
+.h4 small,
+h5 small,
+.h5 small,
+h6 small,
+.h6 small,
+h4 .small,
+.h4 .small,
+h5 .small,
+.h5 .small,
+h6 .small,
+.h6 .small {
+  font-size: 75%;
+}
+h1,
+.h1 {
+  font-size: 36px;
+}
+h2,
+.h2 {
+  font-size: 30px;
+}
+h3,
+.h3 {
+  font-size: 24px;
+}
+h4,
+.h4 {
+  font-size: 18px;
+}
+h5,
+.h5 {
+  font-size: 14px;
+}
+h6,
+.h6 {
+  font-size: 12px;
+}
+p {
+  margin: 0 0 10px;
+}
+.lead {
+  margin-bottom: 20px;
+  font-size: 16px;
+  font-weight: 300;
+  line-height: 1.4;
+}
+@media (min-width: 768px) {
+  .lead {
+    font-size: 21px;
+  }
+}
+small,
+.small {
+  font-size: 85%;
+}
+mark,
+.mark {
+  padding: .2em;
+  background-color: #fcf8e3;
+}
+.text-left {
+  text-align: left;
+}
+.text-right {
+  text-align: right;
+}
+.text-center {
+  text-align: center;
+}
+.text-justify {
+  text-align: justify;
+}
+.text-nowrap {
+  white-space: nowrap;
+}
+.text-lowercase {
+  text-transform: lowercase;
+}
+.text-uppercase {
+  text-transform: uppercase;
+}
+.text-capitalize {
+  text-transform: capitalize;
+}
+.text-muted {
+  color: #777;
+}
+.text-primary {
+  color: #337ab7;
+}
+a.text-primary:hover,
+a.text-primary:focus {
+  color: #286090;
+}
+.text-success {
+  color: #3c763d;
+}
+a.text-success:hover,
+a.text-success:focus {
+  color: #2b542c;
+}
+.text-info {
+  color: #31708f;
+}
+a.text-info:hover,
+a.text-info:focus {
+  color: #245269;
+}
+.text-warning {
+  color: #8a6d3b;
+}
+a.text-warning:hover,
+a.text-warning:focus {
+  color: #66512c;
+}
+.text-danger {
+  color: #a94442;
+}
+a.text-danger:hover,
+a.text-danger:focus {
+  color: #843534;
+}
+.bg-primary {
+  color: #fff;
+  background-color: #337ab7;
+}
+a.bg-primary:hover,
+a.bg-primary:focus {
+  background-color: #286090;
+}
+.bg-success {
+  background-color: #dff0d8;
+}
+a.bg-success:hover,
+a.bg-success:focus {
+  background-color: #c1e2b3;
+}
+.bg-info {
+  background-color: #d9edf7;
+}
+a.bg-info:hover,
+a.bg-info:focus {
+  background-color: #afd9ee;
+}
+.bg-warning {
+  background-color: #fcf8e3;
+}
+a.bg-warning:hover,
+a.bg-warning:focus {
+  background-color: #f7ecb5;
+}
+.bg-danger {
+  background-color: #f2dede;
+}
+a.bg-danger:hover,
+a.bg-danger:focus {
+  background-color: #e4b9b9;
+}
+.page-header {
+  padding-bottom: 9px;
+  margin: 40px 0 20px;
+  border-bottom: 1px solid #eee;
+}
+ul,
+ol {
+  margin-top: 0;
+  margin-bottom: 10px;
+}
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+  margin-bottom: 0;
+}
+.list-unstyled {
+  padding-left: 0;
+  list-style: none;
+}
+.list-inline {
+  padding-left: 0;
+  margin-left: -5px;
+  list-style: none;
+}
+.list-inline > li {
+  display: inline-block;
+  padding-right: 5px;
+  padding-left: 5px;
+}
+dl {
+  margin-top: 0;
+  margin-bottom: 20px;
+}
+dt,
+dd {
+  line-height: 1.42857143;
+}
+dt {
+  font-weight: bold;
+}
+dd {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .dl-horizontal dt {
+    float: left;
+    width: 160px;
+    overflow: hidden;
+    clear: left;
+    text-align: right;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .dl-horizontal dd {
+    margin-left: 180px;
+  }
+}
+abbr[title],
+abbr[data-original-title] {
+  cursor: help;
+  border-bottom: 1px dotted #777;
+}
+.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+blockquote {
+  padding: 10px 20px;
+  margin: 0 0 20px;
+  font-size: 17.5px;
+  border-left: 5px solid #eee;
+}
+blockquote p:last-child,
+blockquote ul:last-child,
+blockquote ol:last-child {
+  margin-bottom: 0;
+}
+blockquote footer,
+blockquote small,
+blockquote .small {
+  display: block;
+  font-size: 80%;
+  line-height: 1.42857143;
+  color: #777;
+}
+blockquote footer:before,
+blockquote small:before,
+blockquote .small:before {
+  content: '\2014 \00A0';
+}
+.blockquote-reverse,
+blockquote.pull-right {
+  padding-right: 15px;
+  padding-left: 0;
+  text-align: right;
+  border-right: 5px solid #eee;
+  border-left: 0;
+}
+.blockquote-reverse footer:before,
+blockquote.pull-right footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right small:before,
+.blockquote-reverse .small:before,
+blockquote.pull-right .small:before {
+  content: '';
+}
+.blockquote-reverse footer:after,
+blockquote.pull-right footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right small:after,
+.blockquote-reverse .small:after,
+blockquote.pull-right .small:after {
+  content: '\00A0 \2014';
+}
+address {
+  margin-bottom: 20px;
+  font-style: normal;
+  line-height: 1.42857143;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+}
+code {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #c7254e;
+  background-color: #f9f2f4;
+  border-radius: 4px;
+}
+kbd {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #fff;
+  background-color: #333;
+  border-radius: 3px;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+}
+kbd kbd {
+  padding: 0;
+  font-size: 100%;
+  font-weight: bold;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+pre {
+  display: block;
+  padding: 9.5px;
+  margin: 0 0 10px;
+  font-size: 13px;
+  line-height: 1.42857143;
+  color: #333;
+  word-break: break-all;
+  word-wrap: break-word;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+pre code {
+  padding: 0;
+  font-size: inherit;
+  color: inherit;
+  white-space: pre-wrap;
+  background-color: transparent;
+  border-radius: 0;
+}
+.pre-scrollable {
+  max-height: 340px;
+  overflow-y: scroll;
+}
+.container {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+@media (min-width: 768px) {
+  .container {
+    width: 750px;
+  }
+}
+@media (min-width: 992px) {
+  .container {
+    width: 970px;
+  }
+}
+@media (min-width: 1200px) {
+  .container {
+    width: 1170px;
+  }
+}
+.container-fluid {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+.row {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
+  position: relative;
+  min-height: 1px;
+  padding-right: 15px;
+  padding-left: 15px;
+}
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
+  float: left;
+}
+.col-xs-12 {
+  width: 100%;
+}
+.col-xs-11 {
+  width: 91.66666667%;
+}
+.col-xs-10 {
+  width: 83.33333333%;
+}
+.col-xs-9 {
+  width: 75%;
+}
+.col-xs-8 {
+  width: 66.66666667%;
+}
+.col-xs-7 {
+  width: 58.33333333%;
+}
+.col-xs-6 {
+  width: 50%;
+}
+.col-xs-5 {
+  width: 41.66666667%;
+}
+.col-xs-4 {
+  width: 33.33333333%;
+}
+.col-xs-3 {
+  width: 25%;
+}
+.col-xs-2 {
+  width: 16.66666667%;
+}
+.col-xs-1 {
+  width: 8.33333333%;
+}
+.col-xs-pull-12 {
+  right: 100%;
+}
+.col-xs-pull-11 {
+  right: 91.66666667%;
+}
+.col-xs-pull-10 {
+  right: 83.33333333%;
+}
+.col-xs-pull-9 {
+  right: 75%;
+}
+.col-xs-pull-8 {
+  right: 66.66666667%;
+}
+.col-xs-pull-7 {
+  right: 58.33333333%;
+}
+.col-xs-pull-6 {
+  right: 50%;
+}
+.col-xs-pull-5 {
+  right: 41.66666667%;
+}
+.col-xs-pull-4 {
+  right: 33.33333333%;
+}
+.col-xs-pull-3 {
+  right: 25%;
+}
+.col-xs-pull-2 {
+  right: 16.66666667%;
+}
+.col-xs-pull-1 {
+  right: 8.33333333%;
+}
+.col-xs-pull-0 {
+  right: auto;
+}
+.col-xs-push-12 {
+  left: 100%;
+}
+.col-xs-push-11 {
+  left: 91.66666667%;
+}
+.col-xs-push-10 {
+  left: 83.33333333%;
+}
+.col-xs-push-9 {
+  left: 75%;
+}
+.col-xs-push-8 {
+  left: 66.66666667%;
+}
+.col-xs-push-7 {
+  left: 58.33333333%;
+}
+.col-xs-push-6 {
+  left: 50%;
+}
+.col-xs-push-5 {
+  left: 41.66666667%;
+}
+.col-xs-push-4 {
+  left: 33.33333333%;
+}
+.col-xs-push-3 {
+  left: 25%;
+}
+.col-xs-push-2 {
+  left: 16.66666667%;
+}
+.col-xs-push-1 {
+  left: 8.33333333%;
+}
+.col-xs-push-0 {
+  left: auto;
+}
+.col-xs-offset-12 {
+  margin-left: 100%;
+}
+.col-xs-offset-11 {
+  margin-left: 91.66666667%;
+}
+.col-xs-offset-10 {
+  margin-left: 83.33333333%;
+}
+.col-xs-offset-9 {
+  margin-left: 75%;
+}
+.col-xs-offset-8 {
+  margin-left: 66.66666667%;
+}
+.col-xs-offset-7 {
+  margin-left: 58.33333333%;
+}
+.col-xs-offset-6 {
+  margin-left: 50%;
+}
+.col-xs-offset-5 {
+  margin-left: 41.66666667%;
+}
+.col-xs-offset-4 {
+  margin-left: 33.33333333%;
+}
+.col-xs-offset-3 {
+  margin-left: 25%;
+}
+.col-xs-offset-2 {
+  margin-left: 16.66666667%;
+}
+.col-xs-offset-1 {
+  margin-left: 8.33333333%;
+}
+.col-xs-offset-0 {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
+    float: left;
+  }
+  .col-sm-12 {
+    width: 100%;
+  }
+  .col-sm-11 {
+    width: 91.66666667%;
+  }
+  .col-sm-10 {
+    width: 83.33333333%;
+  }
+  .col-sm-9 {
+    width: 75%;
+  }
+  .col-sm-8 {
+    width: 66.66666667%;
+  }
+  .col-sm-7 {
+    width: 58.33333333%;
+  }
+  .col-sm-6 {
+    width: 50%;
+  }
+  .col-sm-5 {
+    width: 41.66666667%;
+  }
+  .col-sm-4 {
+    width: 33.33333333%;
+  }
+  .col-sm-3 {
+    width: 25%;
+  }
+  .col-sm-2 {
+    width: 16.66666667%;
+  }
+  .col-sm-1 {
+    width: 8.33333333%;
+  }
+  .col-sm-pull-12 {
+    right: 100%;
+  }
+  .col-sm-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-sm-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-sm-pull-9 {
+    right: 75%;
+  }
+  .col-sm-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-sm-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-sm-pull-6 {
+    right: 50%;
+  }
+  .col-sm-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-sm-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-sm-pull-3 {
+    right: 25%;
+  }
+  .col-sm-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-sm-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-sm-pull-0 {
+    right: auto;
+  }
+  .col-sm-push-12 {
+    left: 100%;
+  }
+  .col-sm-push-11 {
+    left: 91.66666667%;
+  }
+  .col-sm-push-10 {
+    left: 83.33333333%;
+  }
+  .col-sm-push-9 {
+    left: 75%;
+  }
+  .col-sm-push-8 {
+    left: 66.66666667%;
+  }
+  .col-sm-push-7 {
+    left: 58.33333333%;
+  }
+  .col-sm-push-6 {
+    left: 50%;
+  }
+  .col-sm-push-5 {
+    left: 41.66666667%;
+  }
+  .col-sm-push-4 {
+    left: 33.33333333%;
+  }
+  .col-sm-push-3 {
+    left: 25%;
+  }
+  .col-sm-push-2 {
+    left: 16.66666667%;
+  }
+  .col-sm-push-1 {
+    left: 8.33333333%;
+  }
+  .col-sm-push-0 {
+    left: auto;
+  }
+  .col-sm-offset-12 {
+    margin-left: 100%;
+  }
+  .col-sm-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-sm-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-sm-offset-9 {
+    margin-left: 75%;
+  }
+  .col-sm-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-sm-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-sm-offset-6 {
+    margin-left: 50%;
+  }
+  .col-sm-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-sm-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-sm-offset-3 {
+    margin-left: 25%;
+  }
+  .col-sm-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-sm-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-sm-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 992px) {
+  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
+    float: left;
+  }
+  .col-md-12 {
+    width: 100%;
+  }
+  .col-md-11 {
+    width: 91.66666667%;
+  }
+  .col-md-10 {
+    width: 83.33333333%;
+  }
+  .col-md-9 {
+    width: 75%;
+  }
+  .col-md-8 {
+    width: 66.66666667%;
+  }
+  .col-md-7 {
+    width: 58.33333333%;
+  }
+  .col-md-6 {
+    width: 50%;
+  }
+  .col-md-5 {
+    width: 41.66666667%;
+  }
+  .col-md-4 {
+    width: 33.33333333%;
+  }
+  .col-md-3 {
+    width: 25%;
+  }
+  .col-md-2 {
+    width: 16.66666667%;
+  }
+  .col-md-1 {
+    width: 8.33333333%;
+  }
+  .col-md-pull-12 {
+    right: 100%;
+  }
+  .col-md-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-md-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-md-pull-9 {
+    right: 75%;
+  }
+  .col-md-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-md-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-md-pull-6 {
+    right: 50%;
+  }
+  .col-md-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-md-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-md-pull-3 {
+    right: 25%;
+  }
+  .col-md-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-md-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-md-pull-0 {
+    right: auto;
+  }
+  .col-md-push-12 {
+    left: 100%;
+  }
+  .col-md-push-11 {
+    left: 91.66666667%;
+  }
+  .col-md-push-10 {
+    left: 83.33333333%;
+  }
+  .col-md-push-9 {
+    left: 75%;
+  }
+  .col-md-push-8 {
+    left: 66.66666667%;
+  }
+  .col-md-push-7 {
+    left: 58.33333333%;
+  }
+  .col-md-push-6 {
+    left: 50%;
+  }
+  .col-md-push-5 {
+    left: 41.66666667%;
+  }
+  .col-md-push-4 {
+    left: 33.33333333%;
+  }
+  .col-md-push-3 {
+    left: 25%;
+  }
+  .col-md-push-2 {
+    left: 16.66666667%;
+  }
+  .col-md-push-1 {
+    left: 8.33333333%;
+  }
+  .col-md-push-0 {
+    left: auto;
+  }
+  .col-md-offset-12 {
+    margin-left: 100%;
+  }
+  .col-md-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-md-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-md-offset-9 {
+    margin-left: 75%;
+  }
+  .col-md-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-md-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-md-offset-6 {
+    margin-left: 50%;
+  }
+  .col-md-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-md-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-md-offset-3 {
+    margin-left: 25%;
+  }
+  .col-md-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-md-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-md-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 1200px) {
+  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
+    float: left;
+  }
+  .col-lg-12 {
+    width: 100%;
+  }
+  .col-lg-11 {
+    width: 91.66666667%;
+  }
+  .col-lg-10 {
+    width: 83.33333333%;
+  }
+  .col-lg-9 {
+    width: 75%;
+  }
+  .col-lg-8 {
+    width: 66.66666667%;
+  }
+  .col-lg-7 {
+    width: 58.33333333%;
+  }
+  .col-lg-6 {
+    width: 50%;
+  }
+  .col-lg-5 {
+    width: 41.66666667%;
+  }
+  .col-lg-4 {
+    width: 33.33333333%;
+  }
+  .col-lg-3 {
+    width: 25%;
+  }
+  .col-lg-2 {
+    width: 16.66666667%;
+  }
+  .col-lg-1 {
+    width: 8.33333333%;
+  }
+  .col-lg-pull-12 {
+    right: 100%;
+  }
+  .col-lg-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-lg-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-lg-pull-9 {
+    right: 75%;
+  }
+  .col-lg-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-lg-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-lg-pull-6 {
+    right: 50%;
+  }
+  .col-lg-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-lg-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-lg-pull-3 {
+    right: 25%;
+  }
+  .col-lg-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-lg-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-lg-pull-0 {
+    right: auto;
+  }
+  .col-lg-push-12 {
+    left: 100%;
+  }
+  .col-lg-push-11 {
+    left: 91.66666667%;
+  }
+  .col-lg-push-10 {
+    left: 83.33333333%;
+  }
+  .col-lg-push-9 {
+    left: 75%;
+  }
+  .col-lg-push-8 {
+    left: 66.66666667%;
+  }
+  .col-lg-push-7 {
+    left: 58.33333333%;
+  }
+  .col-lg-push-6 {
+    left: 50%;
+  }
+  .col-lg-push-5 {
+    left: 41.66666667%;
+  }
+  .col-lg-push-4 {
+    left: 33.33333333%;
+  }
+  .col-lg-push-3 {
+    left: 25%;
+  }
+  .col-lg-push-2 {
+    left: 16.66666667%;
+  }
+  .col-lg-push-1 {
+    left: 8.33333333%;
+  }
+  .col-lg-push-0 {
+    left: auto;
+  }
+  .col-lg-offset-12 {
+    margin-left: 100%;
+  }
+  .col-lg-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-lg-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-lg-offset-9 {
+    margin-left: 75%;
+  }
+  .col-lg-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-lg-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-lg-offset-6 {
+    margin-left: 50%;
+  }
+  .col-lg-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-lg-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-lg-offset-3 {
+    margin-left: 25%;
+  }
+  .col-lg-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-lg-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-lg-offset-0 {
+    margin-left: 0;
+  }
+}
+table {
+  background-color: transparent;
+}
+caption {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  color: #777;
+  text-align: left;
+}
+th {
+  text-align: left;
+}
+.table {
+  width: 100%;
+  max-width: 100%;
+  margin-bottom: 20px;
+}
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+  padding: 8px;
+  line-height: 1.42857143;
+  vertical-align: top;
+  border-top: 1px solid #ddd;
+}
+.table > thead > tr > th {
+  vertical-align: bottom;
+  border-bottom: 2px solid #ddd;
+}
+.table > caption + thead > tr:first-child > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+  border-top: 0;
+}
+.table > tbody + tbody {
+  border-top: 2px solid #ddd;
+}
+.table .table {
+  background-color: #fff;
+}
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+  padding: 5px;
+}
+.table-bordered {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+  border-bottom-width: 2px;
+}
+.table-striped > tbody > tr:nth-of-type(odd) {
+  background-color: #f9f9f9;
+}
+.table-hover > tbody > tr:hover {
+  background-color: #f5f5f5;
+}
+table col[class*="col-"] {
+  position: static;
+  display: table-column;
+  float: none;
+}
+table td[class*="col-"],
+table th[class*="col-"] {
+  position: static;
+  display: table-cell;
+  float: none;
+}
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+  background-color: #f5f5f5;
+}
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th {
+  background-color: #e8e8e8;
+}
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+  background-color: #dff0d8;
+}
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td,
+.table-hover > tbody > tr:hover > .success,
+.table-hover > tbody > tr.success:hover > th {
+  background-color: #d0e9c6;
+}
+.table > thead > tr > td.info,
+.table > tbody > tr > td.info,
+.table > tfoot > tr > td.info,
+.table > thead > tr > th.info,
+.table > tbody > tr > th.info,
+.table > tfoot > tr > th.info,
+.table > thead > tr.info > td,
+.table > tbody > tr.info > td,
+.table > tfoot > tr.info > td,
+.table > thead > tr.info > th,
+.table > tbody > tr.info > th,
+.table > tfoot > tr.info > th {
+  background-color: #d9edf7;
+}
+.table-hover > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td,
+.table-hover > tbody > tr:hover > .info,
+.table-hover > tbody > tr.info:hover > th {
+  background-color: #c4e3f3;
+}
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+  background-color: #fcf8e3;
+}
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr:hover > .warning,
+.table-hover > tbody > tr.warning:hover > th {
+  background-color: #faf2cc;
+}
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+  background-color: #f2dede;
+}
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr:hover > .danger,
+.table-hover > tbody > tr.danger:hover > th {
+  background-color: #ebcccc;
+}
+.table-responsive {
+  min-height: .01%;
+  overflow-x: auto;
+}
+@media screen and (max-width: 767px) {
+  .table-responsive {
+    width: 100%;
+    margin-bottom: 15px;
+    overflow-y: hidden;
+    -ms-overflow-style: -ms-autohiding-scrollbar;
+    border: 1px solid #ddd;
+  }
+  .table-responsive > .table {
+    margin-bottom: 0;
+  }
+  .table-responsive > .table > thead > tr > th,
+  .table-responsive > .table > tbody > tr > th,
+  .table-responsive > .table > tfoot > tr > th,
+  .table-responsive > .table > thead > tr > td,
+  .table-responsive > .table > tbody > tr > td,
+  .table-responsive > .table > tfoot > tr > td {
+    white-space: nowrap;
+  }
+  .table-responsive > .table-bordered {
+    border: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:first-child,
+  .table-responsive > .table-bordered > tbody > tr > th:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+  .table-responsive > .table-bordered > thead > tr > td:first-child,
+  .table-responsive > .table-bordered > tbody > tr > td:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+    border-left: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:last-child,
+  .table-responsive > .table-bordered > tbody > tr > th:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+  .table-responsive > .table-bordered > thead > tr > td:last-child,
+  .table-responsive > .table-bordered > tbody > tr > td:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+    border-right: 0;
+  }
+  .table-responsive > .table-bordered > tbody > tr:last-child > th,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+  .table-responsive > .table-bordered > tbody > tr:last-child > td,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+    border-bottom: 0;
+  }
+}
+fieldset {
+  min-width: 0;
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+legend {
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: 20px;
+  font-size: 21px;
+  line-height: inherit;
+  color: #333;
+  border: 0;
+  border-bottom: 1px solid #e5e5e5;
+}
+label {
+  display: inline-block;
+  max-width: 100%;
+  margin-bottom: 5px;
+  font-weight: bold;
+}
+input[type="search"] {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+input[type="radio"],
+input[type="checkbox"] {
+  margin: 4px 0 0;
+  margin-top: 1px \9;
+  line-height: normal;
+}
+input[type="file"] {
+  display: block;
+}
+input[type="range"] {
+  display: block;
+  width: 100%;
+}
+select[multiple],
+select[size] {
+  height: auto;
+}
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+output {
+  display: block;
+  padding-top: 7px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+}
+.form-control {
+  display: block;
+  width: 100%;
+  height: 34px;
+  padding: 6px 12px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+  -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
+       -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+          transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+.form-control:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.form-control::-moz-placeholder {
+  color: #999;
+  opacity: 1;
+}
+.form-control:-ms-input-placeholder {
+  color: #999;
+}
+.form-control::-webkit-input-placeholder {
+  color: #999;
+}
+.form-control::-ms-expand {
+  background-color: transparent;
+  border: 0;
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+  background-color: #eee;
+  opacity: 1;
+}
+.form-control[disabled],
+fieldset[disabled] .form-control {
+  cursor: not-allowed;
+}
+textarea.form-control {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-appearance: none;
+}
+@media screen and (-webkit-min-device-pixel-ratio: 0) {
+  input[type="date"].form-control,
+  input[type="time"].form-control,
+  input[type="datetime-local"].form-control,
+  input[type="month"].form-control {
+    line-height: 34px;
+  }
+  input[type="date"].input-sm,
+  input[type="time"].input-sm,
+  input[type="datetime-local"].input-sm,
+  input[type="month"].input-sm,
+  .input-group-sm input[type="date"],
+  .input-group-sm input[type="time"],
+  .input-group-sm input[type="datetime-local"],
+  .input-group-sm input[type="month"] {
+    line-height: 30px;
+  }
+  input[type="date"].input-lg,
+  input[type="time"].input-lg,
+  input[type="datetime-local"].input-lg,
+  input[type="month"].input-lg,
+  .input-group-lg input[type="date"],
+  .input-group-lg input[type="time"],
+  .input-group-lg input[type="datetime-local"],
+  .input-group-lg input[type="month"] {
+    line-height: 46px;
+  }
+}
+.form-group {
+  margin-bottom: 15px;
+}
+.radio,
+.checkbox {
+  position: relative;
+  display: block;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.radio label,
+.checkbox label {
+  min-height: 20px;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+  position: absolute;
+  margin-top: 4px \9;
+  margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+  margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+  position: relative;
+  display: inline-block;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  vertical-align: middle;
+  cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+  margin-top: 0;
+  margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+  cursor: not-allowed;
+}
+.radio-inline.disabled,
+.checkbox-inline.disabled,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox-inline {
+  cursor: not-allowed;
+}
+.radio.disabled label,
+.checkbox.disabled label,
+fieldset[disabled] .radio label,
+fieldset[disabled] .checkbox label {
+  cursor: not-allowed;
+}
+.form-control-static {
+  min-height: 34px;
+  padding-top: 7px;
+  padding-bottom: 7px;
+  margin-bottom: 0;
+}
+.form-control-static.input-lg,
+.form-control-static.input-sm {
+  padding-right: 0;
+  padding-left: 0;
+}
+.input-sm {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-sm {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-sm,
+select[multiple].input-sm {
+  height: auto;
+}
+.form-group-sm .form-control {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.form-group-sm select.form-control {
+  height: 30px;
+  line-height: 30px;
+}
+.form-group-sm textarea.form-control,
+.form-group-sm select[multiple].form-control {
+  height: auto;
+}
+.form-group-sm .form-control-static {
+  height: 30px;
+  min-height: 32px;
+  padding: 6px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+}
+.input-lg {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+select.input-lg {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-lg,
+select[multiple].input-lg {
+  height: auto;
+}
+.form-group-lg .form-control {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+.form-group-lg select.form-control {
+  height: 46px;
+  line-height: 46px;
+}
+.form-group-lg textarea.form-control,
+.form-group-lg select[multiple].form-control {
+  height: auto;
+}
+.form-group-lg .form-control-static {
+  height: 46px;
+  min-height: 38px;
+  padding: 11px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+}
+.has-feedback {
+  position: relative;
+}
+.has-feedback .form-control {
+  padding-right: 42.5px;
+}
+.form-control-feedback {
+  position: absolute;
+  top: 0;
+  right: 0;
+  z-index: 2;
+  display: block;
+  width: 34px;
+  height: 34px;
+  line-height: 34px;
+  text-align: center;
+  pointer-events: none;
+}
+.input-lg + .form-control-feedback,
+.input-group-lg + .form-control-feedback,
+.form-group-lg .form-control + .form-control-feedback {
+  width: 46px;
+  height: 46px;
+  line-height: 46px;
+}
+.input-sm + .form-control-feedback,
+.input-group-sm + .form-control-feedback,
+.form-group-sm .form-control + .form-control-feedback {
+  width: 30px;
+  height: 30px;
+  line-height: 30px;
+}
+.has-success .help-block,
+.has-success .control-label,
+.has-success .radio,
+.has-success .checkbox,
+.has-success .radio-inline,
+.has-success .checkbox-inline,
+.has-success.radio label,
+.has-success.checkbox label,
+.has-success.radio-inline label,
+.has-success.checkbox-inline label {
+  color: #3c763d;
+}
+.has-success .form-control {
+  border-color: #3c763d;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-success .form-control:focus {
+  border-color: #2b542c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+}
+.has-success .input-group-addon {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #3c763d;
+}
+.has-success .form-control-feedback {
+  color: #3c763d;
+}
+.has-warning .help-block,
+.has-warning .control-label,
+.has-warning .radio,
+.has-warning .checkbox,
+.has-warning .radio-inline,
+.has-warning .checkbox-inline,
+.has-warning.radio label,
+.has-warning.checkbox label,
+.has-warning.radio-inline label,
+.has-warning.checkbox-inline label {
+  color: #8a6d3b;
+}
+.has-warning .form-control {
+  border-color: #8a6d3b;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-warning .form-control:focus {
+  border-color: #66512c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+}
+.has-warning .input-group-addon {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #8a6d3b;
+}
+.has-warning .form-control-feedback {
+  color: #8a6d3b;
+}
+.has-error .help-block,
+.has-error .control-label,
+.has-error .radio,
+.has-error .checkbox,
+.has-error .radio-inline,
+.has-error .checkbox-inline,
+.has-error.radio label,
+.has-error.checkbox label,
+.has-error.radio-inline label,
+.has-error.checkbox-inline label {
+  color: #a94442;
+}
+.has-error .form-control {
+  border-color: #a94442;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-error .form-control:focus {
+  border-color: #843534;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+}
+.has-error .input-group-addon {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #a94442;
+}
+.has-error .form-control-feedback {
+  color: #a94442;
+}
+.has-feedback label ~ .form-control-feedback {
+  top: 25px;
+}
+.has-feedback label.sr-only ~ .form-control-feedback {
+  top: 0;
+}
+.help-block {
+  display: block;
+  margin-top: 5px;
+  margin-bottom: 10px;
+  color: #737373;
+}
+@media (min-width: 768px) {
+  .form-inline .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .form-inline .form-control-static {
+    display: inline-block;
+  }
+  .form-inline .input-group {
+    display: inline-table;
+    vertical-align: middle;
+  }
+  .form-inline .input-group .input-group-addon,
+  .form-inline .input-group .input-group-btn,
+  .form-inline .input-group .form-control {
+    width: auto;
+  }
+  .form-inline .input-group > .form-control {
+    width: 100%;
+  }
+  .form-inline .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio,
+  .form-inline .checkbox {
+    display: inline-block;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio label,
+  .form-inline .checkbox label {
+    padding-left: 0;
+  }
+  .form-inline .radio input[type="radio"],
+  .form-inline .checkbox input[type="checkbox"] {
+    position: relative;
+    margin-left: 0;
+  }
+  .form-inline .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+  padding-top: 7px;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox {
+  min-height: 27px;
+}
+.form-horizontal .form-group {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+@media (min-width: 768px) {
+  .form-horizontal .control-label {
+    padding-top: 7px;
+    margin-bottom: 0;
+    text-align: right;
+  }
+}
+.form-horizontal .has-feedback .form-control-feedback {
+  right: 15px;
+}
+@media (min-width: 768px) {
+  .form-horizontal .form-group-lg .control-label {
+    padding-top: 11px;
+    font-size: 18px;
+  }
+}
+@media (min-width: 768px) {
+  .form-horizontal .form-group-sm .control-label {
+    padding-top: 6px;
+    font-size: 12px;
+  }
+}
+.btn {
+  display: inline-block;
+  padding: 6px 12px;
+  margin-bottom: 0;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  -ms-touch-action: manipulation;
+      touch-action: manipulation;
+  cursor: pointer;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn.active.focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus,
+.btn.focus {
+  color: #333;
+  text-decoration: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+  outline: 0;
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+  cursor: not-allowed;
+  filter: alpha(opacity=65);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  opacity: .65;
+}
+a.btn.disabled,
+fieldset[disabled] a.btn {
+  pointer-events: none;
+}
+.btn-default {
+  color: #333;
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default:focus,
+.btn-default.focus {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #8c8c8c;
+}
+.btn-default:hover {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #adadad;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #adadad;
+}
+.btn-default:active:hover,
+.btn-default.active:hover,
+.open > .dropdown-toggle.btn-default:hover,
+.btn-default:active:focus,
+.btn-default.active:focus,
+.open > .dropdown-toggle.btn-default:focus,
+.btn-default:active.focus,
+.btn-default.active.focus,
+.open > .dropdown-toggle.btn-default.focus {
+  color: #333;
+  background-color: #d4d4d4;
+  border-color: #8c8c8c;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  background-image: none;
+}
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus {
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default .badge {
+  color: #fff;
+  background-color: #333;
+}
+.btn-primary {
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #2e6da4;
+}
+.btn-primary:focus,
+.btn-primary.focus {
+  color: #fff;
+  background-color: #286090;
+  border-color: #122b40;
+}
+.btn-primary:hover {
+  color: #fff;
+  background-color: #286090;
+  border-color: #204d74;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  color: #fff;
+  background-color: #286090;
+  border-color: #204d74;
+}
+.btn-primary:active:hover,
+.btn-primary.active:hover,
+.open > .dropdown-toggle.btn-primary:hover,
+.btn-primary:active:focus,
+.btn-primary.active:focus,
+.open > .dropdown-toggle.btn-primary:focus,
+.btn-primary:active.focus,
+.btn-primary.active.focus,
+.open > .dropdown-toggle.btn-primary.focus {
+  color: #fff;
+  background-color: #204d74;
+  border-color: #122b40;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  background-image: none;
+}
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus {
+  background-color: #337ab7;
+  border-color: #2e6da4;
+}
+.btn-primary .badge {
+  color: #337ab7;
+  background-color: #fff;
+}
+.btn-success {
+  color: #fff;
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success:focus,
+.btn-success.focus {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #255625;
+}
+.btn-success:hover {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #398439;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #398439;
+}
+.btn-success:active:hover,
+.btn-success.active:hover,
+.open > .dropdown-toggle.btn-success:hover,
+.btn-success:active:focus,
+.btn-success.active:focus,
+.open > .dropdown-toggle.btn-success:focus,
+.btn-success:active.focus,
+.btn-success.active.focus,
+.open > .dropdown-toggle.btn-success.focus {
+  color: #fff;
+  background-color: #398439;
+  border-color: #255625;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  background-image: none;
+}
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus {
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success .badge {
+  color: #5cb85c;
+  background-color: #fff;
+}
+.btn-info {
+  color: #fff;
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info:focus,
+.btn-info.focus {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #1b6d85;
+}
+.btn-info:hover {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #269abc;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #269abc;
+}
+.btn-info:active:hover,
+.btn-info.active:hover,
+.open > .dropdown-toggle.btn-info:hover,
+.btn-info:active:focus,
+.btn-info.active:focus,
+.open > .dropdown-toggle.btn-info:focus,
+.btn-info:active.focus,
+.btn-info.active.focus,
+.open > .dropdown-toggle.btn-info.focus {
+  color: #fff;
+  background-color: #269abc;
+  border-color: #1b6d85;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  background-image: none;
+}
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus {
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info .badge {
+  color: #5bc0de;
+  background-color: #fff;
+}
+.btn-warning {
+  color: #fff;
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning:focus,
+.btn-warning.focus {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #985f0d;
+}
+.btn-warning:hover {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #d58512;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #d58512;
+}
+.btn-warning:active:hover,
+.btn-warning.active:hover,
+.open > .dropdown-toggle.btn-warning:hover,
+.btn-warning:active:focus,
+.btn-warning.active:focus,
+.open > .dropdown-toggle.btn-warning:focus,
+.btn-warning:active.focus,
+.btn-warning.active.focus,
+.open > .dropdown-toggle.btn-warning.focus {
+  color: #fff;
+  background-color: #d58512;
+  border-color: #985f0d;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  background-image: none;
+}
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus {
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning .badge {
+  color: #f0ad4e;
+  background-color: #fff;
+}
+.btn-danger {
+  color: #fff;
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger:focus,
+.btn-danger.focus {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #761c19;
+}
+.btn-danger:hover {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #ac2925;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #ac2925;
+}
+.btn-danger:active:hover,
+.btn-danger.active:hover,
+.open > .dropdown-toggle.btn-danger:hover,
+.btn-danger:active:focus,
+.btn-danger.active:focus,
+.open > .dropdown-toggle.btn-danger:focus,
+.btn-danger:active.focus,
+.btn-danger.active.focus,
+.open > .dropdown-toggle.btn-danger.focus {
+  color: #fff;
+  background-color: #ac2925;
+  border-color: #761c19;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  background-image: none;
+}
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus {
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger .badge {
+  color: #d9534f;
+  background-color: #fff;
+}
+.btn-link {
+  font-weight: normal;
+  color: #337ab7;
+  border-radius: 0;
+}
+.btn-link,
+.btn-link:active,
+.btn-link.active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+  background-color: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+  border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+  color: #23527c;
+  text-decoration: underline;
+  background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+  color: #777;
+  text-decoration: none;
+}
+.btn-lg,
+.btn-group-lg > .btn {
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+.btn-sm,
+.btn-group-sm > .btn {
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-xs,
+.btn-group-xs > .btn {
+  padding: 1px 5px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-block {
+  display: block;
+  width: 100%;
+}
+.btn-block + .btn-block {
+  margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+  width: 100%;
+}
+.fade {
+  opacity: 0;
+  -webkit-transition: opacity .15s linear;
+       -o-transition: opacity .15s linear;
+          transition: opacity .15s linear;
+}
+.fade.in {
+  opacity: 1;
+}
+.collapse {
+  display: none;
+}
+.collapse.in {
+  display: block;
+}
+tr.collapse.in {
+  display: table-row;
+}
+tbody.collapse.in {
+  display: table-row-group;
+}
+.collapsing {
+  position: relative;
+  height: 0;
+  overflow: hidden;
+  -webkit-transition-timing-function: ease;
+       -o-transition-timing-function: ease;
+          transition-timing-function: ease;
+  -webkit-transition-duration: .35s;
+       -o-transition-duration: .35s;
+          transition-duration: .35s;
+  -webkit-transition-property: height, visibility;
+       -o-transition-property: height, visibility;
+          transition-property: height, visibility;
+}
+.caret {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  margin-left: 2px;
+  vertical-align: middle;
+  border-top: 4px dashed;
+  border-top: 4px solid \9;
+  border-right: 4px solid transparent;
+  border-left: 4px solid transparent;
+}
+.dropup,
+.dropdown {
+  position: relative;
+}
+.dropdown-toggle:focus {
+  outline: 0;
+}
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none;
+  float: left;
+  min-width: 160px;
+  padding: 5px 0;
+  margin: 2px 0 0;
+  font-size: 14px;
+  text-align: left;
+  list-style: none;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .15);
+  border-radius: 4px;
+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+          box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+}
+.dropdown-menu.pull-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu .divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.dropdown-menu > li > a {
+  display: block;
+  padding: 3px 20px;
+  clear: both;
+  font-weight: normal;
+  line-height: 1.42857143;
+  color: #333;
+  white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  color: #262626;
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  color: #fff;
+  text-decoration: none;
+  background-color: #337ab7;
+  outline: 0;
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  color: #777;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+  background-image: none;
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.open > .dropdown-menu {
+  display: block;
+}
+.open > a {
+  outline: 0;
+}
+.dropdown-menu-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu-left {
+  right: auto;
+  left: 0;
+}
+.dropdown-header {
+  display: block;
+  padding: 3px 20px;
+  font-size: 12px;
+  line-height: 1.42857143;
+  color: #777;
+  white-space: nowrap;
+}
+.dropdown-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 990;
+}
+.pull-right > .dropdown-menu {
+  right: 0;
+  left: auto;
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+  content: "";
+  border-top: 0;
+  border-bottom: 4px dashed;
+  border-bottom: 4px solid \9;
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+  top: auto;
+  bottom: 100%;
+  margin-bottom: 2px;
+}
+@media (min-width: 768px) {
+  .navbar-right .dropdown-menu {
+    right: 0;
+    left: auto;
+  }
+  .navbar-right .dropdown-menu-left {
+    right: auto;
+    left: 0;
+  }
+}
+.btn-group,
+.btn-group-vertical {
+  position: relative;
+  display: inline-block;
+  vertical-align: middle;
+}
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+  position: relative;
+  float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+  z-index: 2;
+}
+.btn-group .btn + .btn,
+.btn-group .btn + .btn-group,
+.btn-group .btn-group + .btn,
+.btn-group .btn-group + .btn-group {
+  margin-left: -1px;
+}
+.btn-toolbar {
+  margin-left: -5px;
+}
+.btn-toolbar .btn,
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
+  float: left;
+}
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
+  margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+  border-radius: 0;
+}
+.btn-group > .btn:first-child {
+  margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group > .btn-group {
+  float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+  outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+  padding-right: 8px;
+  padding-left: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+  padding-right: 12px;
+  padding-left: 12px;
+}
+.btn-group.open .dropdown-toggle {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-group.open .dropdown-toggle.btn-link {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn .caret {
+  margin-left: 0;
+}
+.btn-lg .caret {
+  border-width: 5px 5px 0;
+  border-bottom-width: 0;
+}
+.dropup .btn-lg .caret {
+  border-width: 0 5px 5px;
+}
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group,
+.btn-group-vertical > .btn-group > .btn {
+  display: block;
+  float: none;
+  width: 100%;
+  max-width: 100%;
+}
+.btn-group-vertical > .btn-group > .btn {
+  float: none;
+}
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+  margin-top: -1px;
+  margin-left: 0;
+}
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn:last-child:not(:first-child) {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.btn-group-justified {
+  display: table;
+  width: 100%;
+  table-layout: fixed;
+  border-collapse: separate;
+}
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
+  display: table-cell;
+  float: none;
+  width: 1%;
+}
+.btn-group-justified > .btn-group .btn {
+  width: 100%;
+}
+.btn-group-justified > .btn-group .dropdown-menu {
+  left: auto;
+}
+[data-toggle="buttons"] > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn input[type="checkbox"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] {
+  position: absolute;
+  clip: rect(0, 0, 0, 0);
+  pointer-events: none;
+}
+.input-group {
+  position: relative;
+  display: table;
+  border-collapse: separate;
+}
+.input-group[class*="col-"] {
+  float: none;
+  padding-right: 0;
+  padding-left: 0;
+}
+.input-group .form-control {
+  position: relative;
+  z-index: 2;
+  float: left;
+  width: 100%;
+  margin-bottom: 0;
+}
+.input-group .form-control:focus {
+  z-index: 3;
+}
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+  border-radius: 6px;
+}
+select.input-group-lg > .form-control,
+select.input-group-lg > .input-group-addon,
+select.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn,
+select[multiple].input-group-lg > .form-control,
+select[multiple].input-group-lg > .input-group-addon,
+select[multiple].input-group-lg > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-group-sm > .form-control,
+select.input-group-sm > .input-group-addon,
+select.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn,
+select[multiple].input-group-sm > .form-control,
+select[multiple].input-group-sm > .input-group-addon,
+select[multiple].input-group-sm > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+  display: table-cell;
+}
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.input-group-addon,
+.input-group-btn {
+  width: 1%;
+  white-space: nowrap;
+  vertical-align: middle;
+}
+.input-group-addon {
+  padding: 6px 12px;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1;
+  color: #555;
+  text-align: center;
+  background-color: #eee;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+.input-group-addon.input-sm {
+  padding: 5px 10px;
+  font-size: 12px;
+  border-radius: 3px;
+}
+.input-group-addon.input-lg {
+  padding: 10px 16px;
+  font-size: 18px;
+  border-radius: 6px;
+}
+.input-group-addon input[type="radio"],
+.input-group-addon input[type="checkbox"] {
+  margin-top: 0;
+}
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group > .btn,
+.input-group-btn:first-child > .dropdown-toggle,
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.input-group-addon:first-child {
+  border-right: 0;
+}
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.input-group-addon:last-child {
+  border-left: 0;
+}
+.input-group-btn {
+  position: relative;
+  font-size: 0;
+  white-space: nowrap;
+}
+.input-group-btn > .btn {
+  position: relative;
+}
+.input-group-btn > .btn + .btn {
+  margin-left: -1px;
+}
+.input-group-btn > .btn:hover,
+.input-group-btn > .btn:focus,
+.input-group-btn > .btn:active {
+  z-index: 2;
+}
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group {
+  margin-right: -1px;
+}
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group {
+  z-index: 2;
+  margin-left: -1px;
+}
+.nav {
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+}
+.nav > li {
+  position: relative;
+  display: block;
+}
+.nav > li > a {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.nav > li.disabled > a {
+  color: #777;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+  color: #777;
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+  background-color: #eee;
+  border-color: #337ab7;
+}
+.nav .nav-divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.nav > li > a > img {
+  max-width: none;
+}
+.nav-tabs {
+  border-bottom: 1px solid #ddd;
+}
+.nav-tabs > li {
+  float: left;
+  margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+  margin-right: 2px;
+  line-height: 1.42857143;
+  border: 1px solid transparent;
+  border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover {
+  border-color: #eee #eee #ddd;
+}
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+  color: #555;
+  cursor: default;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+}
+.nav-tabs.nav-justified {
+  width: 100%;
+  border-bottom: 0;
+}
+.nav-tabs.nav-justified > li {
+  float: none;
+}
+.nav-tabs.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-tabs.nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs.nav-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs.nav-justified > .active > a,
+.nav-tabs.nav-justified > .active > a:hover,
+.nav-tabs.nav-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs.nav-justified > .active > a,
+  .nav-tabs.nav-justified > .active > a:hover,
+  .nav-tabs.nav-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.nav-pills > li {
+  float: left;
+}
+.nav-pills > li > a {
+  border-radius: 4px;
+}
+.nav-pills > li + li {
+  margin-left: 2px;
+}
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+  color: #fff;
+  background-color: #337ab7;
+}
+.nav-stacked > li {
+  float: none;
+}
+.nav-stacked > li + li {
+  margin-top: 2px;
+  margin-left: 0;
+}
+.nav-justified {
+  width: 100%;
+}
+.nav-justified > li {
+  float: none;
+}
+.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs-justified {
+  border-bottom: 0;
+}
+.nav-tabs-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs-justified > .active > a,
+.nav-tabs-justified > .active > a:hover,
+.nav-tabs-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs-justified > .active > a,
+  .nav-tabs-justified > .active > a:hover,
+  .nav-tabs-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.tab-content > .tab-pane {
+  display: none;
+}
+.tab-content > .active {
+  display: block;
+}
+.nav-tabs .dropdown-menu {
+  margin-top: -1px;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar {
+  position: relative;
+  min-height: 50px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+}
+@media (min-width: 768px) {
+  .navbar {
+    border-radius: 4px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-header {
+    float: left;
+  }
+}
+.navbar-collapse {
+  padding-right: 15px;
+  padding-left: 15px;
+  overflow-x: visible;
+  -webkit-overflow-scrolling: touch;
+  border-top: 1px solid transparent;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+}
+.navbar-collapse.in {
+  overflow-y: auto;
+}
+@media (min-width: 768px) {
+  .navbar-collapse {
+    width: auto;
+    border-top: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-collapse.collapse {
+    display: block !important;
+    height: auto !important;
+    padding-bottom: 0;
+    overflow: visible !important;
+  }
+  .navbar-collapse.in {
+    overflow-y: visible;
+  }
+  .navbar-fixed-top .navbar-collapse,
+  .navbar-static-top .navbar-collapse,
+  .navbar-fixed-bottom .navbar-collapse {
+    padding-right: 0;
+    padding-left: 0;
+  }
+}
+.navbar-fixed-top .navbar-collapse,
+.navbar-fixed-bottom .navbar-collapse {
+  max-height: 340px;
+}
+@media (max-device-width: 480px) and (orientation: landscape) {
+  .navbar-fixed-top .navbar-collapse,
+  .navbar-fixed-bottom .navbar-collapse {
+    max-height: 200px;
+  }
+}
+.container > .navbar-header,
+.container-fluid > .navbar-header,
+.container > .navbar-collapse,
+.container-fluid > .navbar-collapse {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+@media (min-width: 768px) {
+  .container > .navbar-header,
+  .container-fluid > .navbar-header,
+  .container > .navbar-collapse,
+  .container-fluid > .navbar-collapse {
+    margin-right: 0;
+    margin-left: 0;
+  }
+}
+.navbar-static-top {
+  z-index: 1000;
+  border-width: 0 0 1px;
+}
+@media (min-width: 768px) {
+  .navbar-static-top {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  position: fixed;
+  right: 0;
+  left: 0;
+  z-index: 1030;
+}
+@media (min-width: 768px) {
+  .navbar-fixed-top,
+  .navbar-fixed-bottom {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top {
+  top: 0;
+  border-width: 0 0 1px;
+}
+.navbar-fixed-bottom {
+  bottom: 0;
+  margin-bottom: 0;
+  border-width: 1px 0 0;
+}
+.navbar-brand {
+  float: left;
+  height: 50px;
+  padding: 15px 15px;
+  font-size: 18px;
+  line-height: 20px;
+}
+.navbar-brand:hover,
+.navbar-brand:focus {
+  text-decoration: none;
+}
+.navbar-brand > img {
+  display: block;
+}
+@media (min-width: 768px) {
+  .navbar > .container .navbar-brand,
+  .navbar > .container-fluid .navbar-brand {
+    margin-left: -15px;
+  }
+}
+.navbar-toggle {
+  position: relative;
+  float: right;
+  padding: 9px 10px;
+  margin-top: 8px;
+  margin-right: 15px;
+  margin-bottom: 8px;
+  background-color: transparent;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.navbar-toggle:focus {
+  outline: 0;
+}
+.navbar-toggle .icon-bar {
+  display: block;
+  width: 22px;
+  height: 2px;
+  border-radius: 1px;
+}
+.navbar-toggle .icon-bar + .icon-bar {
+  margin-top: 4px;
+}
+@media (min-width: 768px) {
+  .navbar-toggle {
+    display: none;
+  }
+}
+.navbar-nav {
+  margin: 7.5px -15px;
+}
+.navbar-nav > li > a {
+  padding-top: 10px;
+  padding-bottom: 10px;
+  line-height: 20px;
+}
+@media (max-width: 767px) {
+  .navbar-nav .open .dropdown-menu {
+    position: static;
+    float: none;
+    width: auto;
+    margin-top: 0;
+    background-color: transparent;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-nav .open .dropdown-menu > li > a,
+  .navbar-nav .open .dropdown-menu .dropdown-header {
+    padding: 5px 15px 5px 25px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a {
+    line-height: 20px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-nav .open .dropdown-menu > li > a:focus {
+    background-image: none;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-nav {
+    float: left;
+    margin: 0;
+  }
+  .navbar-nav > li {
+    float: left;
+  }
+  .navbar-nav > li > a {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+}
+.navbar-form {
+  padding: 10px 15px;
+  margin-top: 8px;
+  margin-right: -15px;
+  margin-bottom: 8px;
+  margin-left: -15px;
+  border-top: 1px solid transparent;
+  border-bottom: 1px solid transparent;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+}
+@media (min-width: 768px) {
+  .navbar-form .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .navbar-form .form-control-static {
+    display: inline-block;
+  }
+  .navbar-form .input-group {
+    display: inline-table;
+    vertical-align: middle;
+  }
+  .navbar-form .input-group .input-group-addon,
+  .navbar-form .input-group .input-group-btn,
+  .navbar-form .input-group .form-control {
+    width: auto;
+  }
+  .navbar-form .input-group > .form-control {
+    width: 100%;
+  }
+  .navbar-form .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio,
+  .navbar-form .checkbox {
+    display: inline-block;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio label,
+  .navbar-form .checkbox label {
+    padding-left: 0;
+  }
+  .navbar-form .radio input[type="radio"],
+  .navbar-form .checkbox input[type="checkbox"] {
+    position: relative;
+    margin-left: 0;
+  }
+  .navbar-form .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+@media (max-width: 767px) {
+  .navbar-form .form-group {
+    margin-bottom: 5px;
+  }
+  .navbar-form .form-group:last-child {
+    margin-bottom: 0;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-form {
+    width: auto;
+    padding-top: 0;
+    padding-bottom: 0;
+    margin-right: 0;
+    margin-left: 0;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+}
+.navbar-nav > li > .dropdown-menu {
+  margin-top: 0;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+  margin-bottom: 0;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.navbar-btn {
+  margin-top: 8px;
+  margin-bottom: 8px;
+}
+.navbar-btn.btn-sm {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.navbar-btn.btn-xs {
+  margin-top: 14px;
+  margin-bottom: 14px;
+}
+.navbar-text {
+  margin-top: 15px;
+  margin-bottom: 15px;
+}
+@media (min-width: 768px) {
+  .navbar-text {
+    float: left;
+    margin-right: 15px;
+    margin-left: 15px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-left {
+    float: left !important;
+  }
+  .navbar-right {
+    float: right !important;
+    margin-right: -15px;
+  }
+  .navbar-right ~ .navbar-right {
+    margin-right: 0;
+  }
+}
+.navbar-default {
+  background-color: #f8f8f8;
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-brand {
+  color: #777;
+}
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+  color: #5e5e5e;
+  background-color: transparent;
+}
+.navbar-default .navbar-text {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+  color: #333;
+  background-color: transparent;
+}
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+  color: #ccc;
+  background-color: transparent;
+}
+.navbar-default .navbar-toggle {
+  border-color: #ddd;
+}
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+  background-color: #ddd;
+}
+.navbar-default .navbar-toggle .icon-bar {
+  background-color: #888;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+@media (max-width: 767px) {
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+    color: #777;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #333;
+    background-color: transparent;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #555;
+    background-color: #e7e7e7;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #ccc;
+    background-color: transparent;
+  }
+}
+.navbar-default .navbar-link {
+  color: #777;
+}
+.navbar-default .navbar-link:hover {
+  color: #333;
+}
+.navbar-default .btn-link {
+  color: #777;
+}
+.navbar-default .btn-link:hover,
+.navbar-default .btn-link:focus {
+  color: #333;
+}
+.navbar-default .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-default .btn-link:hover,
+.navbar-default .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-default .btn-link:focus {
+  color: #ccc;
+}
+.navbar-inverse {
+  background-color: #222;
+  border-color: #080808;
+}
+.navbar-inverse .navbar-brand {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-text {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-nav > li > a {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+  color: #444;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-toggle {
+  border-color: #333;
+}
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+  background-color: #333;
+}
+.navbar-inverse .navbar-toggle .icon-bar {
+  background-color: #fff;
+}
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+  border-color: #101010;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+@media (max-width: 767px) {
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+    border-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+    color: #9d9d9d;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #fff;
+    background-color: transparent;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #fff;
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #444;
+    background-color: transparent;
+  }
+}
+.navbar-inverse .navbar-link {
+  color: #9d9d9d;
+}
+.navbar-inverse .navbar-link:hover {
+  color: #fff;
+}
+.navbar-inverse .btn-link {
+  color: #9d9d9d;
+}
+.navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link:focus {
+  color: #fff;
+}
+.navbar-inverse .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-inverse .btn-link:focus {
+  color: #444;
+}
+.breadcrumb {
+  padding: 8px 15px;
+  margin-bottom: 20px;
+  list-style: none;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+.breadcrumb > li {
+  display: inline-block;
+}
+.breadcrumb > li + li:before {
+  padding: 0 5px;
+  color: #ccc;
+  content: "/\00a0";
+}
+.breadcrumb > .active {
+  color: #777;
+}
+.pagination {
+  display: inline-block;
+  padding-left: 0;
+  margin: 20px 0;
+  border-radius: 4px;
+}
+.pagination > li {
+  display: inline;
+}
+.pagination > li > a,
+.pagination > li > span {
+  position: relative;
+  float: left;
+  padding: 6px 12px;
+  margin-left: -1px;
+  line-height: 1.42857143;
+  color: #337ab7;
+  text-decoration: none;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+  margin-left: 0;
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+  z-index: 2;
+  color: #23527c;
+  background-color: #eee;
+  border-color: #ddd;
+}
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+  z-index: 3;
+  color: #fff;
+  cursor: default;
+  background-color: #337ab7;
+  border-color: #337ab7;
+}
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #fff;
+  border-color: #ddd;
+}
+.pagination-lg > li > a,
+.pagination-lg > li > span {
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.3333333;
+}
+.pagination-lg > li:first-child > a,
+.pagination-lg > li:first-child > span {
+  border-top-left-radius: 6px;
+  border-bottom-left-radius: 6px;
+}
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+  border-top-right-radius: 6px;
+  border-bottom-right-radius: 6px;
+}
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+}
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+  border-top-left-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+  border-top-right-radius: 3px;
+  border-bottom-right-radius: 3px;
+}
+.pager {
+  padding-left: 0;
+  margin: 20px 0;
+  text-align: center;
+  list-style: none;
+}
+.pager li {
+  display: inline;
+}
+.pager li > a,
+.pager li > span {
+  display: inline-block;
+  padding: 5px 14px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 15px;
+}
+.pager li > a:hover,
+.pager li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.pager .next > a,
+.pager .next > span {
+  float: right;
+}
+.pager .previous > a,
+.pager .previous > span {
+  float: left;
+}
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #fff;
+}
+.label {
+  display: inline;
+  padding: .2em .6em .3em;
+  font-size: 75%;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: .25em;
+}
+a.label:hover,
+a.label:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+.label:empty {
+  display: none;
+}
+.btn .label {
+  position: relative;
+  top: -1px;
+}
+.label-default {
+  background-color: #777;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+  background-color: #5e5e5e;
+}
+.label-primary {
+  background-color: #337ab7;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+  background-color: #286090;
+}
+.label-success {
+  background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+  background-color: #449d44;
+}
+.label-info {
+  background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+  background-color: #31b0d5;
+}
+.label-warning {
+  background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+  background-color: #ec971f;
+}
+.label-danger {
+  background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+  background-color: #c9302c;
+}
+.badge {
+  display: inline-block;
+  min-width: 10px;
+  padding: 3px 7px;
+  font-size: 12px;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  background-color: #777;
+  border-radius: 10px;
+}
+.badge:empty {
+  display: none;
+}
+.btn .badge {
+  position: relative;
+  top: -1px;
+}
+.btn-xs .badge,
+.btn-group-xs > .btn .badge {
+  top: 0;
+  padding: 1px 5px;
+}
+a.badge:hover,
+a.badge:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+  color: #337ab7;
+  background-color: #fff;
+}
+.list-group-item > .badge {
+  float: right;
+}
+.list-group-item > .badge + .badge {
+  margin-right: 5px;
+}
+.nav-pills > li > a > .badge {
+  margin-left: 3px;
+}
+.jumbotron {
+  padding-top: 30px;
+  padding-bottom: 30px;
+  margin-bottom: 30px;
+  color: inherit;
+  background-color: #eee;
+}
+.jumbotron h1,
+.jumbotron .h1 {
+  color: inherit;
+}
+.jumbotron p {
+  margin-bottom: 15px;
+  font-size: 21px;
+  font-weight: 200;
+}
+.jumbotron > hr {
+  border-top-color: #d5d5d5;
+}
+.container .jumbotron,
+.container-fluid .jumbotron {
+  padding-right: 15px;
+  padding-left: 15px;
+  border-radius: 6px;
+}
+.jumbotron .container {
+  max-width: 100%;
+}
+@media screen and (min-width: 768px) {
+  .jumbotron {
+    padding-top: 48px;
+    padding-bottom: 48px;
+  }
+  .container .jumbotron,
+  .container-fluid .jumbotron {
+    padding-right: 60px;
+    padding-left: 60px;
+  }
+  .jumbotron h1,
+  .jumbotron .h1 {
+    font-size: 63px;
+  }
+}
+.thumbnail {
+  display: block;
+  padding: 4px;
+  margin-bottom: 20px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: border .2s ease-in-out;
+       -o-transition: border .2s ease-in-out;
+          transition: border .2s ease-in-out;
+}
+.thumbnail > img,
+.thumbnail a > img {
+  margin-right: auto;
+  margin-left: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus,
+a.thumbnail.active {
+  border-color: #337ab7;
+}
+.thumbnail .caption {
+  padding: 9px;
+  color: #333;
+}
+.alert {
+  padding: 15px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.alert h4 {
+  margin-top: 0;
+  color: inherit;
+}
+.alert .alert-link {
+  font-weight: bold;
+}
+.alert > p,
+.alert > ul {
+  margin-bottom: 0;
+}
+.alert > p + p {
+  margin-top: 5px;
+}
+.alert-dismissable,
+.alert-dismissible {
+  padding-right: 35px;
+}
+.alert-dismissable .close,
+.alert-dismissible .close {
+  position: relative;
+  top: -2px;
+  right: -21px;
+  color: inherit;
+}
+.alert-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.alert-success hr {
+  border-top-color: #c9e2b3;
+}
+.alert-success .alert-link {
+  color: #2b542c;
+}
+.alert-info {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.alert-info hr {
+  border-top-color: #a6e1ec;
+}
+.alert-info .alert-link {
+  color: #245269;
+}
+.alert-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.alert-warning hr {
+  border-top-color: #f7e1b5;
+}
+.alert-warning .alert-link {
+  color: #66512c;
+}
+.alert-danger {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.alert-danger hr {
+  border-top-color: #e4b9c0;
+}
+.alert-danger .alert-link {
+  color: #843534;
+}
+@-webkit-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@-o-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+.progress {
+  height: 20px;
+  margin-bottom: 20px;
+  overflow: hidden;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+}
+.progress-bar {
+  float: left;
+  width: 0;
+  height: 100%;
+  font-size: 12px;
+  line-height: 20px;
+  color: #fff;
+  text-align: center;
+  background-color: #337ab7;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+  -webkit-transition: width .6s ease;
+       -o-transition: width .6s ease;
+          transition: width .6s ease;
+}
+.progress-striped .progress-bar,
+.progress-bar-striped {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  -webkit-background-size: 40px 40px;
+          background-size: 40px 40px;
+}
+.progress.active .progress-bar,
+.progress-bar.active {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+       -o-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-bar-success {
+  background-color: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-info {
+  background-color: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-warning {
+  background-color: #f0ad4e;
+}
+.progress-striped .progress-bar-warning {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-danger {
+  background-color: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.media {
+  margin-top: 15px;
+}
+.media:first-child {
+  margin-top: 0;
+}
+.media,
+.media-body {
+  overflow: hidden;
+  zoom: 1;
+}
+.media-body {
+  width: 10000px;
+}
+.media-object {
+  display: block;
+}
+.media-object.img-thumbnail {
+  max-width: none;
+}
+.media-right,
+.media > .pull-right {
+  padding-left: 10px;
+}
+.media-left,
+.media > .pull-left {
+  padding-right: 10px;
+}
+.media-left,
+.media-right,
+.media-body {
+  display: table-cell;
+  vertical-align: top;
+}
+.media-middle {
+  vertical-align: middle;
+}
+.media-bottom {
+  vertical-align: bottom;
+}
+.media-heading {
+  margin-top: 0;
+  margin-bottom: 5px;
+}
+.media-list {
+  padding-left: 0;
+  list-style: none;
+}
+.list-group {
+  padding-left: 0;
+  margin-bottom: 20px;
+}
+.list-group-item {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+  margin-bottom: -1px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.list-group-item:first-child {
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+.list-group-item:last-child {
+  margin-bottom: 0;
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+a.list-group-item,
+button.list-group-item {
+  color: #555;
+}
+a.list-group-item .list-group-item-heading,
+button.list-group-item .list-group-item-heading {
+  color: #333;
+}
+a.list-group-item:hover,
+button.list-group-item:hover,
+a.list-group-item:focus,
+button.list-group-item:focus {
+  color: #555;
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+button.list-group-item {
+  width: 100%;
+  text-align: left;
+}
+.list-group-item.disabled,
+.list-group-item.disabled:hover,
+.list-group-item.disabled:focus {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #eee;
+}
+.list-group-item.disabled .list-group-item-heading,
+.list-group-item.disabled:hover .list-group-item-heading,
+.list-group-item.disabled:focus .list-group-item-heading {
+  color: inherit;
+}
+.list-group-item.disabled .list-group-item-text,
+.list-group-item.disabled:hover .list-group-item-text,
+.list-group-item.disabled:focus .list-group-item-text {
+  color: #777;
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+  z-index: 2;
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #337ab7;
+}
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading,
+.list-group-item.active .list-group-item-heading > small,
+.list-group-item.active:hover .list-group-item-heading > small,
+.list-group-item.active:focus .list-group-item-heading > small,
+.list-group-item.active .list-group-item-heading > .small,
+.list-group-item.active:hover .list-group-item-heading > .small,
+.list-group-item.active:focus .list-group-item-heading > .small {
+  color: inherit;
+}
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+  color: #c7ddef;
+}
+.list-group-item-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+}
+a.list-group-item-success,
+button.list-group-item-success {
+  color: #3c763d;
+}
+a.list-group-item-success .list-group-item-heading,
+button.list-group-item-success .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-success:hover,
+button.list-group-item-success:hover,
+a.list-group-item-success:focus,
+button.list-group-item-success:focus {
+  color: #3c763d;
+  background-color: #d0e9c6;
+}
+a.list-group-item-success.active,
+button.list-group-item-success.active,
+a.list-group-item-success.active:hover,
+button.list-group-item-success.active:hover,
+a.list-group-item-success.active:focus,
+button.list-group-item-success.active:focus {
+  color: #fff;
+  background-color: #3c763d;
+  border-color: #3c763d;
+}
+.list-group-item-info {
+  color: #31708f;
+  background-color: #d9edf7;
+}
+a.list-group-item-info,
+button.list-group-item-info {
+  color: #31708f;
+}
+a.list-group-item-info .list-group-item-heading,
+button.list-group-item-info .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-info:hover,
+button.list-group-item-info:hover,
+a.list-group-item-info:focus,
+button.list-group-item-info:focus {
+  color: #31708f;
+  background-color: #c4e3f3;
+}
+a.list-group-item-info.active,
+button.list-group-item-info.active,
+a.list-group-item-info.active:hover,
+button.list-group-item-info.active:hover,
+a.list-group-item-info.active:focus,
+button.list-group-item-info.active:focus {
+  color: #fff;
+  background-color: #31708f;
+  border-color: #31708f;
+}
+.list-group-item-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+}
+a.list-group-item-warning,
+button.list-group-item-warning {
+  color: #8a6d3b;
+}
+a.list-group-item-warning .list-group-item-heading,
+button.list-group-item-warning .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-warning:hover,
+button.list-group-item-warning:hover,
+a.list-group-item-warning:focus,
+button.list-group-item-warning:focus {
+  color: #8a6d3b;
+  background-color: #faf2cc;
+}
+a.list-group-item-warning.active,
+button.list-group-item-warning.active,
+a.list-group-item-warning.active:hover,
+button.list-group-item-warning.active:hover,
+a.list-group-item-warning.active:focus,
+button.list-group-item-warning.active:focus {
+  color: #fff;
+  background-color: #8a6d3b;
+  border-color: #8a6d3b;
+}
+.list-group-item-danger {
+  color: #a94442;
+  background-color: #f2dede;
+}
+a.list-group-item-danger,
+button.list-group-item-danger {
+  color: #a94442;
+}
+a.list-group-item-danger .list-group-item-heading,
+button.list-group-item-danger .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-danger:hover,
+button.list-group-item-danger:hover,
+a.list-group-item-danger:focus,
+button.list-group-item-danger:focus {
+  color: #a94442;
+  background-color: #ebcccc;
+}
+a.list-group-item-danger.active,
+button.list-group-item-danger.active,
+a.list-group-item-danger.active:hover,
+button.list-group-item-danger.active:hover,
+a.list-group-item-danger.active:focus,
+button.list-group-item-danger.active:focus {
+  color: #fff;
+  background-color: #a94442;
+  border-color: #a94442;
+}
+.list-group-item-heading {
+  margin-top: 0;
+  margin-bottom: 5px;
+}
+.list-group-item-text {
+  margin-bottom: 0;
+  line-height: 1.3;
+}
+.panel {
+  margin-bottom: 20px;
+  background-color: #fff;
+  border: 1px solid transparent;
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+}
+.panel-body {
+  padding: 15px;
+}
+.panel-heading {
+  padding: 10px 15px;
+  border-bottom: 1px solid transparent;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+  color: inherit;
+}
+.panel-title {
+  margin-top: 0;
+  margin-bottom: 0;
+  font-size: 16px;
+  color: inherit;
+}
+.panel-title > a,
+.panel-title > small,
+.panel-title > .small,
+.panel-title > small > a,
+.panel-title > .small > a {
+  color: inherit;
+}
+.panel-footer {
+  padding: 10px 15px;
+  background-color: #f5f5f5;
+  border-top: 1px solid #ddd;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .list-group,
+.panel > .panel-collapse > .list-group {
+  margin-bottom: 0;
+}
+.panel > .list-group .list-group-item,
+.panel > .panel-collapse > .list-group .list-group-item {
+  border-width: 1px 0;
+  border-radius: 0;
+}
+.panel > .list-group:first-child .list-group-item:first-child,
+.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {
+  border-top: 0;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .list-group:last-child .list-group-item:last-child,
+.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {
+  border-bottom: 0;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+  border-top-width: 0;
+}
+.list-group + .panel-footer {
+  border-top-width: 0;
+}
+.panel > .table,
+.panel > .table-responsive > .table,
+.panel > .panel-collapse > .table {
+  margin-bottom: 0;
+}
+.panel > .table caption,
+.panel > .table-responsive > .table caption,
+.panel > .panel-collapse > .table caption {
+  padding-right: 15px;
+  padding-left: 15px;
+}
+.panel > .table:first-child,
+.panel > .table-responsive:first-child > .table:first-child {
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+  border-top-left-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+  border-top-right-radius: 3px;
+}
+.panel > .table:last-child,
+.panel > .table-responsive:last-child > .table:last-child {
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {
+  border-bottom-right-radius: 3px;
+}
+.panel > .panel-body + .table,
+.panel > .panel-body + .table-responsive,
+.panel > .table + .panel-body,
+.panel > .table-responsive + .panel-body {
+  border-top: 1px solid #ddd;
+}
+.panel > .table > tbody:first-child > tr:first-child th,
+.panel > .table > tbody:first-child > tr:first-child td {
+  border-top: 0;
+}
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+  border: 0;
+}
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+  border-left: 0;
+}
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+  border-right: 0;
+}
+.panel > .table-bordered > thead > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
+.panel > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-bordered > thead > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
+.panel > .table-bordered > tbody > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
+  border-bottom: 0;
+}
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {
+  border-bottom: 0;
+}
+.panel > .table-responsive {
+  margin-bottom: 0;
+  border: 0;
+}
+.panel-group {
+  margin-bottom: 20px;
+}
+.panel-group .panel {
+  margin-bottom: 0;
+  border-radius: 4px;
+}
+.panel-group .panel + .panel {
+  margin-top: 5px;
+}
+.panel-group .panel-heading {
+  border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse > .panel-body,
+.panel-group .panel-heading + .panel-collapse > .list-group {
+  border-top: 1px solid #ddd;
+}
+.panel-group .panel-footer {
+  border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+  border-bottom: 1px solid #ddd;
+}
+.panel-default {
+  border-color: #ddd;
+}
+.panel-default > .panel-heading {
+  color: #333;
+  background-color: #f5f5f5;
+  border-color: #ddd;
+}
+.panel-default > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #ddd;
+}
+.panel-default > .panel-heading .badge {
+  color: #f5f5f5;
+  background-color: #333;
+}
+.panel-default > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #ddd;
+}
+.panel-primary {
+  border-color: #337ab7;
+}
+.panel-primary > .panel-heading {
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #337ab7;
+}
+.panel-primary > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #337ab7;
+}
+.panel-primary > .panel-heading .badge {
+  color: #337ab7;
+  background-color: #fff;
+}
+.panel-primary > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #337ab7;
+}
+.panel-success {
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #d6e9c6;
+}
+.panel-success > .panel-heading .badge {
+  color: #dff0d8;
+  background-color: #3c763d;
+}
+.panel-success > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #d6e9c6;
+}
+.panel-info {
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #bce8f1;
+}
+.panel-info > .panel-heading .badge {
+  color: #d9edf7;
+  background-color: #31708f;
+}
+.panel-info > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #bce8f1;
+}
+.panel-warning {
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #faebcc;
+}
+.panel-warning > .panel-heading .badge {
+  color: #fcf8e3;
+  background-color: #8a6d3b;
+}
+.panel-warning > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #faebcc;
+}
+.panel-danger {
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #ebccd1;
+}
+.panel-danger > .panel-heading .badge {
+  color: #f2dede;
+  background-color: #a94442;
+}
+.panel-danger > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #ebccd1;
+}
+.embed-responsive {
+  position: relative;
+  display: block;
+  height: 0;
+  padding: 0;
+  overflow: hidden;
+}
+.embed-responsive .embed-responsive-item,
+.embed-responsive iframe,
+.embed-responsive embed,
+.embed-responsive object,
+.embed-responsive video {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  border: 0;
+}
+.embed-responsive-16by9 {
+  padding-bottom: 56.25%;
+}
+.embed-responsive-4by3 {
+  padding-bottom: 75%;
+}
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  border: 1px solid #e3e3e3;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+}
+.well blockquote {
+  border-color: #ddd;
+  border-color: rgba(0, 0, 0, .15);
+}
+.well-lg {
+  padding: 24px;
+  border-radius: 6px;
+}
+.well-sm {
+  padding: 9px;
+  border-radius: 3px;
+}
+.close {
+  float: right;
+  font-size: 21px;
+  font-weight: bold;
+  line-height: 1;
+  color: #000;
+  text-shadow: 0 1px 0 #fff;
+  filter: alpha(opacity=20);
+  opacity: .2;
+}
+.close:hover,
+.close:focus {
+  color: #000;
+  text-decoration: none;
+  cursor: pointer;
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+button.close {
+  -webkit-appearance: none;
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+}
+.modal-open {
+  overflow: hidden;
+}
+.modal {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1050;
+  display: none;
+  overflow: hidden;
+  -webkit-overflow-scrolling: touch;
+  outline: 0;
+}
+.modal.fade .modal-dialog {
+  -webkit-transition: -webkit-transform .3s ease-out;
+       -o-transition:      -o-transform .3s ease-out;
+          transition:         transform .3s ease-out;
+  -webkit-transform: translate(0, -25%);
+      -ms-transform: translate(0, -25%);
+       -o-transform: translate(0, -25%);
+          transform: translate(0, -25%);
+}
+.modal.in .modal-dialog {
+  -webkit-transform: translate(0, 0);
+      -ms-transform: translate(0, 0);
+       -o-transform: translate(0, 0);
+          transform: translate(0, 0);
+}
+.modal-open .modal {
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+.modal-dialog {
+  position: relative;
+  width: auto;
+  margin: 10px;
+}
+.modal-content {
+  position: relative;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #999;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  outline: 0;
+  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+          box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+}
+.modal-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1040;
+  background-color: #000;
+}
+.modal-backdrop.fade {
+  filter: alpha(opacity=0);
+  opacity: 0;
+}
+.modal-backdrop.in {
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.modal-header {
+  padding: 15px;
+  border-bottom: 1px solid #e5e5e5;
+}
+.modal-header .close {
+  margin-top: -2px;
+}
+.modal-title {
+  margin: 0;
+  line-height: 1.42857143;
+}
+.modal-body {
+  position: relative;
+  padding: 15px;
+}
+.modal-footer {
+  padding: 15px;
+  text-align: right;
+  border-top: 1px solid #e5e5e5;
+}
+.modal-footer .btn + .btn {
+  margin-bottom: 0;
+  margin-left: 5px;
+}
+.modal-footer .btn-group .btn + .btn {
+  margin-left: -1px;
+}
+.modal-footer .btn-block + .btn-block {
+  margin-left: 0;
+}
+.modal-scrollbar-measure {
+  position: absolute;
+  top: -9999px;
+  width: 50px;
+  height: 50px;
+  overflow: scroll;
+}
+@media (min-width: 768px) {
+  .modal-dialog {
+    width: 600px;
+    margin: 30px auto;
+  }
+  .modal-content {
+    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+            box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+  }
+  .modal-sm {
+    width: 300px;
+  }
+}
+@media (min-width: 992px) {
+  .modal-lg {
+    width: 900px;
+  }
+}
+.tooltip {
+  position: absolute;
+  z-index: 1070;
+  display: block;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 12px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: left;
+  text-align: start;
+  text-decoration: none;
+  text-shadow: none;
+  text-transform: none;
+  letter-spacing: normal;
+  word-break: normal;
+  word-spacing: normal;
+  word-wrap: normal;
+  white-space: normal;
+  filter: alpha(opacity=0);
+  opacity: 0;
+
+  line-break: auto;
+}
+.tooltip.in {
+  filter: alpha(opacity=90);
+  opacity: .9;
+}
+.tooltip.top {
+  padding: 5px 0;
+  margin-top: -3px;
+}
+.tooltip.right {
+  padding: 0 5px;
+  margin-left: 3px;
+}
+.tooltip.bottom {
+  padding: 5px 0;
+  margin-top: 3px;
+}
+.tooltip.left {
+  padding: 0 5px;
+  margin-left: -3px;
+}
+.tooltip-inner {
+  max-width: 200px;
+  padding: 3px 8px;
+  color: #fff;
+  text-align: center;
+  background-color: #000;
+  border-radius: 4px;
+}
+.tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.tooltip.top .tooltip-arrow {
+  bottom: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-left .tooltip-arrow {
+  right: 5px;
+  bottom: 0;
+  margin-bottom: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-right .tooltip-arrow {
+  bottom: 0;
+  left: 5px;
+  margin-bottom: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.right .tooltip-arrow {
+  top: 50%;
+  left: 0;
+  margin-top: -5px;
+  border-width: 5px 5px 5px 0;
+  border-right-color: #000;
+}
+.tooltip.left .tooltip-arrow {
+  top: 50%;
+  right: 0;
+  margin-top: -5px;
+  border-width: 5px 0 5px 5px;
+  border-left-color: #000;
+}
+.tooltip.bottom .tooltip-arrow {
+  top: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-left .tooltip-arrow {
+  top: 0;
+  right: 5px;
+  margin-top: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-right .tooltip-arrow {
+  top: 0;
+  left: 5px;
+  margin-top: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1060;
+  display: none;
+  max-width: 276px;
+  padding: 1px;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: left;
+  text-align: start;
+  text-decoration: none;
+  text-shadow: none;
+  text-transform: none;
+  letter-spacing: normal;
+  word-break: normal;
+  word-spacing: normal;
+  word-wrap: normal;
+  white-space: normal;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+          box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+
+  line-break: auto;
+}
+.popover.top {
+  margin-top: -10px;
+}
+.popover.right {
+  margin-left: 10px;
+}
+.popover.bottom {
+  margin-top: 10px;
+}
+.popover.left {
+  margin-left: -10px;
+}
+.popover-title {
+  padding: 8px 14px;
+  margin: 0;
+  font-size: 14px;
+  background-color: #f7f7f7;
+  border-bottom: 1px solid #ebebeb;
+  border-radius: 5px 5px 0 0;
+}
+.popover-content {
+  padding: 9px 14px;
+}
+.popover > .arrow,
+.popover > .arrow:after {
+  position: absolute;
+  display: block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.popover > .arrow {
+  border-width: 11px;
+}
+.popover > .arrow:after {
+  content: "";
+  border-width: 10px;
+}
+.popover.top > .arrow {
+  bottom: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-color: #999;
+  border-top-color: rgba(0, 0, 0, .25);
+  border-bottom-width: 0;
+}
+.popover.top > .arrow:after {
+  bottom: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-color: #fff;
+  border-bottom-width: 0;
+}
+.popover.right > .arrow {
+  top: 50%;
+  left: -11px;
+  margin-top: -11px;
+  border-right-color: #999;
+  border-right-color: rgba(0, 0, 0, .25);
+  border-left-width: 0;
+}
+.popover.right > .arrow:after {
+  bottom: -10px;
+  left: 1px;
+  content: " ";
+  border-right-color: #fff;
+  border-left-width: 0;
+}
+.popover.bottom > .arrow {
+  top: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-width: 0;
+  border-bottom-color: #999;
+  border-bottom-color: rgba(0, 0, 0, .25);
+}
+.popover.bottom > .arrow:after {
+  top: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-width: 0;
+  border-bottom-color: #fff;
+}
+.popover.left > .arrow {
+  top: 50%;
+  right: -11px;
+  margin-top: -11px;
+  border-right-width: 0;
+  border-left-color: #999;
+  border-left-color: rgba(0, 0, 0, .25);
+}
+.popover.left > .arrow:after {
+  right: 1px;
+  bottom: -10px;
+  content: " ";
+  border-right-width: 0;
+  border-left-color: #fff;
+}
+.carousel {
+  position: relative;
+}
+.carousel-inner {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+}
+.carousel-inner > .item {
+  position: relative;
+  display: none;
+  -webkit-transition: .6s ease-in-out left;
+       -o-transition: .6s ease-in-out left;
+          transition: .6s ease-in-out left;
+}
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  line-height: 1;
+}
+@media all and (transform-3d), (-webkit-transform-3d) {
+  .carousel-inner > .item {
+    -webkit-transition: -webkit-transform .6s ease-in-out;
+         -o-transition:      -o-transform .6s ease-in-out;
+            transition:         transform .6s ease-in-out;
+
+    -webkit-backface-visibility: hidden;
+            backface-visibility: hidden;
+    -webkit-perspective: 1000px;
+            perspective: 1000px;
+  }
+  .carousel-inner > .item.next,
+  .carousel-inner > .item.active.right {
+    left: 0;
+    -webkit-transform: translate3d(100%, 0, 0);
+            transform: translate3d(100%, 0, 0);
+  }
+  .carousel-inner > .item.prev,
+  .carousel-inner > .item.active.left {
+    left: 0;
+    -webkit-transform: translate3d(-100%, 0, 0);
+            transform: translate3d(-100%, 0, 0);
+  }
+  .carousel-inner > .item.next.left,
+  .carousel-inner > .item.prev.right,
+  .carousel-inner > .item.active {
+    left: 0;
+    -webkit-transform: translate3d(0, 0, 0);
+            transform: translate3d(0, 0, 0);
+  }
+}
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  display: block;
+}
+.carousel-inner > .active {
+  left: 0;
+}
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.carousel-inner > .next {
+  left: 100%;
+}
+.carousel-inner > .prev {
+  left: -100%;
+}
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+  left: 0;
+}
+.carousel-inner > .active.left {
+  left: -100%;
+}
+.carousel-inner > .active.right {
+  left: 100%;
+}
+.carousel-control {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 15%;
+  font-size: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+  background-color: rgba(0, 0, 0, 0);
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.carousel-control.left {
+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control.right {
+  right: 0;
+  left: auto;
+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control:hover,
+.carousel-control:focus {
+  color: #fff;
+  text-decoration: none;
+  filter: alpha(opacity=90);
+  outline: 0;
+  opacity: .9;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+  position: absolute;
+  top: 50%;
+  z-index: 5;
+  display: inline-block;
+  margin-top: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .glyphicon-chevron-left {
+  left: 50%;
+  margin-left: -10px;
+}
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-right {
+  right: 50%;
+  margin-right: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+  width: 20px;
+  height: 20px;
+  font-family: serif;
+  line-height: 1;
+}
+.carousel-control .icon-prev:before {
+  content: '\2039';
+}
+.carousel-control .icon-next:before {
+  content: '\203a';
+}
+.carousel-indicators {
+  position: absolute;
+  bottom: 10px;
+  left: 50%;
+  z-index: 15;
+  width: 60%;
+  padding-left: 0;
+  margin-left: -30%;
+  text-align: center;
+  list-style: none;
+}
+.carousel-indicators li {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  margin: 1px;
+  text-indent: -999px;
+  cursor: pointer;
+  background-color: #000 \9;
+  background-color: rgba(0, 0, 0, 0);
+  border: 1px solid #fff;
+  border-radius: 10px;
+}
+.carousel-indicators .active {
+  width: 12px;
+  height: 12px;
+  margin: 0;
+  background-color: #fff;
+}
+.carousel-caption {
+  position: absolute;
+  right: 15%;
+  bottom: 20px;
+  left: 15%;
+  z-index: 10;
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+}
+.carousel-caption .btn {
+  text-shadow: none;
+}
+@media screen and (min-width: 768px) {
+  .carousel-control .glyphicon-chevron-left,
+  .carousel-control .glyphicon-chevron-right,
+  .carousel-control .icon-prev,
+  .carousel-control .icon-next {
+    width: 30px;
+    height: 30px;
+    margin-top: -10px;
+    font-size: 30px;
+  }
+  .carousel-control .glyphicon-chevron-left,
+  .carousel-control .icon-prev {
+    margin-left: -10px;
+  }
+  .carousel-control .glyphicon-chevron-right,
+  .carousel-control .icon-next {
+    margin-right: -10px;
+  }
+  .carousel-caption {
+    right: 20%;
+    left: 20%;
+    padding-bottom: 30px;
+  }
+  .carousel-indicators {
+    bottom: 20px;
+  }
+}
+.clearfix:before,
+.clearfix:after,
+.dl-horizontal dd:before,
+.dl-horizontal dd:after,
+.container:before,
+.container:after,
+.container-fluid:before,
+.container-fluid:after,
+.row:before,
+.row:after,
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after,
+.btn-toolbar:before,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after,
+.nav:before,
+.nav:after,
+.navbar:before,
+.navbar:after,
+.navbar-header:before,
+.navbar-header:after,
+.navbar-collapse:before,
+.navbar-collapse:after,
+.pager:before,
+.pager:after,
+.panel-body:before,
+.panel-body:after,
+.modal-header:before,
+.modal-header:after,
+.modal-footer:before,
+.modal-footer:after {
+  display: table;
+  content: " ";
+}
+.clearfix:after,
+.dl-horizontal dd:after,
+.container:after,
+.container-fluid:after,
+.row:after,
+.form-horizontal .form-group:after,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:after,
+.nav:after,
+.navbar:after,
+.navbar-header:after,
+.navbar-collapse:after,
+.pager:after,
+.panel-body:after,
+.modal-header:after,
+.modal-footer:after {
+  clear: both;
+}
+.center-block {
+  display: block;
+  margin-right: auto;
+  margin-left: auto;
+}
+.pull-right {
+  float: right !important;
+}
+.pull-left {
+  float: left !important;
+}
+.hide {
+  display: none !important;
+}
+.show {
+  display: block !important;
+}
+.invisible {
+  visibility: hidden;
+}
+.text-hide {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+.hidden {
+  display: none !important;
+}
+.affix {
+  position: fixed;
+}
+@-ms-viewport {
+  width: device-width;
+}
+.visible-xs,
+.visible-sm,
+.visible-md,
+.visible-lg {
+  display: none !important;
+}
+.visible-xs-block,
+.visible-xs-inline,
+.visible-xs-inline-block,
+.visible-sm-block,
+.visible-sm-inline,
+.visible-sm-inline-block,
+.visible-md-block,
+.visible-md-inline,
+.visible-md-inline-block,
+.visible-lg-block,
+.visible-lg-inline,
+.visible-lg-inline-block {
+  display: none !important;
+}
+@media (max-width: 767px) {
+  .visible-xs {
+    display: block !important;
+  }
+  table.visible-xs {
+    display: table !important;
+  }
+  tr.visible-xs {
+    display: table-row !important;
+  }
+  th.visible-xs,
+  td.visible-xs {
+    display: table-cell !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-block {
+    display: block !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-inline {
+    display: inline !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm {
+    display: block !important;
+  }
+  table.visible-sm {
+    display: table !important;
+  }
+  tr.visible-sm {
+    display: table-row !important;
+  }
+  th.visible-sm,
+  td.visible-sm {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-block {
+    display: block !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md {
+    display: block !important;
+  }
+  table.visible-md {
+    display: table !important;
+  }
+  tr.visible-md {
+    display: table-row !important;
+  }
+  th.visible-md,
+  td.visible-md {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-block {
+    display: block !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg {
+    display: block !important;
+  }
+  table.visible-lg {
+    display: table !important;
+  }
+  tr.visible-lg {
+    display: table-row !important;
+  }
+  th.visible-lg,
+  td.visible-lg {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-block {
+    display: block !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (max-width: 767px) {
+  .hidden-xs {
+    display: none !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .hidden-sm {
+    display: none !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .hidden-md {
+    display: none !important;
+  }
+}
+@media (min-width: 1200px) {
+  .hidden-lg {
+    display: none !important;
+  }
+}
+.visible-print {
+  display: none !important;
+}
+@media print {
+  .visible-print {
+    display: block !important;
+  }
+  table.visible-print {
+    display: table !important;
+  }
+  tr.visible-print {
+    display: table-row !important;
+  }
+  th.visible-print,
+  td.visible-print {
+    display: table-cell !important;
+  }
+}
+.visible-print-block {
+  display: none !important;
+}
+@media print {
+  .visible-print-block {
+    display: block !important;
+  }
+}
+.visible-print-inline {
+  display: none !important;
+}
+@media print {
+  .visible-print-inline {
+    display: inline !important;
+  }
+}
+.visible-print-inline-block {
+  display: none !important;
+}
+@media print {
+  .visible-print-inline-block {
+    display: inline-block !important;
+  }
+}
+@media print {
+  .hidden-print {
+    display: none !important;
+  }
+}
+/*# sourceMappingURL=bootstrap.css.map */
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.css.map
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.css.map	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.css.map	(revision 5095)
@@ -0,0 +1,1 @@
+{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,iBAAA;CH8O9C;AG7OmC;EAAW,iBAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EErDA,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNqkCD;AIxgCD;EACE,UAAA;CJ0gCD;AIpgCD;EACE,uBAAA;CJsgCD;AIlgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CPglCD;AItgCD;EACE,mBAAA;CJwgCD;AIlgCD;EACE,aAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CPgmCD;AIlgCD;EACE,mBAAA;CJogCD;AI9/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJggCD;AIx/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJ0/BD;AIl/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJo/BH;AIz+BD;EACE,gBAAA;CJ2+BD;AQloCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR8oCD;AQnpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRoqCH;AQhqCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRqqCD;AQzqCD;;;;;;;;;;;;EAQI,eAAA;CR+qCH;AQ5qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRirCD;AQrrCD;;;;;;;;;;;;EAQI,eAAA;CR2rCH;AQvrCD;;EAAU,gBAAA;CR2rCT;AQ1rCD;;EAAU,gBAAA;CR8rCT;AQ7rCD;;EAAU,gBAAA;CRisCT;AQhsCD;;EAAU,gBAAA;CRosCT;AQnsCD;;EAAU,gBAAA;CRusCT;AQtsCD;;EAAU,gBAAA;CR0sCT;AQpsCD;EACE,iBAAA;CRssCD;AQnsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRqsCD;AQhsCD;EAwOA;IA1OI,gBAAA;GRssCD;CACF;AQ9rCD;;EAEE,eAAA;CRgsCD;AQ7rCD;;EAEE,0BAAA;EACA,cAAA;CR+rCD;AQ3rCD;EAAuB,iBAAA;CR8rCtB;AQ7rCD;EAAuB,kBAAA;CRgsCtB;AQ/rCD;EAAuB,mBAAA;CRksCtB;AQjsCD;EAAuB,oBAAA;CRosCtB;AQnsCD;EAAuB,oBAAA;CRssCtB;AQnsCD;EAAuB,0BAAA;CRssCtB;AQrsCD;EAAuB,0BAAA;CRwsCtB;AQvsCD;EAAuB,2BAAA;CR0sCtB;AQvsCD;EACE,eAAA;CRysCD;AQvsCD;ECrGE,eAAA;CT+yCD;AS9yCC;;EAEE,eAAA;CTgzCH;AQ3sCD;ECxGE,eAAA;CTszCD;ASrzCC;;EAEE,eAAA;CTuzCH;AQ/sCD;EC3GE,eAAA;CT6zCD;AS5zCC;;EAEE,eAAA;CT8zCH;AQntCD;EC9GE,eAAA;CTo0CD;ASn0CC;;EAEE,eAAA;CTq0CH;AQvtCD;ECjHE,eAAA;CT20CD;AS10CC;;EAEE,eAAA;CT40CH;AQvtCD;EAGE,YAAA;EE3HA,0BAAA;CVm1CD;AUl1CC;;EAEE,0BAAA;CVo1CH;AQztCD;EE9HE,0BAAA;CV01CD;AUz1CC;;EAEE,0BAAA;CV21CH;AQ7tCD;EEjIE,0BAAA;CVi2CD;AUh2CC;;EAEE,0BAAA;CVk2CH;AQjuCD;EEpIE,0BAAA;CVw2CD;AUv2CC;;EAEE,0BAAA;CVy2CH;AQruCD;EEvIE,0BAAA;CV+2CD;AU92CC;;EAEE,0BAAA;CVg3CH;AQpuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRsuCD;AQ9tCD;;EAEE,cAAA;EACA,oBAAA;CRguCD;AQnuCD;;;;EAMI,iBAAA;CRmuCH;AQ5tCD;EACE,gBAAA;EACA,iBAAA;CR8tCD;AQ1tCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR6tCD;AQ/tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR6tCH;AQxtCD;EACE,cAAA;EACA,oBAAA;CR0tCD;AQxtCD;;EAEE,wBAAA;CR0tCD;AQxtCD;EACE,kBAAA;CR0tCD;AQxtCD;EACE,eAAA;CR0tCD;AQjsCD;EA6EA;IAvFM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXs6CC;EQ9nCH;IAhFM,mBAAA;GRitCH;CACF;AQxsCD;;EAGE,aAAA;EACA,kCAAA;CRysCD;AQvsCD;EACE,eAAA;EA9IqB,0BAAA;CRw1CtB;AQrsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRusCD;AQlsCG;;;EACE,iBAAA;CRssCL;AQhtCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRksCH;AQhsCG;;;EACE,uBAAA;CRosCL;AQ5rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR8rCD;AQxrCG;;;;;;EAAW,YAAA;CRgsCd;AQ/rCG;;;;;;EACE,uBAAA;CRssCL;AQhsCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRksCD;AYx+CD;;;;EAIE,+DAAA;CZ0+CD;AYt+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZw+CD;AYp+CD;EACE,iBAAA;EACA,eAAA;EACA,YAAA;EACA,uBAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZs+CD;AY5+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZs+CH;AYj+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;CZm+CD;AY9+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZk+CH;AY79CD;EACE,kBAAA;EACA,mBAAA;CZ+9CD;AazhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd+hDD;AazhDC;EAqEF;IAvEI,aAAA;Gb+hDD;CACF;Aa3hDC;EAkEF;IApEI,aAAA;GbiiDD;CACF;Aa7hDD;EA+DA;IAjEI,cAAA;GbmiDD;CACF;Aa1hDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdojDD;AavhDD;ECvBE,mBAAA;EACA,oBAAA;CdijDD;AejjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfijDL;AejiDG;EACE,YAAA;CfmiDL;Ae5hDC;EACE,YAAA;Cf8hDH;Ae/hDC;EACE,oBAAA;CfiiDH;AeliDC;EACE,oBAAA;CfoiDH;AeriDC;EACE,WAAA;CfuiDH;AexiDC;EACE,oBAAA;Cf0iDH;Ae3iDC;EACE,oBAAA;Cf6iDH;Ae9iDC;EACE,WAAA;CfgjDH;AejjDC;EACE,oBAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,WAAA;CfyjDH;Ae1jDC;EACE,oBAAA;Cf4jDH;Ae7jDC;EACE,mBAAA;Cf+jDH;AejjDC;EACE,YAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,oBAAA;CfyjDH;Ae1jDC;EACE,WAAA;Cf4jDH;Ae7jDC;EACE,oBAAA;Cf+jDH;AehkDC;EACE,oBAAA;CfkkDH;AenkDC;EACE,WAAA;CfqkDH;AetkDC;EACE,oBAAA;CfwkDH;AezkDC;EACE,oBAAA;Cf2kDH;Ae5kDC;EACE,WAAA;Cf8kDH;Ae/kDC;EACE,oBAAA;CfilDH;AellDC;EACE,mBAAA;CfolDH;AehlDC;EACE,YAAA;CfklDH;AelmDC;EACE,WAAA;CfomDH;AermDC;EACE,mBAAA;CfumDH;AexmDC;EACE,mBAAA;Cf0mDH;Ae3mDC;EACE,UAAA;Cf6mDH;Ae9mDC;EACE,mBAAA;CfgnDH;AejnDC;EACE,mBAAA;CfmnDH;AepnDC;EACE,UAAA;CfsnDH;AevnDC;EACE,mBAAA;CfynDH;Ae1nDC;EACE,mBAAA;Cf4nDH;Ae7nDC;EACE,UAAA;Cf+nDH;AehoDC;EACE,mBAAA;CfkoDH;AenoDC;EACE,kBAAA;CfqoDH;AejoDC;EACE,WAAA;CfmoDH;AernDC;EACE,kBAAA;CfunDH;AexnDC;EACE,0BAAA;Cf0nDH;Ae3nDC;EACE,0BAAA;Cf6nDH;Ae9nDC;EACE,iBAAA;CfgoDH;AejoDC;EACE,0BAAA;CfmoDH;AepoDC;EACE,0BAAA;CfsoDH;AevoDC;EACE,iBAAA;CfyoDH;Ae1oDC;EACE,0BAAA;Cf4oDH;Ae7oDC;EACE,0BAAA;Cf+oDH;AehpDC;EACE,iBAAA;CfkpDH;AenpDC;EACE,0BAAA;CfqpDH;AetpDC;EACE,yBAAA;CfwpDH;AezpDC;EACE,gBAAA;Cf2pDH;Aa3pDD;EElCI;IACE,YAAA;GfgsDH;EezrDD;IACE,YAAA;Gf2rDD;Ee5rDD;IACE,oBAAA;Gf8rDD;Ee/rDD;IACE,oBAAA;GfisDD;EelsDD;IACE,WAAA;GfosDD;EersDD;IACE,oBAAA;GfusDD;EexsDD;IACE,oBAAA;Gf0sDD;Ee3sDD;IACE,WAAA;Gf6sDD;Ee9sDD;IACE,oBAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,WAAA;GfstDD;EevtDD;IACE,oBAAA;GfytDD;Ee1tDD;IACE,mBAAA;Gf4tDD;Ee9sDD;IACE,YAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,oBAAA;GfstDD;EevtDD;IACE,WAAA;GfytDD;Ee1tDD;IACE,oBAAA;Gf4tDD;Ee7tDD;IACE,oBAAA;Gf+tDD;EehuDD;IACE,WAAA;GfkuDD;EenuDD;IACE,oBAAA;GfquDD;EetuDD;IACE,oBAAA;GfwuDD;EezuDD;IACE,WAAA;Gf2uDD;Ee5uDD;IACE,oBAAA;Gf8uDD;Ee/uDD;IACE,mBAAA;GfivDD;Ee7uDD;IACE,YAAA;Gf+uDD;Ee/vDD;IACE,WAAA;GfiwDD;EelwDD;IACE,mBAAA;GfowDD;EerwDD;IACE,mBAAA;GfuwDD;EexwDD;IACE,UAAA;Gf0wDD;Ee3wDD;IACE,mBAAA;Gf6wDD;Ee9wDD;IACE,mBAAA;GfgxDD;EejxDD;IACE,UAAA;GfmxDD;EepxDD;IACE,mBAAA;GfsxDD;EevxDD;IACE,mBAAA;GfyxDD;Ee1xDD;IACE,UAAA;Gf4xDD;Ee7xDD;IACE,mBAAA;Gf+xDD;EehyDD;IACE,kBAAA;GfkyDD;Ee9xDD;IACE,WAAA;GfgyDD;EelxDD;IACE,kBAAA;GfoxDD;EerxDD;IACE,0BAAA;GfuxDD;EexxDD;IACE,0BAAA;Gf0xDD;Ee3xDD;IACE,iBAAA;Gf6xDD;Ee9xDD;IACE,0BAAA;GfgyDD;EejyDD;IACE,0BAAA;GfmyDD;EepyDD;IACE,iBAAA;GfsyDD;EevyDD;IACE,0BAAA;GfyyDD;Ee1yDD;IACE,0BAAA;Gf4yDD;Ee7yDD;IACE,iBAAA;Gf+yDD;EehzDD;IACE,0BAAA;GfkzDD;EenzDD;IACE,yBAAA;GfqzDD;EetzDD;IACE,gBAAA;GfwzDD;CACF;AahzDD;EE3CI;IACE,YAAA;Gf81DH;Eev1DD;IACE,YAAA;Gfy1DD;Ee11DD;IACE,oBAAA;Gf41DD;Ee71DD;IACE,oBAAA;Gf+1DD;Eeh2DD;IACE,WAAA;Gfk2DD;Een2DD;IACE,oBAAA;Gfq2DD;Eet2DD;IACE,oBAAA;Gfw2DD;Eez2DD;IACE,WAAA;Gf22DD;Ee52DD;IACE,oBAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,WAAA;Gfo3DD;Eer3DD;IACE,oBAAA;Gfu3DD;Eex3DD;IACE,mBAAA;Gf03DD;Ee52DD;IACE,YAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,oBAAA;Gfo3DD;Eer3DD;IACE,WAAA;Gfu3DD;Eex3DD;IACE,oBAAA;Gf03DD;Ee33DD;IACE,oBAAA;Gf63DD;Ee93DD;IACE,WAAA;Gfg4DD;Eej4DD;IACE,oBAAA;Gfm4DD;Eep4DD;IACE,oBAAA;Gfs4DD;Eev4DD;IACE,WAAA;Gfy4DD;Ee14DD;IACE,oBAAA;Gf44DD;Ee74DD;IACE,mBAAA;Gf+4DD;Ee34DD;IACE,YAAA;Gf64DD;Ee75DD;IACE,WAAA;Gf+5DD;Eeh6DD;IACE,mBAAA;Gfk6DD;Een6DD;IACE,mBAAA;Gfq6DD;Eet6DD;IACE,UAAA;Gfw6DD;Eez6DD;IACE,mBAAA;Gf26DD;Ee56DD;IACE,mBAAA;Gf86DD;Ee/6DD;IACE,UAAA;Gfi7DD;Eel7DD;IACE,mBAAA;Gfo7DD;Eer7DD;IACE,mBAAA;Gfu7DD;Eex7DD;IACE,UAAA;Gf07DD;Ee37DD;IACE,mBAAA;Gf67DD;Ee97DD;IACE,kBAAA;Gfg8DD;Ee57DD;IACE,WAAA;Gf87DD;Eeh7DD;IACE,kBAAA;Gfk7DD;Een7DD;IACE,0BAAA;Gfq7DD;Eet7DD;IACE,0BAAA;Gfw7DD;Eez7DD;IACE,iBAAA;Gf27DD;Ee57DD;IACE,0BAAA;Gf87DD;Ee/7DD;IACE,0BAAA;Gfi8DD;Eel8DD;IACE,iBAAA;Gfo8DD;Eer8DD;IACE,0BAAA;Gfu8DD;Eex8DD;IACE,0BAAA;Gf08DD;Ee38DD;IACE,iBAAA;Gf68DD;Ee98DD;IACE,0BAAA;Gfg9DD;Eej9DD;IACE,yBAAA;Gfm9DD;Eep9DD;IACE,gBAAA;Gfs9DD;CACF;Aa38DD;EE9CI;IACE,YAAA;Gf4/DH;Eer/DD;IACE,YAAA;Gfu/DD;Eex/DD;IACE,oBAAA;Gf0/DD;Ee3/DD;IACE,oBAAA;Gf6/DD;Ee9/DD;IACE,WAAA;GfggED;EejgED;IACE,oBAAA;GfmgED;EepgED;IACE,oBAAA;GfsgED;EevgED;IACE,WAAA;GfygED;Ee1gED;IACE,oBAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,WAAA;GfkhED;EenhED;IACE,oBAAA;GfqhED;EethED;IACE,mBAAA;GfwhED;Ee1gED;IACE,YAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,oBAAA;GfkhED;EenhED;IACE,WAAA;GfqhED;EethED;IACE,oBAAA;GfwhED;EezhED;IACE,oBAAA;Gf2hED;Ee5hED;IACE,WAAA;Gf8hED;Ee/hED;IACE,oBAAA;GfiiED;EeliED;IACE,oBAAA;GfoiED;EeriED;IACE,WAAA;GfuiED;EexiED;IACE,oBAAA;Gf0iED;Ee3iED;IACE,mBAAA;Gf6iED;EeziED;IACE,YAAA;Gf2iED;Ee3jED;IACE,WAAA;Gf6jED;Ee9jED;IACE,mBAAA;GfgkED;EejkED;IACE,mBAAA;GfmkED;EepkED;IACE,UAAA;GfskED;EevkED;IACE,mBAAA;GfykED;Ee1kED;IACE,mBAAA;Gf4kED;Ee7kED;IACE,UAAA;Gf+kED;EehlED;IACE,mBAAA;GfklED;EenlED;IACE,mBAAA;GfqlED;EetlED;IACE,UAAA;GfwlED;EezlED;IACE,mBAAA;Gf2lED;Ee5lED;IACE,kBAAA;Gf8lED;Ee1lED;IACE,WAAA;Gf4lED;Ee9kED;IACE,kBAAA;GfglED;EejlED;IACE,0BAAA;GfmlED;EeplED;IACE,0BAAA;GfslED;EevlED;IACE,iBAAA;GfylED;Ee1lED;IACE,0BAAA;Gf4lED;Ee7lED;IACE,0BAAA;Gf+lED;EehmED;IACE,iBAAA;GfkmED;EenmED;IACE,0BAAA;GfqmED;EetmED;IACE,0BAAA;GfwmED;EezmED;IACE,iBAAA;Gf2mED;Ee5mED;IACE,0BAAA;Gf8mED;Ee/mED;IACE,yBAAA;GfinED;EelnED;IACE,gBAAA;GfonED;CACF;AgBxrED;EACE,8BAAA;ChB0rED;AgBxrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChB0rED;AgBxrED;EACE,iBAAA;ChB0rED;AgBprED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBsrED;AgBzrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,2BAAA;ChBsrEP;AgBpsED;EAoBI,uBAAA;EACA,8BAAA;ChBmrEH;AgBxsED;;;;;;EA8BQ,cAAA;ChBkrEP;AgBhtED;EAoCI,2BAAA;ChB+qEH;AgBntED;EAyCI,uBAAA;ChB6qEH;AgBtqED;;;;;;EAOQ,aAAA;ChBuqEP;AgB5pED;EACE,uBAAA;ChB8pED;AgB/pED;;;;;;EAQQ,uBAAA;ChB+pEP;AgBvqED;;EAeM,yBAAA;ChB4pEL;AgBlpED;EAEI,0BAAA;ChBmpEH;AgB1oED;EAEI,0BAAA;ChB2oEH;AgBloED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBooED;AgB/nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBkoEL;AiB9wEC;;;;;;;;;;;;EAOI,0BAAA;CjBqxEL;AiB/wEC;;;;;EAMI,0BAAA;CjBgxEL;AiBnyEC;;;;;;;;;;;;EAOI,0BAAA;CjB0yEL;AiBpyEC;;;;;EAMI,0BAAA;CjBqyEL;AiBxzEC;;;;;;;;;;;;EAOI,0BAAA;CjB+zEL;AiBzzEC;;;;;EAMI,0BAAA;CjB0zEL;AiB70EC;;;;;;;;;;;;EAOI,0BAAA;CjBo1EL;AiB90EC;;;;;EAMI,0BAAA;CjB+0EL;AiBl2EC;;;;;;;;;;;;EAOI,0BAAA;CjBy2EL;AiBn2EC;;;;;EAMI,0BAAA;CjBo2EL;AgBltED;EACE,iBAAA;EACA,kBAAA;ChBotED;AgBvpED;EACA;IA3DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,uBAAA;GhBqtED;EgB9pEH;IAnDM,iBAAA;GhBotEH;EgBjqEH;;;;;;IA1CY,oBAAA;GhBmtET;EgBzqEH;IAlCM,UAAA;GhB8sEH;EgB5qEH;;;;;;IAzBY,eAAA;GhB6sET;EgBprEH;;;;;;IArBY,gBAAA;GhBitET;EgB5rEH;;;;IARY,iBAAA;GhB0sET;CACF;AkBp6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBm6ED;AkBh6ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBk6ED;AkB/5ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBi6ED;AkBt5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL63ET;AkBt5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBw5ED;AkBr5ED;EACE,eAAA;ClBu5ED;AkBn5ED;EACE,eAAA;EACA,YAAA;ClBq5ED;AkBj5ED;;EAEE,aAAA;ClBm5ED;AkB/4ED;;;EZvEE,qBAAA;EAEA,2CAAA;EACA,qBAAA;CN09ED;AkB/4ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClBi5ED;AkBv3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CL0zET;AmBl8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CL27ET;AK15EC;EACE,YAAA;EACA,WAAA;CL45EH;AK15EC;EAA0B,YAAA;CL65E3B;AK55EC;EAAgC,YAAA;CL+5EjC;AkBn4EC;EACE,UAAA;EACA,8BAAA;ClBq4EH;AkB73EC;;;EAGE,0BAAA;EACA,WAAA;ClB+3EH;AkB53EC;;EAEE,oBAAA;ClB83EH;AkB13EC;EACE,aAAA;ClB43EH;AkBh3ED;EACE,yBAAA;ClBk3ED;AkB10ED;EAtBI;;;;IACE,kBAAA;GlBs2EH;EkBn2EC;;;;;;;;IAEE,kBAAA;GlB22EH;EkBx2EC;;;;;;;;IAEE,kBAAA;GlBg3EH;CACF;AkBt2ED;EACE,oBAAA;ClBw2ED;AkBh2ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBk2ED;AkBv2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBm2EH;AkBh2ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBk2ED;AkB/1ED;;EAEE,iBAAA;ClBi2ED;AkB71ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClB+1ED;AkB71ED;;EAEE,cAAA;EACA,kBAAA;ClB+1ED;AkBt1EC;;;;;;EAGE,oBAAA;ClB21EH;AkBr1EC;;;;EAEE,oBAAA;ClBy1EH;AkBn1EC;;;;EAGI,oBAAA;ClBs1EL;AkB30ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClB20ED;AkBz0EC;;EAEE,gBAAA;EACA,iBAAA;ClB20EH;AkB9zED;ECnQE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBokFD;AmBlkFC;EACE,aAAA;EACA,kBAAA;CnBokFH;AmBjkFC;;EAEE,aAAA;CnBmkFH;AkB10ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClB20EH;AkBj1ED;EASI,aAAA;EACA,kBAAA;ClB20EH;AkBr1ED;;EAcI,aAAA;ClB20EH;AkBz1ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClB20EH;AkBv0ED;EC/RE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBymFD;AmBvmFC;EACE,aAAA;EACA,kBAAA;CnBymFH;AmBtmFC;;EAEE,aAAA;CnBwmFH;AkBn1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBo1EH;AkB11ED;EASI,aAAA;EACA,kBAAA;ClBo1EH;AkB91ED;;EAcI,aAAA;ClBo1EH;AkBl2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBo1EH;AkB30ED;EAEE,mBAAA;ClB40ED;AkB90ED;EAMI,sBAAA;ClB20EH;AkBv0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClBy0ED;AkBv0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBy0ED;AkBv0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBy0ED;AkBr0ED;;;;;;;;;;EC1ZI,eAAA;CnB2uFH;AkBj1ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CL4rFT;AmB1uFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CLisFT;AkB31ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnB0uFH;AkBh2ED;ECtYI,eAAA;CnByuFH;AkBh2ED;;;;;;;;;;EC7ZI,eAAA;CnBywFH;AkB52ED;ECzZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CL0tFT;AmBxwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL+tFT;AkBt3ED;EC/YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBwwFH;AkB33ED;ECzYI,eAAA;CnBuwFH;AkB33ED;;;;;;;;;;EChaI,eAAA;CnBuyFH;AkBv4ED;EC5ZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwvFT;AmBtyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6vFT;AkBj5ED;EClZI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBsyFH;AkBt5ED;EC5YI,eAAA;CnBqyFH;AkBl5EC;EACE,UAAA;ClBo5EH;AkBl5EC;EACE,OAAA;ClBo5EH;AkB14ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB44ED;AkBzzED;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB23EH;EkBvvEH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBy3EH;EkB5vEH;IAxHM,sBAAA;GlBu3EH;EkB/vEH;IApHM,sBAAA;IACA,uBAAA;GlBs3EH;EkBnwEH;;;IA9GQ,YAAA;GlBs3EL;EkBxwEH;IAxGM,YAAA;GlBm3EH;EkB3wEH;IApGM,iBAAA;IACA,uBAAA;GlBk3EH;EkB/wEH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB+2EH;EkBtxEH;;IAtFQ,gBAAA;GlBg3EL;EkB1xEH;;IAjFM,mBAAA;IACA,eAAA;GlB+2EH;EkB/xEH;IA3EM,OAAA;GlB62EH;CACF;AkBn2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClBg2EH;AkB32ED;;EAiBI,iBAAA;ClB81EH;AkB/2ED;EJthBE,mBAAA;EACA,oBAAA;Cdw4FD;AkB50EC;EAyBF;IAnCM,kBAAA;IACA,iBAAA;IACA,iBAAA;GlB01EH;CACF;AkB13ED;EAwCI,YAAA;ClBq1EH;AkBv0EC;EAUF;IAdQ,kBAAA;IACA,gBAAA;GlB+0EL;CACF;AkBr0EC;EAEF;IANQ,iBAAA;IACA,gBAAA;GlB60EL;CACF;AoBt6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC0CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB+JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CLiuFT;AoBz6FG;;;;;;EdrBF,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNq8FD;AoB76FC;;;EAGE,YAAA;EACA,sBAAA;CpB+6FH;AoB56FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLo5FT;AoB56FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CL65FT;AoB56FG;;EAEE,qBAAA;CpB86FL;AoBr6FD;EC3DE,YAAA;EACA,uBAAA;EACA,mBAAA;CrBm+FD;AqBj+FC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBm+FP;AqBj+FC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBm+FP;AqBj+FC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBm+FP;AqBj+FG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBy+FT;AqBt+FC;;;EAGE,uBAAA;CrBw+FH;AqBn+FG;;;;;;;;;EAGE,uBAAA;EACI,mBAAA;CrB2+FT;AoB19FD;ECZI,YAAA;EACA,uBAAA;CrBy+FH;AoB39FD;EC9DE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB4hGD;AqB1hGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB4hGP;AqB1hGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB4hGP;AqB1hGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB4hGP;AqB1hGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBkiGT;AqB/hGC;;;EAGE,uBAAA;CrBiiGH;AqB5hGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBoiGT;AoBhhGD;ECfI,eAAA;EACA,uBAAA;CrBkiGH;AoBhhGD;EClEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBqlGD;AqBnlGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBqlGP;AqBnlGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBqlGP;AqBnlGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBqlGP;AqBnlGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2lGT;AqBxlGC;;;EAGE,uBAAA;CrB0lGH;AqBrlGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB6lGT;AoBrkGD;ECnBI,eAAA;EACA,uBAAA;CrB2lGH;AoBrkGD;ECtEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB8oGD;AqB5oGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB8oGP;AqB5oGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB8oGP;AqB5oGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB8oGP;AqB5oGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBopGT;AqBjpGC;;;EAGE,uBAAA;CrBmpGH;AqB9oGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBspGT;AoB1nGD;ECvBI,eAAA;EACA,uBAAA;CrBopGH;AoB1nGD;EC1EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBusGD;AqBrsGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBusGP;AqBrsGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBusGP;AqBrsGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBusGP;AqBrsGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6sGT;AqB1sGC;;;EAGE,uBAAA;CrB4sGH;AqBvsGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB+sGT;AoB/qGD;EC3BI,eAAA;EACA,uBAAA;CrB6sGH;AoB/qGD;EC9EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBgwGD;AqB9vGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBgwGP;AqB9vGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBgwGP;AqB9vGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBgwGP;AqB9vGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBswGT;AqBnwGC;;;EAGE,uBAAA;CrBqwGH;AqBhwGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBwwGT;AoBpuGD;EC/BI,eAAA;EACA,uBAAA;CrBswGH;AoB/tGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpBiuGD;AoB/tGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLqwGT;AoBhuGC;;;;EAIE,0BAAA;CpBkuGH;AoBhuGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpBkuGH;AoB9tGG;;;;EAEE,eAAA;EACA,sBAAA;CpBkuGL;AoBztGD;;ECxEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBqyGD;AoB5tGD;;EC5EE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrB4yGD;AoB/tGD;;EChFE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBmzGD;AoB9tGD;EACE,eAAA;EACA,YAAA;CpBguGD;AoB5tGD;EACE,gBAAA;CpB8tGD;AoBvtGC;;;EACE,YAAA;CpB2tGH;AuBr3GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLosGT;AuBx3GC;EACE,WAAA;CvB03GH;AuBt3GD;EACE,cAAA;CvBw3GD;AuBt3GC;EAAY,eAAA;CvBy3Gb;AuBx3GC;EAAY,mBAAA;CvB23Gb;AuB13GC;EAAY,yBAAA;CvB63Gb;AuB13GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CL8sGT;AwBx5GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxB05GD;AwBt5GD;;EAEE,mBAAA;CxBw5GD;AwBp5GD;EACE,WAAA;CxBs5GD;AwBl5GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBq5GD;AwBh5GC;EACE,SAAA;EACA,WAAA;CxBk5GH;AwB36GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBu8GD;AwBj7GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBi5GH;AwB34GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB64GH;AwBv4GC;;;EAGE,YAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxBy4GH;AwBh4GC;;;EAGE,eAAA;CxBk4GH;AwB93GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxBg4GH;AwB33GD;EAGI,eAAA;CxB23GH;AwB93GD;EAQI,WAAA;CxBy3GH;AwBj3GD;EACE,WAAA;EACA,SAAA;CxBm3GD;AwB32GD;EACE,QAAA;EACA,YAAA;CxB62GD;AwBz2GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB22GD;AwBv2GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxBy2GD;AwBr2GD;EACE,SAAA;EACA,WAAA;CxBu2GD;AwB/1GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxB+1GH;AwBt2GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxB+1GH;AwB10GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB65GC;EwB11GD;IA1DA,QAAA;IACA,YAAA;GxBu5GC;CACF;A2BviHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3ByiHD;A2B7iHD;;EAMI,mBAAA;EACA,YAAA;C3B2iHH;A2BziHG;;;;;;;;EAIE,WAAA;C3B+iHL;A2BziHD;;;;EAKI,kBAAA;C3B0iHH;A2BriHD;EACE,kBAAA;C3BuiHD;A2BxiHD;;;EAOI,YAAA;C3BsiHH;A2B7iHD;;;EAYI,iBAAA;C3BsiHH;A2BliHD;EACE,iBAAA;C3BoiHD;A2BhiHD;EACE,eAAA;C3BkiHD;A2BjiHC;EClDA,8BAAA;EACG,2BAAA;C5BslHJ;A2BhiHD;;EC/CE,6BAAA;EACG,0BAAA;C5BmlHJ;A2B/hHD;EACE,YAAA;C3BiiHD;A2B/hHD;EACE,iBAAA;C3BiiHD;A2B/hHD;;ECnEE,8BAAA;EACG,2BAAA;C5BsmHJ;A2B9hHD;ECjEE,6BAAA;EACG,0BAAA;C5BkmHJ;A2B7hHD;;EAEE,WAAA;C3B+hHD;A2B9gHD;EACE,kBAAA;EACA,mBAAA;C3BghHD;A2B9gHD;EACE,mBAAA;EACA,oBAAA;C3BghHD;A2B3gHD;EtB/CE,yDAAA;EACQ,iDAAA;CL6jHT;A2B3gHC;EtBnDA,yBAAA;EACQ,iBAAA;CLikHT;A2BxgHD;EACE,eAAA;C3B0gHD;A2BvgHD;EACE,wBAAA;EACA,uBAAA;C3BygHD;A2BtgHD;EACE,wBAAA;C3BwgHD;A2BjgHD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3BkgHH;A2BzgHD;EAcM,YAAA;C3B8/GL;A2B5gHD;;;;EAsBI,iBAAA;EACA,eAAA;C3B4/GH;A2Bv/GC;EACE,iBAAA;C3By/GH;A2Bv/GC;EC3KA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5B+pHF;A2Bz/GC;EC/KA,2BAAA;EACC,0BAAA;EAOD,gCAAA;EACC,+BAAA;C5BqqHF;A2B1/GD;EACE,iBAAA;C3B4/GD;A2B1/GD;;EC/KE,8BAAA;EACC,6BAAA;C5B6qHF;A2Bz/GD;EC7LE,2BAAA;EACC,0BAAA;C5ByrHF;A2Br/GD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3Bu/GD;A2B3/GD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3Bw/GH;A2BjgHD;EAYI,YAAA;C3Bw/GH;A2BpgHD;EAgBI,WAAA;C3Bu/GH;A2Bt+GD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3Bu+GL;A6BjtHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BmtHD;A6BhtHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7BktHH;A6B3tHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7B0sHH;A6BxsHG;EACE,WAAA;C7B0sHL;A6BhsHD;;;EV0BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnB2qHD;AmBzqHC;;;EACE,aAAA;EACA,kBAAA;CnB6qHH;AmB1qHC;;;;;;EAEE,aAAA;CnBgrHH;A6BltHD;;;EVqBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBksHD;AmBhsHC;;;EACE,aAAA;EACA,kBAAA;CnBosHH;AmBjsHC;;;;;;EAEE,aAAA;CnBusHH;A6BhuHD;;;EAGE,oBAAA;C7BkuHD;A6BhuHC;;;EACE,iBAAA;C7BouHH;A6BhuHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7BkuHD;A6B7tHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;C7B+tHD;A6B5tHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B8tHH;A6B5tHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B8tHH;A6BlvHD;;EA0BI,cAAA;C7B4tHH;A6BvtHD;;;;;;;EDpGE,8BAAA;EACG,2BAAA;C5Bo0HJ;A6BxtHD;EACE,gBAAA;C7B0tHD;A6BxtHD;;;;;;;EDxGE,6BAAA;EACG,0BAAA;C5By0HJ;A6BztHD;EACE,eAAA;C7B2tHD;A6BttHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BstHD;A6B3tHD;EAUI,mBAAA;C7BotHH;A6B9tHD;EAYM,kBAAA;C7BqtHL;A6BltHG;;;EAGE,WAAA;C7BotHL;A6B/sHC;;EAGI,mBAAA;C7BgtHL;A6B7sHC;;EAGI,WAAA;EACA,kBAAA;C7B8sHL;A8B72HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B+2HD;A8Bl3HD;EAOI,mBAAA;EACA,eAAA;C9B82HH;A8Bt3HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B82HL;A8B72HK;;EAEE,sBAAA;EACA,0BAAA;C9B+2HP;A8B12HG;EACE,eAAA;C9B42HL;A8B12HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9B42HP;A8Br2HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bu2HL;A8Bh5HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBs5HD;A8Bt5HD;EA0DI,gBAAA;C9B+1HH;A8Bt1HD;EACE,8BAAA;C9Bw1HD;A8Bz1HD;EAGI,YAAA;EAEA,oBAAA;C9Bw1HH;A8B71HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bu1HL;A8Bt1HK;EACE,mCAAA;C9Bw1HP;A8Bl1HK;;;EAGE,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,iCAAA;EACA,gBAAA;C9Bo1HP;A8B/0HC;EAqDA,YAAA;EA8BA,iBAAA;C9BgwHD;A8Bn1HC;EAwDE,YAAA;C9B8xHH;A8Bt1HC;EA0DI,mBAAA;EACA,mBAAA;C9B+xHL;A8B11HC;EAgEE,UAAA;EACA,WAAA;C9B6xHH;A8BjxHD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9B4xHH;E8B5tHH;IA9DQ,iBAAA;G9B6xHL;CACF;A8Bv2HC;EAuFE,gBAAA;EACA,mBAAA;C9BmxHH;A8B32HC;;;EA8FE,uBAAA;C9BkxHH;A8BpwHD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9BixHH;E8B9uHH;;;IA9BM,0BAAA;G9BixHH;CACF;A8Bl3HD;EAEI,YAAA;C9Bm3HH;A8Br3HD;EAMM,mBAAA;C9Bk3HL;A8Bx3HD;EASM,iBAAA;C9Bk3HL;A8B72HK;;;EAGE,YAAA;EACA,0BAAA;C9B+2HP;A8Bv2HD;EAEI,YAAA;C9Bw2HH;A8B12HD;EAIM,gBAAA;EACA,eAAA;C9By2HL;A8B71HD;EACE,YAAA;C9B+1HD;A8Bh2HD;EAII,YAAA;C9B+1HH;A8Bn2HD;EAMM,mBAAA;EACA,mBAAA;C9Bg2HL;A8Bv2HD;EAYI,UAAA;EACA,WAAA;C9B81HH;A8Bl1HD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9B61HH;E8B7xHH;IA9DQ,iBAAA;G9B81HL;CACF;A8Bt1HD;EACE,iBAAA;C9Bw1HD;A8Bz1HD;EAKI,gBAAA;EACA,mBAAA;C9Bu1HH;A8B71HD;;;EAYI,uBAAA;C9Bs1HH;A8Bx0HD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9Bq1HH;E8BlzHH;;;IA9BM,0BAAA;G9Bq1HH;CACF;A8B50HD;EAEI,cAAA;C9B60HH;A8B/0HD;EAKI,eAAA;C9B60HH;A8Bp0HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5BijIF;A+B3iID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B6iID;A+BriID;EA8nBA;IAhoBI,mBAAA;G/B2iID;CACF;A+B5hID;EAgnBA;IAlnBI,YAAA;G/BkiID;CACF;A+BphID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BqhID;A+BnhIC;EACE,iBAAA;C/BqhIH;A+Bz/HD;EA6jBA;IArlBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BqhID;E+BnhIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BqhIH;E+BlhIC;IACE,oBAAA;G/BohIH;E+B/gIC;;;IAGE,gBAAA;IACA,iBAAA;G/BihIH;CACF;A+B7gID;;EAGI,kBAAA;C/B8gIH;A+BzgIC;EAmjBF;;IArjBM,kBAAA;G/BghIH;CACF;A+BvgID;;;;EAII,oBAAA;EACA,mBAAA;C/BygIH;A+BngIC;EAgiBF;;;;IAniBM,gBAAA;IACA,eAAA;G/B6gIH;CACF;A+BjgID;EACE,cAAA;EACA,sBAAA;C/BmgID;A+B9/HD;EA8gBA;IAhhBI,iBAAA;G/BogID;CACF;A+BhgID;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/BkgID;A+B5/HD;EAggBA;;IAlgBI,iBAAA;G/BmgID;CACF;A+BjgID;EACE,OAAA;EACA,sBAAA;C/BmgID;A+BjgID;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BmgID;A+B7/HD;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B+/HD;A+B7/HC;;EAEE,sBAAA;C/B+/HH;A+BxgID;EAaI,eAAA;C/B8/HH;A+Br/HD;EALI;;IAEE,mBAAA;G/B6/HH;CACF;A+Bn/HD;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/Bs/HD;A+Bl/HC;EACE,WAAA;C/Bo/HH;A+BlgID;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/Bk/HH;A+BxgID;EAyBI,gBAAA;C/Bk/HH;A+B5+HD;EAqbA;IAvbI,cAAA;G/Bk/HD;CACF;A+Bz+HD;EACE,oBAAA;C/B2+HD;A+B5+HD;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/B2+HH;A+B/8HC;EA2YF;IAjaM,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/By+HH;E+B9kHH;;IAxZQ,2BAAA;G/B0+HL;E+BllHH;IArZQ,kBAAA;G/B0+HL;E+Bz+HK;;IAEE,uBAAA;G/B2+HP;CACF;A+Bz9HD;EA+XA;IA1YI,YAAA;IACA,UAAA;G/Bw+HD;E+B/lHH;IAtYM,YAAA;G/Bw+HH;E+BlmHH;IApYQ,kBAAA;IACA,qBAAA;G/By+HL;CACF;A+B99HD;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC+vID;AkBzuHD;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB2yHH;EkBvqHH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlByyHH;EkB5qHH;IAxHM,sBAAA;GlBuyHH;EkB/qHH;IApHM,sBAAA;IACA,uBAAA;GlBsyHH;EkBnrHH;;;IA9GQ,YAAA;GlBsyHL;EkBxrHH;IAxGM,YAAA;GlBmyHH;EkB3rHH;IApGM,iBAAA;IACA,uBAAA;GlBkyHH;EkB/rHH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB+xHH;EkBtsHH;;IAtFQ,gBAAA;GlBgyHL;EkB1sHH;;IAjFM,mBAAA;IACA,eAAA;GlB+xHH;EkB/sHH;IA3EM,OAAA;GlB6xHH;CACF;A+BvgIC;EAmWF;IAzWM,mBAAA;G/BihIH;E+B/gIG;IACE,iBAAA;G/BihIL;CACF;A+BhgID;EAoVA;IA5VI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLswIP;CACF;A+BtgID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B60IF;A+BtgID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5B40IF;A+BlgID;EChVE,gBAAA;EACA,mBAAA;ChCq1ID;A+BngIC;ECnVA,iBAAA;EACA,oBAAA;ChCy1ID;A+BpgIC;ECtVA,iBAAA;EACA,oBAAA;ChC61ID;A+B9/HD;EChWE,iBAAA;EACA,oBAAA;ChCi2ID;A+B1/HD;EAsSA;IA1SI,YAAA;IACA,kBAAA;IACA,mBAAA;G/BkgID;CACF;A+Br+HD;EAhBE;IExWA,uBAAA;GjCi2IC;E+Bx/HD;IE5WA,wBAAA;IF8WE,oBAAA;G/B0/HD;E+B5/HD;IAKI,gBAAA;G/B0/HH;CACF;A+Bj/HD;EACE,0BAAA;EACA,sBAAA;C/Bm/HD;A+Br/HD;EAKI,YAAA;C/Bm/HH;A+Bl/HG;;EAEE,eAAA;EACA,8BAAA;C/Bo/HL;A+B7/HD;EAcI,YAAA;C/Bk/HH;A+BhgID;EAmBM,YAAA;C/Bg/HL;A+B9+HK;;EAEE,YAAA;EACA,8BAAA;C/Bg/HP;A+B5+HK;;;EAGE,YAAA;EACA,0BAAA;C/B8+HP;A+B1+HK;;;EAGE,YAAA;EACA,8BAAA;C/B4+HP;A+BphID;EA8CI,mBAAA;C/By+HH;A+Bx+HG;;EAEE,uBAAA;C/B0+HL;A+B3hID;EAoDM,uBAAA;C/B0+HL;A+B9hID;;EA0DI,sBAAA;C/Bw+HH;A+Bj+HK;;;EAGE,0BAAA;EACA,YAAA;C/Bm+HP;A+Bl8HC;EAoKF;IA7LU,YAAA;G/B+9HP;E+B99HO;;IAEE,YAAA;IACA,8BAAA;G/Bg+HT;E+B59HO;;;IAGE,YAAA;IACA,0BAAA;G/B89HT;E+B19HO;;;IAGE,YAAA;IACA,8BAAA;G/B49HT;CACF;A+B9jID;EA8GI,YAAA;C/Bm9HH;A+Bl9HG;EACE,YAAA;C/Bo9HL;A+BpkID;EAqHI,YAAA;C/Bk9HH;A+Bj9HG;;EAEE,YAAA;C/Bm9HL;A+B/8HK;;;;EAEE,YAAA;C/Bm9HP;A+B38HD;EACE,uBAAA;EACA,sBAAA;C/B68HD;A+B/8HD;EAKI,eAAA;C/B68HH;A+B58HG;;EAEE,YAAA;EACA,8BAAA;C/B88HL;A+Bv9HD;EAcI,eAAA;C/B48HH;A+B19HD;EAmBM,eAAA;C/B08HL;A+Bx8HK;;EAEE,YAAA;EACA,8BAAA;C/B08HP;A+Bt8HK;;;EAGE,YAAA;EACA,0BAAA;C/Bw8HP;A+Bp8HK;;;EAGE,YAAA;EACA,8BAAA;C/Bs8HP;A+B9+HD;EA+CI,mBAAA;C/Bk8HH;A+Bj8HG;;EAEE,uBAAA;C/Bm8HL;A+Br/HD;EAqDM,uBAAA;C/Bm8HL;A+Bx/HD;;EA2DI,sBAAA;C/Bi8HH;A+B37HK;;;EAGE,0BAAA;EACA,YAAA;C/B67HP;A+Bt5HC;EAwBF;IAvDU,sBAAA;G/By7HP;E+Bl4HH;IApDU,0BAAA;G/By7HP;E+Br4HH;IAjDU,eAAA;G/By7HP;E+Bx7HO;;IAEE,YAAA;IACA,8BAAA;G/B07HT;E+Bt7HO;;;IAGE,YAAA;IACA,0BAAA;G/Bw7HT;E+Bp7HO;;;IAGE,YAAA;IACA,8BAAA;G/Bs7HT;CACF;A+B9hID;EA+GI,eAAA;C/Bk7HH;A+Bj7HG;EACE,YAAA;C/Bm7HL;A+BpiID;EAsHI,eAAA;C/Bi7HH;A+Bh7HG;;EAEE,YAAA;C/Bk7HL;A+B96HK;;;;EAEE,YAAA;C/Bk7HP;AkC5jJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC8jJD;AkCnkJD;EAQI,sBAAA;ClC8jJH;AkCtkJD;EAWM,kBAAA;EACA,eAAA;EACA,YAAA;ClC8jJL;AkC3kJD;EAkBI,eAAA;ClC4jJH;AmChlJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnCklJD;AmCtlJD;EAOI,gBAAA;CnCklJH;AmCzlJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,kBAAA;CnCmlJL;AmCjlJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B8lJJ;AmChlJG;;EPvBF,gCAAA;EACG,6BAAA;C5B2mJJ;AmC3kJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CnC+kJL;AmCzkJG;;;;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC8kJL;AmCroJD;;;;;;EAkEM,eAAA;EACA,uBAAA;EACA,mBAAA;EACA,oBAAA;CnC2kJL;AmClkJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpCipJL;AoC/oJG;;ERKF,+BAAA;EACG,4BAAA;C5B8oJJ;AoC9oJG;;ERTF,gCAAA;EACG,6BAAA;C5B2pJJ;AmC7kJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpCiqJL;AoC/pJG;;ERKF,+BAAA;EACG,4BAAA;C5B8pJJ;AoC9pJG;;ERTF,gCAAA;EACG,6BAAA;C5B2qJJ;AqC9qJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrCgrJD;AqCprJD;EAOI,gBAAA;CrCgrJH;AqCvrJD;;EAUM,sBAAA;EACA,kBAAA;EACA,uBAAA;EACA,uBAAA;EACA,oBAAA;CrCirJL;AqC/rJD;;EAmBM,sBAAA;EACA,0BAAA;CrCgrJL;AqCpsJD;;EA2BM,aAAA;CrC6qJL;AqCxsJD;;EAkCM,YAAA;CrC0qJL;AqC5sJD;;;;EA2CM,eAAA;EACA,uBAAA;EACA,oBAAA;CrCuqJL;AsCrtJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCutJD;AsCntJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CtCqtJL;AsChtJC;EACE,cAAA;CtCktJH;AsC9sJC;EACE,mBAAA;EACA,UAAA;CtCgtJH;AsCzsJD;ECtCE,0BAAA;CvCkvJD;AuC/uJG;;EAEE,0BAAA;CvCivJL;AsC5sJD;EC1CE,0BAAA;CvCyvJD;AuCtvJG;;EAEE,0BAAA;CvCwvJL;AsC/sJD;EC9CE,0BAAA;CvCgwJD;AuC7vJG;;EAEE,0BAAA;CvC+vJL;AsCltJD;EClDE,0BAAA;CvCuwJD;AuCpwJG;;EAEE,0BAAA;CvCswJL;AsCrtJD;ECtDE,0BAAA;CvC8wJD;AuC3wJG;;EAEE,0BAAA;CvC6wJL;AsCxtJD;EC1DE,0BAAA;CvCqxJD;AuClxJG;;EAEE,0BAAA;CvCoxJL;AwCtxJD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCwxJD;AwCrxJC;EACE,cAAA;CxCuxJH;AwCnxJC;EACE,mBAAA;EACA,UAAA;CxCqxJH;AwClxJC;;EAEE,OAAA;EACA,iBAAA;CxCoxJH;AwC/wJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CxCixJL;AwC5wJC;;EAEE,eAAA;EACA,uBAAA;CxC8wJH;AwC3wJC;EACE,aAAA;CxC6wJH;AwC1wJC;EACE,kBAAA;CxC4wJH;AwCzwJC;EACE,iBAAA;CxC2wJH;AyCr0JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCu0JD;AyC50JD;;EASI,eAAA;CzCu0JH;AyCh1JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCs0JH;AyCr1JD;EAmBI,0BAAA;CzCq0JH;AyCl0JC;;EAEE,mBAAA;EACA,mBAAA;EACA,oBAAA;CzCo0JH;AyC91JD;EA8BI,gBAAA;CzCm0JH;AyCjzJD;EACA;IAfI,kBAAA;IACA,qBAAA;GzCm0JD;EyCj0JC;;IAEE,mBAAA;IACA,oBAAA;GzCm0JH;EyC1zJH;;IAJM,gBAAA;GzCk0JH;CACF;A0C/2JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CLisJT;A0C33JD;;EAaI,kBAAA;EACA,mBAAA;C1Ck3JH;A0C92JC;;;EAGE,sBAAA;C1Cg3JH;A0Cr4JD;EA0BI,aAAA;EACA,eAAA;C1C82JH;A2Cv4JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Cy4JD;A2C74JD;EAQI,cAAA;EAEA,eAAA;C3Cu4JH;A2Cj5JD;EAeI,kBAAA;C3Cq4JH;A2Cp5JD;;EAqBI,iBAAA;C3Cm4JH;A2Cx5JD;EAyBI,gBAAA;C3Ck4JH;A2C13JD;;EAEE,oBAAA;C3C43JD;A2C93JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3C43JH;A2Cp3JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C86JD;A2Cz3JD;EClDI,0BAAA;C5C86JH;A2C53JD;EC/CI,eAAA;C5C86JH;A2C33JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cy7JD;A2Ch4JD;ECtDI,0BAAA;C5Cy7JH;A2Cn4JD;ECnDI,eAAA;C5Cy7JH;A2Cl4JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Co8JD;A2Cv4JD;EC1DI,0BAAA;C5Co8JH;A2C14JD;ECvDI,eAAA;C5Co8JH;A2Cz4JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C+8JD;A2C94JD;EC9DI,0BAAA;C5C+8JH;A2Cj5JD;EC3DI,eAAA;C5C+8JH;A6Cj9JD;EACE;IAAQ,4BAAA;G7Co9JP;E6Cn9JD;IAAQ,yBAAA;G7Cs9JP;CACF;A6Cn9JD;EACE;IAAQ,4BAAA;G7Cs9JP;E6Cr9JD;IAAQ,yBAAA;G7Cw9JP;CACF;A6C39JD;EACE;IAAQ,4BAAA;G7Cs9JP;E6Cr9JD;IAAQ,yBAAA;G7Cw9JP;CACF;A6Cj9JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CL86JT;A6Ch9JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CLk0JT;A6C78JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7Ci9JD;A6C18JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CL0/JT;A6Cv8JD;EErEE,0BAAA;C/C+gKD;A+C5gKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C+9JH;A6C38JD;EEzEE,0BAAA;C/CuhKD;A+CphKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Cu+JH;A6C/8JD;EE7EE,0BAAA;C/C+hKD;A+C5hKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C++JH;A6Cn9JD;EEjFE,0BAAA;C/CuiKD;A+CpiKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Cu/JH;AgD/iKD;EAEE,iBAAA;ChDgjKD;AgD9iKC;EACE,cAAA;ChDgjKH;AgD5iKD;;EAEE,QAAA;EACA,iBAAA;ChD8iKD;AgD3iKD;EACE,eAAA;ChD6iKD;AgD1iKD;EACE,eAAA;ChD4iKD;AgDziKC;EACE,gBAAA;ChD2iKH;AgDviKD;;EAEE,mBAAA;ChDyiKD;AgDtiKD;;EAEE,oBAAA;ChDwiKD;AgDriKD;;;EAGE,oBAAA;EACA,oBAAA;ChDuiKD;AgDpiKD;EACE,uBAAA;ChDsiKD;AgDniKD;EACE,uBAAA;ChDqiKD;AgDjiKD;EACE,cAAA;EACA,mBAAA;ChDmiKD;AgD7hKD;EACE,gBAAA;EACA,iBAAA;ChD+hKD;AiDtlKD;EAEE,oBAAA;EACA,gBAAA;CjDulKD;AiD/kKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,uBAAA;EACA,uBAAA;CjDglKD;AiD7kKC;ErB3BA,6BAAA;EACC,4BAAA;C5B2mKF;AiD9kKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BwmKF;AiDvkKD;;EAEE,YAAA;CjDykKD;AiD3kKD;;EAKI,YAAA;CjD0kKH;AiDtkKC;;;;EAEE,sBAAA;EACA,YAAA;EACA,0BAAA;CjD0kKH;AiDtkKD;EACE,YAAA;EACA,iBAAA;CjDwkKD;AiDnkKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDqkKH;AiD1kKC;;;EASI,eAAA;CjDskKL;AiD/kKC;;;EAYI,eAAA;CjDwkKL;AiDnkKC;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;CjDqkKH;AiD3kKC;;;;;;;;;EAYI,eAAA;CjD0kKL;AiDtlKC;;;EAeI,eAAA;CjD4kKL;AkD9qKC;EACE,eAAA;EACA,0BAAA;ClDgrKH;AkD9qKG;;EAEE,eAAA;ClDgrKL;AkDlrKG;;EAKI,eAAA;ClDirKP;AkD9qKK;;;;EAEE,eAAA;EACA,0BAAA;ClDkrKP;AkDhrKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDqrKP;AkD3sKC;EACE,eAAA;EACA,0BAAA;ClD6sKH;AkD3sKG;;EAEE,eAAA;ClD6sKL;AkD/sKG;;EAKI,eAAA;ClD8sKP;AkD3sKK;;;;EAEE,eAAA;EACA,0BAAA;ClD+sKP;AkD7sKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDktKP;AkDxuKC;EACE,eAAA;EACA,0BAAA;ClD0uKH;AkDxuKG;;EAEE,eAAA;ClD0uKL;AkD5uKG;;EAKI,eAAA;ClD2uKP;AkDxuKK;;;;EAEE,eAAA;EACA,0BAAA;ClD4uKP;AkD1uKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD+uKP;AkDrwKC;EACE,eAAA;EACA,0BAAA;ClDuwKH;AkDrwKG;;EAEE,eAAA;ClDuwKL;AkDzwKG;;EAKI,eAAA;ClDwwKP;AkDrwKK;;;;EAEE,eAAA;EACA,0BAAA;ClDywKP;AkDvwKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD4wKP;AiD3qKD;EACE,cAAA;EACA,mBAAA;CjD6qKD;AiD3qKD;EACE,iBAAA;EACA,iBAAA;CjD6qKD;AmDvyKD;EACE,oBAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CLgvKT;AmDtyKD;EACE,cAAA;CnDwyKD;AmDnyKD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5B0zKF;AmDzyKD;EAMI,eAAA;CnDsyKH;AmDjyKD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnDmyKD;AmDvyKD;;;;;EAWI,eAAA;CnDmyKH;AmD9xKD;EACE,mBAAA;EACA,0BAAA;EACA,2BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5By0KF;AmDxxKD;;EAGI,iBAAA;CnDyxKH;AmD5xKD;;EAMM,oBAAA;EACA,iBAAA;CnD0xKL;AmDtxKG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5Bg2KF;AmDpxKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5B81KF;AmD7yKD;EvB1DE,2BAAA;EACC,0BAAA;C5B02KF;AmDhxKD;EAEI,oBAAA;CnDixKH;AmD9wKD;EACE,oBAAA;CnDgxKD;AmDxwKD;;;EAII,iBAAA;CnDywKH;AmD7wKD;;;EAOM,mBAAA;EACA,oBAAA;CnD2wKL;AmDnxKD;;EvBzGE,6BAAA;EACC,4BAAA;C5Bg4KF;AmDxxKD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnD2wKP;AmD/xKD;;;;;;;;EAwBU,4BAAA;CnDixKT;AmDzyKD;;;;;;;;EA4BU,6BAAA;CnDuxKT;AmDnzKD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bw5KF;AmDxzKD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDqxKP;AmD/zKD;;;;;;;;EA8CU,+BAAA;CnD2xKT;AmDz0KD;;;;;;;;EAkDU,gCAAA;CnDiyKT;AmDn1KD;;;;EA2DI,2BAAA;CnD8xKH;AmDz1KD;;EA+DI,cAAA;CnD8xKH;AmD71KD;;EAmEI,UAAA;CnD8xKH;AmDj2KD;;;;;;;;;;;;EA0EU,eAAA;CnDqyKT;AmD/2KD;;;;;;;;;;;;EA8EU,gBAAA;CnD+yKT;AmD73KD;;;;;;;;EAuFU,iBAAA;CnDgzKT;AmDv4KD;;;;;;;;EAgGU,iBAAA;CnDizKT;AmDj5KD;EAsGI,UAAA;EACA,iBAAA;CnD8yKH;AmDpyKD;EACE,oBAAA;CnDsyKD;AmDvyKD;EAKI,iBAAA;EACA,mBAAA;CnDqyKH;AmD3yKD;EASM,gBAAA;CnDqyKL;AmD9yKD;EAcI,iBAAA;CnDmyKH;AmDjzKD;;EAkBM,2BAAA;CnDmyKL;AmDrzKD;EAuBI,cAAA;CnDiyKH;AmDxzKD;EAyBM,8BAAA;CnDkyKL;AmD3xKD;EC1PE,mBAAA;CpDwhLD;AoDthLC;EACE,eAAA;EACA,0BAAA;EACA,mBAAA;CpDwhLH;AoD3hLC;EAMI,uBAAA;CpDwhLL;AoD9hLC;EASI,eAAA;EACA,0BAAA;CpDwhLL;AoDrhLC;EAEI,0BAAA;CpDshLL;AmD1yKD;EC7PE,sBAAA;CpD0iLD;AoDxiLC;EACE,YAAA;EACA,0BAAA;EACA,sBAAA;CpD0iLH;AoD7iLC;EAMI,0BAAA;CpD0iLL;AoDhjLC;EASI,eAAA;EACA,uBAAA;CpD0iLL;AoDviLC;EAEI,6BAAA;CpDwiLL;AmDzzKD;EChQE,sBAAA;CpD4jLD;AoD1jLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD4jLH;AoD/jLC;EAMI,0BAAA;CpD4jLL;AoDlkLC;EASI,eAAA;EACA,0BAAA;CpD4jLL;AoDzjLC;EAEI,6BAAA;CpD0jLL;AmDx0KD;ECnQE,sBAAA;CpD8kLD;AoD5kLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD8kLH;AoDjlLC;EAMI,0BAAA;CpD8kLL;AoDplLC;EASI,eAAA;EACA,0BAAA;CpD8kLL;AoD3kLC;EAEI,6BAAA;CpD4kLL;AmDv1KD;ECtQE,sBAAA;CpDgmLD;AoD9lLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDgmLH;AoDnmLC;EAMI,0BAAA;CpDgmLL;AoDtmLC;EASI,eAAA;EACA,0BAAA;CpDgmLL;AoD7lLC;EAEI,6BAAA;CpD8lLL;AmDt2KD;ECzQE,sBAAA;CpDknLD;AoDhnLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDknLH;AoDrnLC;EAMI,0BAAA;CpDknLL;AoDxnLC;EASI,eAAA;EACA,0BAAA;CpDknLL;AoD/mLC;EAEI,6BAAA;CpDgnLL;AqDhoLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrDkoLD;AqDvoLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrDkoLH;AqD7nLD;EACE,uBAAA;CrD+nLD;AqD3nLD;EACE,oBAAA;CrD6nLD;AsDxpLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CLmmLT;AsDlqLD;EASI,mBAAA;EACA,kCAAA;CtD4pLH;AsDvpLD;EACE,cAAA;EACA,mBAAA;CtDypLD;AsDvpLD;EACE,aAAA;EACA,mBAAA;CtDypLD;AuD/qLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,0BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBwrLD;AuDhrLC;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtBgsLD;AuD5qLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvD8qLH;AwDnsLD;EACE,iBAAA;CxDqsLD;AwDjsLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxDgsLD;AwD7rLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CLghLT;AwDnsLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CL2lLT;AwDvsLD;EACE,mBAAA;EACA,iBAAA;CxDysLD;AwDrsLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDusLD;AwDnsLD;EACE,mBAAA;EACA,uBAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDqsLD;AwDjsLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,uBAAA;CxDmsLD;AwDjsLC;ElCrEA,WAAA;EAGA,yBAAA;CtBuwLD;AwDpsLC;ElCtEA,aAAA;EAGA,0BAAA;CtB2wLD;AwDnsLD;EACE,cAAA;EACA,iCAAA;CxDqsLD;AwDjsLD;EACE,iBAAA;CxDmsLD;AwD/rLD;EACE,UAAA;EACA,wBAAA;CxDisLD;AwD5rLD;EACE,mBAAA;EACA,cAAA;CxD8rLD;AwD1rLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxD4rLD;AwD/rLD;EAQI,iBAAA;EACA,iBAAA;CxD0rLH;AwDnsLD;EAaI,kBAAA;CxDyrLH;AwDtsLD;EAiBI,eAAA;CxDwrLH;AwDnrLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDqrLD;AwDnqLD;EAZE;IACE,aAAA;IACA,kBAAA;GxDkrLD;EwDhrLD;InDvEA,kDAAA;IACQ,0CAAA;GL0vLP;EwD/qLD;IAAY,aAAA;GxDkrLX;CACF;AwD7qLD;EAFE;IAAY,aAAA;GxDmrLX;CACF;AyDl0LD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBy1LD;AyD90LC;EnCdA,aAAA;EAGA,0BAAA;CtB61LD;AyDj1LC;EAAW,iBAAA;EAAmB,eAAA;CzDq1L/B;AyDp1LC;EAAW,iBAAA;EAAmB,eAAA;CzDw1L/B;AyDv1LC;EAAW,gBAAA;EAAmB,eAAA;CzD21L/B;AyD11LC;EAAW,kBAAA;EAAmB,eAAA;CzD81L/B;AyD11LD;EACE,iBAAA;EACA,iBAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,mBAAA;CzD41LD;AyDx1LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzD01LD;AyDt1LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,uBAAA;CzDw1LH;AyDt1LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDw1LH;AyDt1LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDw1LH;AyDt1LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,yBAAA;CzDw1LH;AyDt1LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,wBAAA;CzDw1LH;AyDt1LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDw1LH;AyDt1LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDw1LH;AyDt1LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDw1LH;A2Dr7LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,uBAAA;EACA,qCAAA;UAAA,6BAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLq5LT;A2Dh8LC;EAAY,kBAAA;C3Dm8Lb;A2Dl8LC;EAAY,kBAAA;C3Dq8Lb;A2Dp8LC;EAAY,iBAAA;C3Du8Lb;A2Dt8LC;EAAY,mBAAA;C3Dy8Lb;A2Dt8LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Dw8LD;A2Dr8LD;EACE,kBAAA;C3Du8LD;A2D/7LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3Di8LH;A2D97LD;EACE,mBAAA;C3Dg8LD;A2D97LD;EACE,mBAAA;EACA,YAAA;C3Dg8LD;A2D57LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D87LH;A2D77LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,uBAAA;C3D+7LL;A2D57LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D87LH;A2D77LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,yBAAA;C3D+7LL;A2D57LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D87LH;A2D77LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,0BAAA;C3D+7LL;A2D37LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3D67LH;A2D57LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,wBAAA;EACA,cAAA;C3D87LL;A4DvjMD;EACE,mBAAA;C5DyjMD;A4DtjMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DwjMD;A4D3jMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CL44LT;A4DlkMD;;EAcM,eAAA;C5DwjML;A4D9hMC;EA4NF;IvD3DE,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GLi7LP;E4D5jMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D+jML;E4D7jMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5DgkML;E4D9jMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5DikML;CACF;A4DvmMD;;;EA6CI,eAAA;C5D+jMH;A4D5mMD;EAiDI,QAAA;C5D8jMH;A4D/mMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5D6jMH;A4DrnMD;EA4DI,WAAA;C5D4jMH;A4DxnMD;EA+DI,YAAA;C5D4jMH;A4D3nMD;;EAmEI,QAAA;C5D4jMH;A4D/nMD;EAuEI,YAAA;C5D2jMH;A4DloMD;EA0EI,WAAA;C5D2jMH;A4DnjMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;EACA,mCAAA;C5DsjMD;A4DjjMC;EdnGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CupMH;A4DrjMC;EACE,WAAA;EACA,SAAA;EdxGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CgqMH;A4DvjMC;;EAEE,WAAA;EACA,YAAA;EACA,sBAAA;EtCvHF,aAAA;EAGA,0BAAA;CtB+qMD;A4DzlMD;;;;EAuCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DwjMH;A4DnmMD;;EA+CI,UAAA;EACA,mBAAA;C5DwjMH;A4DxmMD;;EAoDI,WAAA;EACA,oBAAA;C5DwjMH;A4D7mMD;;EAyDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DwjMH;A4DnjMG;EACE,iBAAA;C5DqjML;A4DjjMG;EACE,iBAAA;C5DmjML;A4DziMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5D2iMD;A4DpjMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5DiiMH;A4DhkMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;C5DiiMH;A4D1hMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;C5D4hMD;A4D3hMC;EACE,kBAAA;C5D6hMH;A4Dp/LD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DshMH;E4D9hMD;;IAYI,mBAAA;G5DshMH;E4DliMD;;IAgBI,oBAAA;G5DshMH;E4DjhMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5DmhMD;E4D/gMD;IACE,aAAA;G5DihMD;CACF;A6DhxMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7DgzMH;A6D9yMC;;;;;;;;;;;;;;;;EACE,YAAA;C7D+zMH;AiCv0MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9Dk1MD;AiCz0MD;EACE,wBAAA;CjC20MD;AiCz0MD;EACE,uBAAA;CjC20MD;AiCn0MD;EACE,yBAAA;CjCq0MD;AiCn0MD;EACE,0BAAA;CjCq0MD;AiCn0MD;EACE,mBAAA;CjCq0MD;AiCn0MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/D+1MD;AiCj0MD;EACE,yBAAA;CjCm0MD;AiC5zMD;EACE,gBAAA;CjC8zMD;AgE/1MD;EACE,oBAAA;ChEi2MD;AgE31MD;;;;ECdE,yBAAA;CjE+2MD;AgE11MD;;;;;;;;;;;;EAYE,yBAAA;ChE41MD;AgEr1MD;EA6IA;IC7LE,0BAAA;GjEy4MC;EiEx4MD;IAAU,0BAAA;GjE24MT;EiE14MD;IAAU,8BAAA;GjE64MT;EiE54MD;;IACU,+BAAA;GjE+4MT;CACF;AgE/1MD;EAwIA;IA1II,0BAAA;GhEq2MD;CACF;AgE/1MD;EAmIA;IArII,2BAAA;GhEq2MD;CACF;AgE/1MD;EA8HA;IAhII,iCAAA;GhEq2MD;CACF;AgE91MD;EAwHA;IC7LE,0BAAA;GjEu6MC;EiEt6MD;IAAU,0BAAA;GjEy6MT;EiEx6MD;IAAU,8BAAA;GjE26MT;EiE16MD;;IACU,+BAAA;GjE66MT;CACF;AgEx2MD;EAmHA;IArHI,0BAAA;GhE82MD;CACF;AgEx2MD;EA8GA;IAhHI,2BAAA;GhE82MD;CACF;AgEx2MD;EAyGA;IA3GI,iCAAA;GhE82MD;CACF;AgEv2MD;EAmGA;IC7LE,0BAAA;GjEq8MC;EiEp8MD;IAAU,0BAAA;GjEu8MT;EiEt8MD;IAAU,8BAAA;GjEy8MT;EiEx8MD;;IACU,+BAAA;GjE28MT;CACF;AgEj3MD;EA8FA;IAhGI,0BAAA;GhEu3MD;CACF;AgEj3MD;EAyFA;IA3FI,2BAAA;GhEu3MD;CACF;AgEj3MD;EAoFA;IAtFI,iCAAA;GhEu3MD;CACF;AgEh3MD;EA8EA;IC7LE,0BAAA;GjEm+MC;EiEl+MD;IAAU,0BAAA;GjEq+MT;EiEp+MD;IAAU,8BAAA;GjEu+MT;EiEt+MD;;IACU,+BAAA;GjEy+MT;CACF;AgE13MD;EAyEA;IA3EI,0BAAA;GhEg4MD;CACF;AgE13MD;EAoEA;IAtEI,2BAAA;GhEg4MD;CACF;AgE13MD;EA+DA;IAjEI,iCAAA;GhEg4MD;CACF;AgEz3MD;EAyDA;ICrLE,yBAAA;GjEy/MC;CACF;AgEz3MD;EAoDA;ICrLE,yBAAA;GjE8/MC;CACF;AgEz3MD;EA+CA;ICrLE,yBAAA;GjEmgNC;CACF;AgEz3MD;EA0CA;ICrLE,yBAAA;GjEwgNC;CACF;AgEt3MD;ECnJE,yBAAA;CjE4gND;AgEn3MD;EA4BA;IC7LE,0BAAA;GjEwhNC;EiEvhND;IAAU,0BAAA;GjE0hNT;EiEzhND;IAAU,8BAAA;GjE4hNT;EiE3hND;;IACU,+BAAA;GjE8hNT;CACF;AgEj4MD;EACE,yBAAA;ChEm4MD;AgE93MD;EAqBA;IAvBI,0BAAA;GhEo4MD;CACF;AgEl4MD;EACE,yBAAA;ChEo4MD;AgE/3MD;EAcA;IAhBI,2BAAA;GhEq4MD;CACF;AgEn4MD;EACE,yBAAA;ChEq4MD;AgEh4MD;EAOA;IATI,iCAAA;GhEs4MD;CACF;AgE/3MD;EACA;ICrLE,yBAAA;GjEujNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.6 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n  font-family: sans-serif;\n  -ms-text-size-adjust: 100%;\n  -webkit-text-size-adjust: 100%;\n}\nbody {\n  margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block;\n  vertical-align: baseline;\n}\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n[hidden],\ntemplate {\n  display: none;\n}\na {\n  background-color: transparent;\n}\na:active,\na:hover {\n  outline: 0;\n}\nabbr[title] {\n  border-bottom: 1px dotted;\n}\nb,\nstrong {\n  font-weight: bold;\n}\ndfn {\n  font-style: italic;\n}\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\nmark {\n  background: #ff0;\n  color: #000;\n}\nsmall {\n  font-size: 80%;\n}\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\nsup {\n  top: -0.5em;\n}\nsub {\n  bottom: -0.25em;\n}\nimg {\n  border: 0;\n}\nsvg:not(:root) {\n  overflow: hidden;\n}\nfigure {\n  margin: 1em 40px;\n}\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\npre {\n  overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit;\n  font: inherit;\n  margin: 0;\n}\nbutton {\n  overflow: visible;\n}\nbutton,\nselect {\n  text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button;\n  cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\ninput {\n  line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box;\n  padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-appearance: textfield;\n  box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n  border: 0;\n  padding: 0;\n}\ntextarea {\n  overflow: auto;\n}\noptgroup {\n  font-weight: bold;\n}\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\ntd,\nth {\n  padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n  *,\n  *:before,\n  *:after {\n    background: transparent !important;\n    color: #000 !important;\n    box-shadow: none !important;\n    text-shadow: none !important;\n  }\n  a,\n  a:visited {\n    text-decoration: underline;\n  }\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n  a[href^=\"#\"]:after,\n  a[href^=\"javascript:\"]:after {\n    content: \"\";\n  }\n  pre,\n  blockquote {\n    border: 1px solid #999;\n    page-break-inside: avoid;\n  }\n  thead {\n    display: table-header-group;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  img {\n    max-width: 100% !important;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  .navbar {\n    display: none;\n  }\n  .btn > .caret,\n  .dropup > .btn > .caret {\n    border-top-color: #000 !important;\n  }\n  .label {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #ddd !important;\n  }\n}\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('../fonts/glyphicons-halflings-regular.eot');\n  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n  content: \"\\002a\";\n}\n.glyphicon-plus:before {\n  content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n  content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n  content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n  content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n  content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n  content: \"\\270f\";\n}\n.glyphicon-glass:before {\n  content: \"\\e001\";\n}\n.glyphicon-music:before {\n  content: \"\\e002\";\n}\n.glyphicon-search:before {\n  content: \"\\e003\";\n}\n.glyphicon-heart:before {\n  content: \"\\e005\";\n}\n.glyphicon-star:before {\n  content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n  content: \"\\e007\";\n}\n.glyphicon-user:before {\n  content: \"\\e008\";\n}\n.glyphicon-film:before {\n  content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n  content: \"\\e010\";\n}\n.glyphicon-th:before {\n  content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n  content: \"\\e012\";\n}\n.glyphicon-ok:before {\n  content: \"\\e013\";\n}\n.glyphicon-remove:before {\n  content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n  content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n  content: \"\\e016\";\n}\n.glyphicon-off:before {\n  content: \"\\e017\";\n}\n.glyphicon-signal:before {\n  content: \"\\e018\";\n}\n.glyphicon-cog:before {\n  content: \"\\e019\";\n}\n.glyphicon-trash:before {\n  content: \"\\e020\";\n}\n.glyphicon-home:before {\n  content: \"\\e021\";\n}\n.glyphicon-file:before {\n  content: \"\\e022\";\n}\n.glyphicon-time:before {\n  content: \"\\e023\";\n}\n.glyphicon-road:before {\n  content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n  content: \"\\e025\";\n}\n.glyphicon-download:before {\n  content: \"\\e026\";\n}\n.glyphicon-upload:before {\n  content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n  content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n  content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n  content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n  content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n  content: \"\\e032\";\n}\n.glyphicon-lock:before {\n  content: \"\\e033\";\n}\n.glyphicon-flag:before {\n  content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n  content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n  content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n  content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n  content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n  content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n  content: \"\\e040\";\n}\n.glyphicon-tag:before {\n  content: \"\\e041\";\n}\n.glyphicon-tags:before {\n  content: \"\\e042\";\n}\n.glyphicon-book:before {\n  content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n  content: \"\\e044\";\n}\n.glyphicon-print:before {\n  content: \"\\e045\";\n}\n.glyphicon-camera:before {\n  content: \"\\e046\";\n}\n.glyphicon-font:before {\n  content: \"\\e047\";\n}\n.glyphicon-bold:before {\n  content: \"\\e048\";\n}\n.glyphicon-italic:before {\n  content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n  content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n  content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n  content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n  content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n  content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n  content: \"\\e055\";\n}\n.glyphicon-list:before {\n  content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n  content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n  content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n  content: \"\\e059\";\n}\n.glyphicon-picture:before {\n  content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n  content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n  content: \"\\e063\";\n}\n.glyphicon-tint:before {\n  content: \"\\e064\";\n}\n.glyphicon-edit:before {\n  content: \"\\e065\";\n}\n.glyphicon-share:before {\n  content: \"\\e066\";\n}\n.glyphicon-check:before {\n  content: \"\\e067\";\n}\n.glyphicon-move:before {\n  content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n  content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n  content: \"\\e070\";\n}\n.glyphicon-backward:before {\n  content: \"\\e071\";\n}\n.glyphicon-play:before {\n  content: \"\\e072\";\n}\n.glyphicon-pause:before {\n  content: \"\\e073\";\n}\n.glyphicon-stop:before {\n  content: \"\\e074\";\n}\n.glyphicon-forward:before {\n  content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n  content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n  content: \"\\e077\";\n}\n.glyphicon-eject:before {\n  content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n  content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n  content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n  content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n  content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n  content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n  content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n  content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n  content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n  content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n  content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n  content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n  content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n  content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n  content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n  content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n  content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n  content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n  content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n  content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n  content: \"\\e101\";\n}\n.glyphicon-gift:before {\n  content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n  content: \"\\e103\";\n}\n.glyphicon-fire:before {\n  content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n  content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n  content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n  content: \"\\e107\";\n}\n.glyphicon-plane:before {\n  content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n  content: \"\\e109\";\n}\n.glyphicon-random:before {\n  content: \"\\e110\";\n}\n.glyphicon-comment:before {\n  content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n  content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n  content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n  content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n  content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n  content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n  content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n  content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n  content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n  content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n  content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n  content: \"\\e122\";\n}\n.glyphicon-bell:before {\n  content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n  content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n  content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n  content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n  content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n  content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n  content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n  content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n  content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n  content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n  content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n  content: \"\\e134\";\n}\n.glyphicon-globe:before {\n  content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n  content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n  content: \"\\e137\";\n}\n.glyphicon-filter:before {\n  content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n  content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n  content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n  content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n  content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n  content: \"\\e143\";\n}\n.glyphicon-link:before {\n  content: \"\\e144\";\n}\n.glyphicon-phone:before {\n  content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n  content: \"\\e146\";\n}\n.glyphicon-usd:before {\n  content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n  content: \"\\e149\";\n}\n.glyphicon-sort:before {\n  content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n  content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n  content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n  content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n  content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n  content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n  content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n  content: \"\\e157\";\n}\n.glyphicon-expand:before {\n  content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n  content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n  content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n  content: \"\\e161\";\n}\n.glyphicon-flash:before {\n  content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n  content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n  content: \"\\e164\";\n}\n.glyphicon-record:before {\n  content: \"\\e165\";\n}\n.glyphicon-save:before {\n  content: \"\\e166\";\n}\n.glyphicon-open:before {\n  content: \"\\e167\";\n}\n.glyphicon-saved:before {\n  content: \"\\e168\";\n}\n.glyphicon-import:before {\n  content: \"\\e169\";\n}\n.glyphicon-export:before {\n  content: \"\\e170\";\n}\n.glyphicon-send:before {\n  content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n  content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n  content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n  content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n  content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n  content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n  content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n  content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n  content: \"\\e179\";\n}\n.glyphicon-header:before {\n  content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n  content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n  content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n  content: \"\\e183\";\n}\n.glyphicon-tower:before {\n  content: \"\\e184\";\n}\n.glyphicon-stats:before {\n  content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n  content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n  content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n  content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n  content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n  content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n  content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n  content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n  content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n  content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n  content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n  content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n  content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n  content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n  content: \"\\e200\";\n}\n.glyphicon-cd:before {\n  content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n  content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n  content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n  content: \"\\e204\";\n}\n.glyphicon-copy:before {\n  content: \"\\e205\";\n}\n.glyphicon-paste:before {\n  content: \"\\e206\";\n}\n.glyphicon-alert:before {\n  content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n  content: \"\\e210\";\n}\n.glyphicon-king:before {\n  content: \"\\e211\";\n}\n.glyphicon-queen:before {\n  content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n  content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n  content: \"\\e214\";\n}\n.glyphicon-knight:before {\n  content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n  content: \"\\e216\";\n}\n.glyphicon-tent:before {\n  content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n  content: \"\\e218\";\n}\n.glyphicon-bed:before {\n  content: \"\\e219\";\n}\n.glyphicon-apple:before {\n  content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n  content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n  content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n  content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n  content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n  content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n  content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n  content: \"\\e227\";\n}\n.glyphicon-btc:before {\n  content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n  content: \"\\e227\";\n}\n.glyphicon-yen:before {\n  content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n  content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n  content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n  content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n  content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n  content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n  content: \"\\e232\";\n}\n.glyphicon-education:before {\n  content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n  content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n  content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n  content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n  content: \"\\e237\";\n}\n.glyphicon-oil:before {\n  content: \"\\e238\";\n}\n.glyphicon-grain:before {\n  content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n  content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n  content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n  content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n  content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n  content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n  content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n  content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n  content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n  content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n  content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n  content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n  content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n  content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n  content: \"\\e253\";\n}\n.glyphicon-console:before {\n  content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n  content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n  content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n  content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n  content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n  content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n  content: \"\\e260\";\n}\n* {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\n*:before,\n*:after {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\nhtml {\n  font-size: 10px;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #333333;\n  background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\na {\n  color: #337ab7;\n  text-decoration: none;\n}\na:hover,\na:focus {\n  color: #23527c;\n  text-decoration: underline;\n}\na:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\nfigure {\n  margin: 0;\n}\nimg {\n  vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  display: block;\n  max-width: 100%;\n  height: auto;\n}\n.img-rounded {\n  border-radius: 6px;\n}\n.img-thumbnail {\n  padding: 4px;\n  line-height: 1.42857143;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-radius: 4px;\n  -webkit-transition: all 0.2s ease-in-out;\n  -o-transition: all 0.2s ease-in-out;\n  transition: all 0.2s ease-in-out;\n  display: inline-block;\n  max-width: 100%;\n  height: auto;\n}\n.img-circle {\n  border-radius: 50%;\n}\nhr {\n  margin-top: 20px;\n  margin-bottom: 20px;\n  border: 0;\n  border-top: 1px solid #eeeeee;\n}\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  clip: auto;\n}\n[role=\"button\"] {\n  cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.1;\n  color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n  font-weight: normal;\n  line-height: 1;\n  color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n  margin-top: 20px;\n  margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n  font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n  font-size: 75%;\n}\nh1,\n.h1 {\n  font-size: 36px;\n}\nh2,\n.h2 {\n  font-size: 30px;\n}\nh3,\n.h3 {\n  font-size: 24px;\n}\nh4,\n.h4 {\n  font-size: 18px;\n}\nh5,\n.h5 {\n  font-size: 14px;\n}\nh6,\n.h6 {\n  font-size: 12px;\n}\np {\n  margin: 0 0 10px;\n}\n.lead {\n  margin-bottom: 20px;\n  font-size: 16px;\n  font-weight: 300;\n  line-height: 1.4;\n}\n@media (min-width: 768px) {\n  .lead {\n    font-size: 21px;\n  }\n}\nsmall,\n.small {\n  font-size: 85%;\n}\nmark,\n.mark {\n  background-color: #fcf8e3;\n  padding: .2em;\n}\n.text-left {\n  text-align: left;\n}\n.text-right {\n  text-align: right;\n}\n.text-center {\n  text-align: center;\n}\n.text-justify {\n  text-align: justify;\n}\n.text-nowrap {\n  white-space: nowrap;\n}\n.text-lowercase {\n  text-transform: lowercase;\n}\n.text-uppercase {\n  text-transform: uppercase;\n}\n.text-capitalize {\n  text-transform: capitalize;\n}\n.text-muted {\n  color: #777777;\n}\n.text-primary {\n  color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n  color: #286090;\n}\n.text-success {\n  color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n  color: #2b542c;\n}\n.text-info {\n  color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n  color: #245269;\n}\n.text-warning {\n  color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n  color: #66512c;\n}\n.text-danger {\n  color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n  color: #843534;\n}\n.bg-primary {\n  color: #fff;\n  background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n  background-color: #286090;\n}\n.bg-success {\n  background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n  background-color: #c1e2b3;\n}\n.bg-info {\n  background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n  background-color: #afd9ee;\n}\n.bg-warning {\n  background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n  background-color: #f7ecb5;\n}\n.bg-danger {\n  background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n  background-color: #e4b9b9;\n}\n.page-header {\n  padding-bottom: 9px;\n  margin: 40px 0 20px;\n  border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n  margin-top: 0;\n  margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n  margin-bottom: 0;\n}\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n  margin-left: -5px;\n}\n.list-inline > li {\n  display: inline-block;\n  padding-left: 5px;\n  padding-right: 5px;\n}\ndl {\n  margin-top: 0;\n  margin-bottom: 20px;\n}\ndt,\ndd {\n  line-height: 1.42857143;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0;\n}\n@media (min-width: 768px) {\n  .dl-horizontal dt {\n    float: left;\n    width: 160px;\n    clear: left;\n    text-align: right;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .dl-horizontal dd {\n    margin-left: 180px;\n  }\n}\nabbr[title],\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted #777777;\n}\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\nblockquote {\n  padding: 10px 20px;\n  margin: 0 0 20px;\n  font-size: 17.5px;\n  border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n  margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n  display: block;\n  font-size: 80%;\n  line-height: 1.42857143;\n  color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n  content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid #eeeeee;\n  border-left: 0;\n  text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n  content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n  content: '\\00A0 \\2014';\n}\naddress {\n  margin-bottom: 20px;\n  font-style: normal;\n  line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #c7254e;\n  background-color: #f9f2f4;\n  border-radius: 4px;\n}\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #fff;\n  background-color: #333;\n  border-radius: 3px;\n  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n  padding: 0;\n  font-size: 100%;\n  font-weight: bold;\n  box-shadow: none;\n}\npre {\n  display: block;\n  padding: 9.5px;\n  margin: 0 0 10px;\n  font-size: 13px;\n  line-height: 1.42857143;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: #333333;\n  background-color: #f5f5f5;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n}\npre code {\n  padding: 0;\n  font-size: inherit;\n  color: inherit;\n  white-space: pre-wrap;\n  background-color: transparent;\n  border-radius: 0;\n}\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n.container {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n@media (min-width: 768px) {\n  .container {\n    width: 750px;\n  }\n}\n@media (min-width: 992px) {\n  .container {\n    width: 970px;\n  }\n}\n@media (min-width: 1200px) {\n  .container {\n    width: 1170px;\n  }\n}\n.container-fluid {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.row {\n  margin-left: -15px;\n  margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n  position: relative;\n  min-height: 1px;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n  float: left;\n}\n.col-xs-12 {\n  width: 100%;\n}\n.col-xs-11 {\n  width: 91.66666667%;\n}\n.col-xs-10 {\n  width: 83.33333333%;\n}\n.col-xs-9 {\n  width: 75%;\n}\n.col-xs-8 {\n  width: 66.66666667%;\n}\n.col-xs-7 {\n  width: 58.33333333%;\n}\n.col-xs-6 {\n  width: 50%;\n}\n.col-xs-5 {\n  width: 41.66666667%;\n}\n.col-xs-4 {\n  width: 33.33333333%;\n}\n.col-xs-3 {\n  width: 25%;\n}\n.col-xs-2 {\n  width: 16.66666667%;\n}\n.col-xs-1 {\n  width: 8.33333333%;\n}\n.col-xs-pull-12 {\n  right: 100%;\n}\n.col-xs-pull-11 {\n  right: 91.66666667%;\n}\n.col-xs-pull-10 {\n  right: 83.33333333%;\n}\n.col-xs-pull-9 {\n  right: 75%;\n}\n.col-xs-pull-8 {\n  right: 66.66666667%;\n}\n.col-xs-pull-7 {\n  right: 58.33333333%;\n}\n.col-xs-pull-6 {\n  right: 50%;\n}\n.col-xs-pull-5 {\n  right: 41.66666667%;\n}\n.col-xs-pull-4 {\n  right: 33.33333333%;\n}\n.col-xs-pull-3 {\n  right: 25%;\n}\n.col-xs-pull-2 {\n  right: 16.66666667%;\n}\n.col-xs-pull-1 {\n  right: 8.33333333%;\n}\n.col-xs-pull-0 {\n  right: auto;\n}\n.col-xs-push-12 {\n  left: 100%;\n}\n.col-xs-push-11 {\n  left: 91.66666667%;\n}\n.col-xs-push-10 {\n  left: 83.33333333%;\n}\n.col-xs-push-9 {\n  left: 75%;\n}\n.col-xs-push-8 {\n  left: 66.66666667%;\n}\n.col-xs-push-7 {\n  left: 58.33333333%;\n}\n.col-xs-push-6 {\n  left: 50%;\n}\n.col-xs-push-5 {\n  left: 41.66666667%;\n}\n.col-xs-push-4 {\n  left: 33.33333333%;\n}\n.col-xs-push-3 {\n  left: 25%;\n}\n.col-xs-push-2 {\n  left: 16.66666667%;\n}\n.col-xs-push-1 {\n  left: 8.33333333%;\n}\n.col-xs-push-0 {\n  left: auto;\n}\n.col-xs-offset-12 {\n  margin-left: 100%;\n}\n.col-xs-offset-11 {\n  margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n  margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n  margin-left: 75%;\n}\n.col-xs-offset-8 {\n  margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n  margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n  margin-left: 50%;\n}\n.col-xs-offset-5 {\n  margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n  margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n  margin-left: 25%;\n}\n.col-xs-offset-2 {\n  margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n  margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n  margin-left: 0%;\n}\n@media (min-width: 768px) {\n  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n    float: left;\n  }\n  .col-sm-12 {\n    width: 100%;\n  }\n  .col-sm-11 {\n    width: 91.66666667%;\n  }\n  .col-sm-10 {\n    width: 83.33333333%;\n  }\n  .col-sm-9 {\n    width: 75%;\n  }\n  .col-sm-8 {\n    width: 66.66666667%;\n  }\n  .col-sm-7 {\n    width: 58.33333333%;\n  }\n  .col-sm-6 {\n    width: 50%;\n  }\n  .col-sm-5 {\n    width: 41.66666667%;\n  }\n  .col-sm-4 {\n    width: 33.33333333%;\n  }\n  .col-sm-3 {\n    width: 25%;\n  }\n  .col-sm-2 {\n    width: 16.66666667%;\n  }\n  .col-sm-1 {\n    width: 8.33333333%;\n  }\n  .col-sm-pull-12 {\n    right: 100%;\n  }\n  .col-sm-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-sm-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-sm-pull-9 {\n    right: 75%;\n  }\n  .col-sm-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-sm-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-sm-pull-6 {\n    right: 50%;\n  }\n  .col-sm-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-sm-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-sm-pull-3 {\n    right: 25%;\n  }\n  .col-sm-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-sm-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-sm-pull-0 {\n    right: auto;\n  }\n  .col-sm-push-12 {\n    left: 100%;\n  }\n  .col-sm-push-11 {\n    left: 91.66666667%;\n  }\n  .col-sm-push-10 {\n    left: 83.33333333%;\n  }\n  .col-sm-push-9 {\n    left: 75%;\n  }\n  .col-sm-push-8 {\n    left: 66.66666667%;\n  }\n  .col-sm-push-7 {\n    left: 58.33333333%;\n  }\n  .col-sm-push-6 {\n    left: 50%;\n  }\n  .col-sm-push-5 {\n    left: 41.66666667%;\n  }\n  .col-sm-push-4 {\n    left: 33.33333333%;\n  }\n  .col-sm-push-3 {\n    left: 25%;\n  }\n  .col-sm-push-2 {\n    left: 16.66666667%;\n  }\n  .col-sm-push-1 {\n    left: 8.33333333%;\n  }\n  .col-sm-push-0 {\n    left: auto;\n  }\n  .col-sm-offset-12 {\n    margin-left: 100%;\n  }\n  .col-sm-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-sm-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-sm-offset-9 {\n    margin-left: 75%;\n  }\n  .col-sm-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-sm-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-sm-offset-6 {\n    margin-left: 50%;\n  }\n  .col-sm-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-sm-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-sm-offset-3 {\n    margin-left: 25%;\n  }\n  .col-sm-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-sm-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-sm-offset-0 {\n    margin-left: 0%;\n  }\n}\n@media (min-width: 992px) {\n  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n    float: left;\n  }\n  .col-md-12 {\n    width: 100%;\n  }\n  .col-md-11 {\n    width: 91.66666667%;\n  }\n  .col-md-10 {\n    width: 83.33333333%;\n  }\n  .col-md-9 {\n    width: 75%;\n  }\n  .col-md-8 {\n    width: 66.66666667%;\n  }\n  .col-md-7 {\n    width: 58.33333333%;\n  }\n  .col-md-6 {\n    width: 50%;\n  }\n  .col-md-5 {\n    width: 41.66666667%;\n  }\n  .col-md-4 {\n    width: 33.33333333%;\n  }\n  .col-md-3 {\n    width: 25%;\n  }\n  .col-md-2 {\n    width: 16.66666667%;\n  }\n  .col-md-1 {\n    width: 8.33333333%;\n  }\n  .col-md-pull-12 {\n    right: 100%;\n  }\n  .col-md-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-md-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-md-pull-9 {\n    right: 75%;\n  }\n  .col-md-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-md-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-md-pull-6 {\n    right: 50%;\n  }\n  .col-md-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-md-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-md-pull-3 {\n    right: 25%;\n  }\n  .col-md-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-md-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-md-pull-0 {\n    right: auto;\n  }\n  .col-md-push-12 {\n    left: 100%;\n  }\n  .col-md-push-11 {\n    left: 91.66666667%;\n  }\n  .col-md-push-10 {\n    left: 83.33333333%;\n  }\n  .col-md-push-9 {\n    left: 75%;\n  }\n  .col-md-push-8 {\n    left: 66.66666667%;\n  }\n  .col-md-push-7 {\n    left: 58.33333333%;\n  }\n  .col-md-push-6 {\n    left: 50%;\n  }\n  .col-md-push-5 {\n    left: 41.66666667%;\n  }\n  .col-md-push-4 {\n    left: 33.33333333%;\n  }\n  .col-md-push-3 {\n    left: 25%;\n  }\n  .col-md-push-2 {\n    left: 16.66666667%;\n  }\n  .col-md-push-1 {\n    left: 8.33333333%;\n  }\n  .col-md-push-0 {\n    left: auto;\n  }\n  .col-md-offset-12 {\n    margin-left: 100%;\n  }\n  .col-md-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-md-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-md-offset-9 {\n    margin-left: 75%;\n  }\n  .col-md-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-md-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-md-offset-6 {\n    margin-left: 50%;\n  }\n  .col-md-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-md-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-md-offset-3 {\n    margin-left: 25%;\n  }\n  .col-md-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-md-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-md-offset-0 {\n    margin-left: 0%;\n  }\n}\n@media (min-width: 1200px) {\n  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n    float: left;\n  }\n  .col-lg-12 {\n    width: 100%;\n  }\n  .col-lg-11 {\n    width: 91.66666667%;\n  }\n  .col-lg-10 {\n    width: 83.33333333%;\n  }\n  .col-lg-9 {\n    width: 75%;\n  }\n  .col-lg-8 {\n    width: 66.66666667%;\n  }\n  .col-lg-7 {\n    width: 58.33333333%;\n  }\n  .col-lg-6 {\n    width: 50%;\n  }\n  .col-lg-5 {\n    width: 41.66666667%;\n  }\n  .col-lg-4 {\n    width: 33.33333333%;\n  }\n  .col-lg-3 {\n    width: 25%;\n  }\n  .col-lg-2 {\n    width: 16.66666667%;\n  }\n  .col-lg-1 {\n    width: 8.33333333%;\n  }\n  .col-lg-pull-12 {\n    right: 100%;\n  }\n  .col-lg-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-lg-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-lg-pull-9 {\n    right: 75%;\n  }\n  .col-lg-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-lg-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-lg-pull-6 {\n    right: 50%;\n  }\n  .col-lg-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-lg-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-lg-pull-3 {\n    right: 25%;\n  }\n  .col-lg-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-lg-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-lg-pull-0 {\n    right: auto;\n  }\n  .col-lg-push-12 {\n    left: 100%;\n  }\n  .col-lg-push-11 {\n    left: 91.66666667%;\n  }\n  .col-lg-push-10 {\n    left: 83.33333333%;\n  }\n  .col-lg-push-9 {\n    left: 75%;\n  }\n  .col-lg-push-8 {\n    left: 66.66666667%;\n  }\n  .col-lg-push-7 {\n    left: 58.33333333%;\n  }\n  .col-lg-push-6 {\n    left: 50%;\n  }\n  .col-lg-push-5 {\n    left: 41.66666667%;\n  }\n  .col-lg-push-4 {\n    left: 33.33333333%;\n  }\n  .col-lg-push-3 {\n    left: 25%;\n  }\n  .col-lg-push-2 {\n    left: 16.66666667%;\n  }\n  .col-lg-push-1 {\n    left: 8.33333333%;\n  }\n  .col-lg-push-0 {\n    left: auto;\n  }\n  .col-lg-offset-12 {\n    margin-left: 100%;\n  }\n  .col-lg-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-lg-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-lg-offset-9 {\n    margin-left: 75%;\n  }\n  .col-lg-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-lg-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-lg-offset-6 {\n    margin-left: 50%;\n  }\n  .col-lg-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-lg-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-lg-offset-3 {\n    margin-left: 25%;\n  }\n  .col-lg-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-lg-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-lg-offset-0 {\n    margin-left: 0%;\n  }\n}\ntable {\n  background-color: transparent;\n}\ncaption {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  color: #777777;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n  padding: 8px;\n  line-height: 1.42857143;\n  vertical-align: top;\n  border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n  border-top: 0;\n}\n.table > tbody + tbody {\n  border-top: 2px solid #ddd;\n}\n.table .table {\n  background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n  padding: 5px;\n}\n.table-bordered {\n  border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n  border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n  border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n  background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n  background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n  position: static;\n  float: none;\n  display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n  position: static;\n  float: none;\n  display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n  background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n  background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n  background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n  background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n  background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n  background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n  background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n  background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n  background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n  background-color: #ebcccc;\n}\n.table-responsive {\n  overflow-x: auto;\n  min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n  .table-responsive {\n    width: 100%;\n    margin-bottom: 15px;\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid #ddd;\n  }\n  .table-responsive > .table {\n    margin-bottom: 0;\n  }\n  .table-responsive > .table > thead > tr > th,\n  .table-responsive > .table > tbody > tr > th,\n  .table-responsive > .table > tfoot > tr > th,\n  .table-responsive > .table > thead > tr > td,\n  .table-responsive > .table > tbody > tr > td,\n  .table-responsive > .table > tfoot > tr > td {\n    white-space: nowrap;\n  }\n  .table-responsive > .table-bordered {\n    border: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:first-child,\n  .table-responsive > .table-bordered > tbody > tr > th:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n  .table-responsive > .table-bordered > thead > tr > td:first-child,\n  .table-responsive > .table-bordered > tbody > tr > td:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n    border-left: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:last-child,\n  .table-responsive > .table-bordered > tbody > tr > th:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n  .table-responsive > .table-bordered > thead > tr > td:last-child,\n  .table-responsive > .table-bordered > tbody > tr > td:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n    border-right: 0;\n  }\n  .table-responsive > .table-bordered > tbody > tr:last-child > th,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n  .table-responsive > .table-bordered > tbody > tr:last-child > td,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n    border-bottom: 0;\n  }\n}\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  min-width: 0;\n}\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 20px;\n  font-size: 21px;\n  line-height: inherit;\n  color: #333333;\n  border: 0;\n  border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n  display: inline-block;\n  max-width: 100%;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\ninput[type=\"search\"] {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9;\n  line-height: normal;\n}\ninput[type=\"file\"] {\n  display: block;\n}\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\nselect[multiple],\nselect[size] {\n  height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\noutput {\n  display: block;\n  padding-top: 7px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555555;\n}\n.form-control {\n  display: block;\n  width: 100%;\n  height: 34px;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555555;\n  background-color: #fff;\n  background-image: none;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n  border-color: #66afe9;\n  outline: 0;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n  color: #999;\n  opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n  color: #999;\n}\n.form-control::-webkit-input-placeholder {\n  color: #999;\n}\n.form-control::-ms-expand {\n  border: 0;\n  background-color: transparent;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n  background-color: #eeeeee;\n  opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n  cursor: not-allowed;\n}\ntextarea.form-control {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"].form-control,\n  input[type=\"time\"].form-control,\n  input[type=\"datetime-local\"].form-control,\n  input[type=\"month\"].form-control {\n    line-height: 34px;\n  }\n  input[type=\"date\"].input-sm,\n  input[type=\"time\"].input-sm,\n  input[type=\"datetime-local\"].input-sm,\n  input[type=\"month\"].input-sm,\n  .input-group-sm input[type=\"date\"],\n  .input-group-sm input[type=\"time\"],\n  .input-group-sm input[type=\"datetime-local\"],\n  .input-group-sm input[type=\"month\"] {\n    line-height: 30px;\n  }\n  input[type=\"date\"].input-lg,\n  input[type=\"time\"].input-lg,\n  input[type=\"datetime-local\"].input-lg,\n  input[type=\"month\"].input-lg,\n  .input-group-lg input[type=\"date\"],\n  .input-group-lg input[type=\"time\"],\n  .input-group-lg input[type=\"datetime-local\"],\n  .input-group-lg input[type=\"month\"] {\n    line-height: 46px;\n  }\n}\n.form-group {\n  margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n  min-height: 20px;\n  padding-left: 20px;\n  margin-bottom: 0;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-left: -20px;\n  margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n  position: relative;\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n  cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n  cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n  cursor: not-allowed;\n}\n.form-control-static {\n  padding-top: 7px;\n  padding-bottom: 7px;\n  margin-bottom: 0;\n  min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n  padding-left: 0;\n  padding-right: 0;\n}\n.input-sm {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-sm {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n  height: auto;\n}\n.form-group-sm .form-control {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.form-group-sm select.form-control {\n  height: 30px;\n  line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n  height: auto;\n}\n.form-group-sm .form-control-static {\n  height: 30px;\n  min-height: 32px;\n  padding: 6px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n}\n.input-lg {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-lg {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n  height: auto;\n}\n.form-group-lg .form-control {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\n.form-group-lg select.form-control {\n  height: 46px;\n  line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n  height: auto;\n}\n.form-group-lg .form-control-static {\n  height: 46px;\n  min-height: 38px;\n  padding: 11px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n}\n.has-feedback {\n  position: relative;\n}\n.has-feedback .form-control {\n  padding-right: 42.5px;\n}\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  display: block;\n  width: 34px;\n  height: 34px;\n  line-height: 34px;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n  width: 46px;\n  height: 46px;\n  line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n  width: 30px;\n  height: 30px;\n  line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n  color: #3c763d;\n}\n.has-success .form-control {\n  border-color: #3c763d;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n  border-color: #2b542c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n  color: #3c763d;\n  border-color: #3c763d;\n  background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n  color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n  color: #8a6d3b;\n}\n.has-warning .form-control {\n  border-color: #8a6d3b;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n  border-color: #66512c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n  color: #8a6d3b;\n  border-color: #8a6d3b;\n  background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n  color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n  color: #a94442;\n}\n.has-error .form-control {\n  border-color: #a94442;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n  border-color: #843534;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n  color: #a94442;\n  border-color: #a94442;\n  background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n  color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n  top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n  top: 0;\n}\n.help-block {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: #737373;\n}\n@media (min-width: 768px) {\n  .form-inline .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .form-inline .form-control-static {\n    display: inline-block;\n  }\n  .form-inline .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .form-inline .input-group .input-group-addon,\n  .form-inline .input-group .input-group-btn,\n  .form-inline .input-group .form-control {\n    width: auto;\n  }\n  .form-inline .input-group > .form-control {\n    width: 100%;\n  }\n  .form-inline .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio,\n  .form-inline .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio label,\n  .form-inline .checkbox label {\n    padding-left: 0;\n  }\n  .form-inline .radio input[type=\"radio\"],\n  .form-inline .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .form-inline .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n  margin-top: 0;\n  margin-bottom: 0;\n  padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n  min-height: 27px;\n}\n.form-horizontal .form-group {\n  margin-left: -15px;\n  margin-right: -15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .control-label {\n    text-align: right;\n    margin-bottom: 0;\n    padding-top: 7px;\n  }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n  right: 15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-lg .control-label {\n    padding-top: 11px;\n    font-size: 18px;\n  }\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-sm .control-label {\n    padding-top: 6px;\n    font-size: 12px;\n  }\n}\n.btn {\n  display: inline-block;\n  margin-bottom: 0;\n  font-weight: normal;\n  text-align: center;\n  vertical-align: middle;\n  touch-action: manipulation;\n  cursor: pointer;\n  background-image: none;\n  border: 1px solid transparent;\n  white-space: nowrap;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  border-radius: 4px;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n  color: #333;\n  text-decoration: none;\n}\n.btn:active,\n.btn.active {\n  outline: 0;\n  background-image: none;\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n  cursor: not-allowed;\n  opacity: 0.65;\n  filter: alpha(opacity=65);\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n  pointer-events: none;\n}\n.btn-default {\n  color: #333;\n  background-color: #fff;\n  border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n  color: #333;\n  background-color: #e6e6e6;\n  border-color: #8c8c8c;\n}\n.btn-default:hover {\n  color: #333;\n  background-color: #e6e6e6;\n  border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  color: #333;\n  background-color: #e6e6e6;\n  border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n  color: #333;\n  background-color: #d4d4d4;\n  border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  background-image: none;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n  background-color: #fff;\n  border-color: #ccc;\n}\n.btn-default .badge {\n  color: #fff;\n  background-color: #333;\n}\n.btn-primary {\n  color: #fff;\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n  color: #fff;\n  background-color: #286090;\n  border-color: #122b40;\n}\n.btn-primary:hover {\n  color: #fff;\n  background-color: #286090;\n  border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  color: #fff;\n  background-color: #286090;\n  border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n  color: #fff;\n  background-color: #204d74;\n  border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  background-image: none;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary .badge {\n  color: #337ab7;\n  background-color: #fff;\n}\n.btn-success {\n  color: #fff;\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n  color: #fff;\n  background-color: #449d44;\n  border-color: #255625;\n}\n.btn-success:hover {\n  color: #fff;\n  background-color: #449d44;\n  border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  color: #fff;\n  background-color: #449d44;\n  border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n  color: #fff;\n  background-color: #398439;\n  border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  background-image: none;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success .badge {\n  color: #5cb85c;\n  background-color: #fff;\n}\n.btn-info {\n  color: #fff;\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n  color: #fff;\n  background-color: #31b0d5;\n  border-color: #1b6d85;\n}\n.btn-info:hover {\n  color: #fff;\n  background-color: #31b0d5;\n  border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  color: #fff;\n  background-color: #31b0d5;\n  border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n  color: #fff;\n  background-color: #269abc;\n  border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  background-image: none;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info .badge {\n  color: #5bc0de;\n  background-color: #fff;\n}\n.btn-warning {\n  color: #fff;\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n  color: #fff;\n  background-color: #ec971f;\n  border-color: #985f0d;\n}\n.btn-warning:hover {\n  color: #fff;\n  background-color: #ec971f;\n  border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  color: #fff;\n  background-color: #ec971f;\n  border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n  color: #fff;\n  background-color: #d58512;\n  border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  background-image: none;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning .badge {\n  color: #f0ad4e;\n  background-color: #fff;\n}\n.btn-danger {\n  color: #fff;\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n  color: #fff;\n  background-color: #c9302c;\n  border-color: #761c19;\n}\n.btn-danger:hover {\n  color: #fff;\n  background-color: #c9302c;\n  border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  color: #fff;\n  background-color: #c9302c;\n  border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n  color: #fff;\n  background-color: #ac2925;\n  border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  background-image: none;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger .badge {\n  color: #d9534f;\n  background-color: #fff;\n}\n.btn-link {\n  color: #337ab7;\n  font-weight: normal;\n  border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n  background-color: transparent;\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n  border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n  color: #23527c;\n  text-decoration: underline;\n  background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n  color: #777777;\n  text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n  padding: 1px 5px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-block {\n  display: block;\n  width: 100%;\n}\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n.fade {\n  opacity: 0;\n  -webkit-transition: opacity 0.15s linear;\n  -o-transition: opacity 0.15s linear;\n  transition: opacity 0.15s linear;\n}\n.fade.in {\n  opacity: 1;\n}\n.collapse {\n  display: none;\n}\n.collapse.in {\n  display: block;\n}\ntr.collapse.in {\n  display: table-row;\n}\ntbody.collapse.in {\n  display: table-row-group;\n}\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  -webkit-transition-property: height, visibility;\n  transition-property: height, visibility;\n  -webkit-transition-duration: 0.35s;\n  transition-duration: 0.35s;\n  -webkit-transition-timing-function: ease;\n  transition-timing-function: ease;\n}\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top: 4px dashed;\n  border-top: 4px solid \\9;\n  border-right: 4px solid transparent;\n  border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n  position: relative;\n}\n.dropdown-toggle:focus {\n  outline: 0;\n}\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0;\n  list-style: none;\n  font-size: 14px;\n  text-align: left;\n  background-color: #fff;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n.dropdown-menu .divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n  display: block;\n  padding: 3px 20px;\n  clear: both;\n  font-weight: normal;\n  line-height: 1.42857143;\n  color: #333333;\n  white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  text-decoration: none;\n  color: #262626;\n  background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  color: #fff;\n  text-decoration: none;\n  outline: 0;\n  background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  text-decoration: none;\n  background-color: transparent;\n  background-image: none;\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  cursor: not-allowed;\n}\n.open > .dropdown-menu {\n  display: block;\n}\n.open > a {\n  outline: 0;\n}\n.dropdown-menu-right {\n  left: auto;\n  right: 0;\n}\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: 12px;\n  line-height: 1.42857143;\n  color: #777777;\n  white-space: nowrap;\n}\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: 990;\n}\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n  border-top: 0;\n  border-bottom: 4px dashed;\n  border-bottom: 4px solid \\9;\n  content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n  .navbar-right .dropdown-menu {\n    left: auto;\n    right: 0;\n  }\n  .navbar-right .dropdown-menu-left {\n    left: 0;\n    right: auto;\n  }\n}\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n  z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n  margin-left: -1px;\n}\n.btn-toolbar {\n  margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n  float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n  margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n.btn-group > .btn:first-child {\n  margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n  -webkit-box-shadow: none;\n  box-shadow: none;\n}\n.btn .caret {\n  margin-left: 0;\n}\n.btn-lg .caret {\n  border-width: 5px 5px 0;\n  border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n  border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n  display: block;\n  float: none;\n  width: 100%;\n  max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n  float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n  margin-top: -1px;\n  margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n  float: none;\n  display: table-cell;\n  width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n  width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n  left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n.input-group {\n  position: relative;\n  display: table;\n  border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n  float: none;\n  padding-left: 0;\n  padding-right: 0;\n}\n.input-group .form-control {\n  position: relative;\n  z-index: 2;\n  float: left;\n  width: 100%;\n  margin-bottom: 0;\n}\n.input-group .form-control:focus {\n  z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle;\n}\n.input-group-addon {\n  padding: 6px 12px;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1;\n  color: #555555;\n  text-align: center;\n  background-color: #eeeeee;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n}\n.input-group-addon.input-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n  border-radius: 3px;\n}\n.input-group-addon.input-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n.input-group-btn {\n  position: relative;\n  font-size: 0;\n  white-space: nowrap;\n}\n.input-group-btn > .btn {\n  position: relative;\n}\n.input-group-btn > .btn + .btn {\n  margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n  z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n  margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n  z-index: 2;\n  margin-left: -1px;\n}\n.nav {\n  margin-bottom: 0;\n  padding-left: 0;\n  list-style: none;\n}\n.nav > li {\n  position: relative;\n  display: block;\n}\n.nav > li > a {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n  color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n  color: #777777;\n  text-decoration: none;\n  background-color: transparent;\n  cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n  background-color: #eeeeee;\n  border-color: #337ab7;\n}\n.nav .nav-divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.nav > li > a > img {\n  max-width: none;\n}\n.nav-tabs {\n  border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n  float: left;\n  margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n  margin-right: 2px;\n  line-height: 1.42857143;\n  border: 1px solid transparent;\n  border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n  border-color: #eeeeee #eeeeee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n  color: #555555;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-bottom-color: transparent;\n  cursor: default;\n}\n.nav-tabs.nav-justified {\n  width: 100%;\n  border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n  float: none;\n}\n.nav-tabs.nav-justified > li > a {\n  text-align: center;\n  margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-tabs.nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs.nav-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n  border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li > a {\n    border-bottom: 1px solid #ddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs.nav-justified > .active > a,\n  .nav-tabs.nav-justified > .active > a:hover,\n  .nav-tabs.nav-justified > .active > a:focus {\n    border-bottom-color: #fff;\n  }\n}\n.nav-pills > li {\n  float: left;\n}\n.nav-pills > li > a {\n  border-radius: 4px;\n}\n.nav-pills > li + li {\n  margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n  color: #fff;\n  background-color: #337ab7;\n}\n.nav-stacked > li {\n  float: none;\n}\n.nav-stacked > li + li {\n  margin-top: 2px;\n  margin-left: 0;\n}\n.nav-justified {\n  width: 100%;\n}\n.nav-justified > li {\n  float: none;\n}\n.nav-justified > li > a {\n  text-align: center;\n  margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs-justified {\n  border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n  border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs-justified > li > a {\n    border-bottom: 1px solid #ddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs-justified > .active > a,\n  .nav-tabs-justified > .active > a:hover,\n  .nav-tabs-justified > .active > a:focus {\n    border-bottom-color: #fff;\n  }\n}\n.tab-content > .tab-pane {\n  display: none;\n}\n.tab-content > .active {\n  display: block;\n}\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.navbar {\n  position: relative;\n  min-height: 50px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n  .navbar {\n    border-radius: 4px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-header {\n    float: left;\n  }\n}\n.navbar-collapse {\n  overflow-x: visible;\n  padding-right: 15px;\n  padding-left: 15px;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n  -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n  overflow-y: auto;\n}\n@media (min-width: 768px) {\n  .navbar-collapse {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n  }\n  .navbar-collapse.collapse {\n    display: block !important;\n    height: auto !important;\n    padding-bottom: 0;\n    overflow: visible !important;\n  }\n  .navbar-collapse.in {\n    overflow-y: visible;\n  }\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-static-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n  max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    max-height: 200px;\n  }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n@media (min-width: 768px) {\n  .container > .navbar-header,\n  .container-fluid > .navbar-header,\n  .container > .navbar-collapse,\n  .container-fluid > .navbar-collapse {\n    margin-right: 0;\n    margin-left: 0;\n  }\n}\n.navbar-static-top {\n  z-index: 1000;\n  border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n  .navbar-static-top {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n@media (min-width: 768px) {\n  .navbar-fixed-top,\n  .navbar-fixed-bottom {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0;\n  border-width: 1px 0 0;\n}\n.navbar-brand {\n  float: left;\n  padding: 15px 15px;\n  font-size: 18px;\n  line-height: 20px;\n  height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n  text-decoration: none;\n}\n.navbar-brand > img {\n  display: block;\n}\n@media (min-width: 768px) {\n  .navbar > .container .navbar-brand,\n  .navbar > .container-fluid .navbar-brand {\n    margin-left: -15px;\n  }\n}\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: 15px;\n  padding: 9px 10px;\n  margin-top: 8px;\n  margin-bottom: 8px;\n  background-color: transparent;\n  background-image: none;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.navbar-toggle:focus {\n  outline: 0;\n}\n.navbar-toggle .icon-bar {\n  display: block;\n  width: 22px;\n  height: 2px;\n  border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n  margin-top: 4px;\n}\n@media (min-width: 768px) {\n  .navbar-toggle {\n    display: none;\n  }\n}\n.navbar-nav {\n  margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n  padding-top: 10px;\n  padding-bottom: 10px;\n  line-height: 20px;\n}\n@media (max-width: 767px) {\n  .navbar-nav .open .dropdown-menu {\n    position: static;\n    float: none;\n    width: auto;\n    margin-top: 0;\n    background-color: transparent;\n    border: 0;\n    box-shadow: none;\n  }\n  .navbar-nav .open .dropdown-menu > li > a,\n  .navbar-nav .open .dropdown-menu .dropdown-header {\n    padding: 5px 15px 5px 25px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a {\n    line-height: 20px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-nav .open .dropdown-menu > li > a:focus {\n    background-image: none;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-nav {\n    float: left;\n    margin: 0;\n  }\n  .navbar-nav > li {\n    float: left;\n  }\n  .navbar-nav > li > a {\n    padding-top: 15px;\n    padding-bottom: 15px;\n  }\n}\n.navbar-form {\n  margin-left: -15px;\n  margin-right: -15px;\n  padding: 10px 15px;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n  .navbar-form .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control-static {\n    display: inline-block;\n  }\n  .navbar-form .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .navbar-form .input-group .input-group-addon,\n  .navbar-form .input-group .input-group-btn,\n  .navbar-form .input-group .form-control {\n    width: auto;\n  }\n  .navbar-form .input-group > .form-control {\n    width: 100%;\n  }\n  .navbar-form .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio,\n  .navbar-form .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio label,\n  .navbar-form .checkbox label {\n    padding-left: 0;\n  }\n  .navbar-form .radio input[type=\"radio\"],\n  .navbar-form .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .navbar-form .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n@media (max-width: 767px) {\n  .navbar-form .form-group {\n    margin-bottom: 5px;\n  }\n  .navbar-form .form-group:last-child {\n    margin-bottom: 0;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-form {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    -webkit-box-shadow: none;\n    box-shadow: none;\n  }\n}\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  margin-bottom: 0;\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.navbar-btn {\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n  margin-top: 14px;\n  margin-bottom: 14px;\n}\n.navbar-text {\n  margin-top: 15px;\n  margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n  .navbar-text {\n    float: left;\n    margin-left: 15px;\n    margin-right: 15px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-left {\n    float: left !important;\n  }\n  .navbar-right {\n    float: right !important;\n    margin-right: -15px;\n  }\n  .navbar-right ~ .navbar-right {\n    margin-right: 0;\n  }\n}\n.navbar-default {\n  background-color: #f8f8f8;\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n  color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n  color: #5e5e5e;\n  background-color: transparent;\n}\n.navbar-default .navbar-text {\n  color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n  color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n  color: #333;\n  background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n  color: #555;\n  background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n  color: #ccc;\n  background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n  border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n  background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n  background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n  background-color: #e7e7e7;\n  color: #555;\n}\n@media (max-width: 767px) {\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n    color: #777;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #333;\n    background-color: transparent;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #555;\n    background-color: #e7e7e7;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #ccc;\n    background-color: transparent;\n  }\n}\n.navbar-default .navbar-link {\n  color: #777;\n}\n.navbar-default .navbar-link:hover {\n  color: #333;\n}\n.navbar-default .btn-link {\n  color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n  color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n  color: #ccc;\n}\n.navbar-inverse {\n  background-color: #222;\n  border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n  color: #fff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n  color: #fff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n  color: #fff;\n  background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n  color: #444;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n  border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n  background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n  background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n  border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n  background-color: #080808;\n  color: #fff;\n}\n@media (max-width: 767px) {\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n    border-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n    color: #9d9d9d;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #fff;\n    background-color: transparent;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #fff;\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #444;\n    background-color: transparent;\n  }\n}\n.navbar-inverse .navbar-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n  color: #fff;\n}\n.navbar-inverse .btn-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n  color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n  color: #444;\n}\n.breadcrumb {\n  padding: 8px 15px;\n  margin-bottom: 20px;\n  list-style: none;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n}\n.breadcrumb > li {\n  display: inline-block;\n}\n.breadcrumb > li + li:before {\n  content: \"/\\00a0\";\n  padding: 0 5px;\n  color: #ccc;\n}\n.breadcrumb > .active {\n  color: #777777;\n}\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: 20px 0;\n  border-radius: 4px;\n}\n.pagination > li {\n  display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n  position: relative;\n  float: left;\n  padding: 6px 12px;\n  line-height: 1.42857143;\n  text-decoration: none;\n  color: #337ab7;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n  margin-left: 0;\n  border-bottom-left-radius: 4px;\n  border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n  border-bottom-right-radius: 4px;\n  border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n  z-index: 2;\n  color: #23527c;\n  background-color: #eeeeee;\n  border-color: #ddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n  z-index: 3;\n  color: #fff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n  cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n  color: #777777;\n  background-color: #fff;\n  border-color: #ddd;\n  cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n  border-bottom-left-radius: 6px;\n  border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n  border-bottom-right-radius: 6px;\n  border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n  border-bottom-left-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n  border-bottom-right-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.pager {\n  padding-left: 0;\n  margin: 20px 0;\n  list-style: none;\n  text-align: center;\n}\n.pager li {\n  display: inline;\n}\n.pager li > a,\n.pager li > span {\n  display: inline-block;\n  padding: 5px 14px;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n  float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n  float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n  color: #777777;\n  background-color: #fff;\n  cursor: not-allowed;\n}\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: #fff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n  color: #fff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.label:empty {\n  display: none;\n}\n.btn .label {\n  position: relative;\n  top: -1px;\n}\n.label-default {\n  background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n  background-color: #5e5e5e;\n}\n.label-primary {\n  background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n  background-color: #286090;\n}\n.label-success {\n  background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n  background-color: #449d44;\n}\n.label-info {\n  background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n  background-color: #31b0d5;\n}\n.label-warning {\n  background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n  background-color: #ec971f;\n}\n.label-danger {\n  background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n  background-color: #c9302c;\n}\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: 12px;\n  font-weight: bold;\n  color: #fff;\n  line-height: 1;\n  vertical-align: middle;\n  white-space: nowrap;\n  text-align: center;\n  background-color: #777777;\n  border-radius: 10px;\n}\n.badge:empty {\n  display: none;\n}\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n  top: 0;\n  padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n  color: #fff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n  color: #337ab7;\n  background-color: #fff;\n}\n.list-group-item > .badge {\n  float: right;\n}\n.list-group-item > .badge + .badge {\n  margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n  margin-left: 3px;\n}\n.jumbotron {\n  padding-top: 30px;\n  padding-bottom: 30px;\n  margin-bottom: 30px;\n  color: inherit;\n  background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n  color: inherit;\n}\n.jumbotron p {\n  margin-bottom: 15px;\n  font-size: 21px;\n  font-weight: 200;\n}\n.jumbotron > hr {\n  border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n  border-radius: 6px;\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.jumbotron .container {\n  max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n  .jumbotron {\n    padding-top: 48px;\n    padding-bottom: 48px;\n  }\n  .container .jumbotron,\n  .container-fluid .jumbotron {\n    padding-left: 60px;\n    padding-right: 60px;\n  }\n  .jumbotron h1,\n  .jumbotron .h1 {\n    font-size: 63px;\n  }\n}\n.thumbnail {\n  display: block;\n  padding: 4px;\n  margin-bottom: 20px;\n  line-height: 1.42857143;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-radius: 4px;\n  -webkit-transition: border 0.2s ease-in-out;\n  -o-transition: border 0.2s ease-in-out;\n  transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n  margin-left: auto;\n  margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n  border-color: #337ab7;\n}\n.thumbnail .caption {\n  padding: 9px;\n  color: #333333;\n}\n.alert {\n  padding: 15px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.alert h4 {\n  margin-top: 0;\n  color: inherit;\n}\n.alert .alert-link {\n  font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n  margin-bottom: 0;\n}\n.alert > p + p {\n  margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n  padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n  position: relative;\n  top: -2px;\n  right: -21px;\n  color: inherit;\n}\n.alert-success {\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n  color: #3c763d;\n}\n.alert-success hr {\n  border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n  color: #2b542c;\n}\n.alert-info {\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n  color: #31708f;\n}\n.alert-info hr {\n  border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n  color: #245269;\n}\n.alert-warning {\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n  color: #8a6d3b;\n}\n.alert-warning hr {\n  border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n  color: #66512c;\n}\n.alert-danger {\n  background-color: #f2dede;\n  border-color: #ebccd1;\n  color: #a94442;\n}\n.alert-danger hr {\n  border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n  color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n.progress {\n  overflow: hidden;\n  height: 20px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: 12px;\n  line-height: 20px;\n  color: #fff;\n  text-align: center;\n  background-color: #337ab7;\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  -webkit-transition: width 0.6s ease;\n  -o-transition: width 0.6s ease;\n  transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n  -webkit-animation: progress-bar-stripes 2s linear infinite;\n  -o-animation: progress-bar-stripes 2s linear infinite;\n  animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n  background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n  background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n  background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n  background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n  margin-top: 15px;\n}\n.media:first-child {\n  margin-top: 0;\n}\n.media,\n.media-body {\n  zoom: 1;\n  overflow: hidden;\n}\n.media-body {\n  width: 10000px;\n}\n.media-object {\n  display: block;\n}\n.media-object.img-thumbnail {\n  max-width: none;\n}\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n.media-middle {\n  vertical-align: middle;\n}\n.media-bottom {\n  vertical-align: bottom;\n}\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n.list-group {\n  margin-bottom: 20px;\n  padding-left: 0;\n}\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  margin-bottom: -1px;\n  background-color: #fff;\n  border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n  margin-bottom: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n  color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n  color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n  text-decoration: none;\n  color: #555;\n  background-color: #f5f5f5;\n}\nbutton.list-group-item {\n  width: 100%;\n  text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n  background-color: #eeeeee;\n  color: #777777;\n  cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n  color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n  color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  z-index: 2;\n  color: #fff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n  color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n  color: #c7ddef;\n}\n.list-group-item-success {\n  color: #3c763d;\n  background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n  color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n  color: #3c763d;\n  background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n  color: #fff;\n  background-color: #3c763d;\n  border-color: #3c763d;\n}\n.list-group-item-info {\n  color: #31708f;\n  background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n  color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n  color: #31708f;\n  background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n  color: #fff;\n  background-color: #31708f;\n  border-color: #31708f;\n}\n.list-group-item-warning {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n  color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n  color: #8a6d3b;\n  background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n  color: #fff;\n  background-color: #8a6d3b;\n  border-color: #8a6d3b;\n}\n.list-group-item-danger {\n  color: #a94442;\n  background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n  color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n  color: #a94442;\n  background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n  color: #fff;\n  background-color: #a94442;\n  border-color: #a94442;\n}\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n.panel {\n  margin-bottom: 20px;\n  background-color: #fff;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n  padding: 15px;\n}\n.panel-heading {\n  padding: 10px 15px;\n  border-bottom: 1px solid transparent;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n  color: inherit;\n}\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: 16px;\n  color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n  color: inherit;\n}\n.panel-footer {\n  padding: 10px 15px;\n  background-color: #f5f5f5;\n  border-top: 1px solid #ddd;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n  margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n  border-width: 1px 0;\n  border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n  border-top: 0;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n  border-bottom: 0;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n  border-top-width: 0;\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n  margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n  padding-left: 15px;\n  padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n  border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n  border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n  border-bottom-left-radius: 3px;\n  border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n  border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n  border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n  border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n  border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n  border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n  border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n  border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n  border-bottom: 0;\n}\n.panel > .table-responsive {\n  border: 0;\n  margin-bottom: 0;\n}\n.panel-group {\n  margin-bottom: 20px;\n}\n.panel-group .panel {\n  margin-bottom: 0;\n  border-radius: 4px;\n}\n.panel-group .panel + .panel {\n  margin-top: 5px;\n}\n.panel-group .panel-heading {\n  border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n  border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n  border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n  border-bottom: 1px solid #ddd;\n}\n.panel-default {\n  border-color: #ddd;\n}\n.panel-default > .panel-heading {\n  color: #333333;\n  background-color: #f5f5f5;\n  border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n  color: #f5f5f5;\n  background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #ddd;\n}\n.panel-primary {\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n  color: #fff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n  color: #337ab7;\n  background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #337ab7;\n}\n.panel-success {\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n  color: #dff0d8;\n  background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #d6e9c6;\n}\n.panel-info {\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n  color: #31708f;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n  color: #d9edf7;\n  background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #bce8f1;\n}\n.panel-warning {\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n  color: #fcf8e3;\n  background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #faebcc;\n}\n.panel-danger {\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n  color: #f2dede;\n  background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  height: 100%;\n  width: 100%;\n  border: 0;\n}\n.embed-responsive-16by9 {\n  padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n  padding-bottom: 75%;\n}\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border: 1px solid #e3e3e3;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n  border-color: #ddd;\n  border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n  padding: 24px;\n  border-radius: 6px;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: 3px;\n}\n.close {\n  float: right;\n  font-size: 21px;\n  font-weight: bold;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: 0.2;\n  filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n  color: #000;\n  text-decoration: none;\n  cursor: pointer;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\nbutton.close {\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  border: 0;\n  -webkit-appearance: none;\n}\n.modal-open {\n  overflow: hidden;\n}\n.modal {\n  display: none;\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1050;\n  -webkit-overflow-scrolling: touch;\n  outline: 0;\n}\n.modal.fade .modal-dialog {\n  -webkit-transform: translate(0, -25%);\n  -ms-transform: translate(0, -25%);\n  -o-transform: translate(0, -25%);\n  transform: translate(0, -25%);\n  -webkit-transition: -webkit-transform 0.3s ease-out;\n  -moz-transition: -moz-transform 0.3s ease-out;\n  -o-transition: -o-transform 0.3s ease-out;\n  transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n  -webkit-transform: translate(0, 0);\n  -ms-transform: translate(0, 0);\n  -o-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n.modal-content {\n  position: relative;\n  background-color: #fff;\n  border: 1px solid #999;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  background-clip: padding-box;\n  outline: 0;\n}\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1040;\n  background-color: #000;\n}\n.modal-backdrop.fade {\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n.modal-header {\n  padding: 15px;\n  border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n  margin-top: -2px;\n}\n.modal-title {\n  margin: 0;\n  line-height: 1.42857143;\n}\n.modal-body {\n  position: relative;\n  padding: 15px;\n}\n.modal-footer {\n  padding: 15px;\n  text-align: right;\n  border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n  margin-left: 5px;\n  margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n  margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n  margin-left: 0;\n}\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n@media (min-width: 768px) {\n  .modal-dialog {\n    width: 600px;\n    margin: 30px auto;\n  }\n  .modal-content {\n    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n  }\n  .modal-sm {\n    width: 300px;\n  }\n}\n@media (min-width: 992px) {\n  .modal-lg {\n    width: 900px;\n  }\n}\n.tooltip {\n  position: absolute;\n  z-index: 1070;\n  display: block;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-style: normal;\n  font-weight: normal;\n  letter-spacing: normal;\n  line-break: auto;\n  line-height: 1.42857143;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  white-space: normal;\n  word-break: normal;\n  word-spacing: normal;\n  word-wrap: normal;\n  font-size: 12px;\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n.tooltip.in {\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n.tooltip.top {\n  margin-top: -3px;\n  padding: 5px 0;\n}\n.tooltip.right {\n  margin-left: 3px;\n  padding: 0 5px;\n}\n.tooltip.bottom {\n  margin-top: 3px;\n  padding: 5px 0;\n}\n.tooltip.left {\n  margin-left: -3px;\n  padding: 0 5px;\n}\n.tooltip-inner {\n  max-width: 200px;\n  padding: 3px 8px;\n  color: #fff;\n  text-align: center;\n  background-color: #000;\n  border-radius: 4px;\n}\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n  bottom: 0;\n  right: 5px;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n  bottom: 0;\n  left: 5px;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-width: 5px 5px 5px 0;\n  border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-width: 5px 0 5px 5px;\n  border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n  top: 0;\n  right: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n  top: 0;\n  left: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000;\n}\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1060;\n  display: none;\n  max-width: 276px;\n  padding: 1px;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-style: normal;\n  font-weight: normal;\n  letter-spacing: normal;\n  line-break: auto;\n  line-height: 1.42857143;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  white-space: normal;\n  word-break: normal;\n  word-spacing: normal;\n  word-wrap: normal;\n  font-size: 14px;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n  margin-top: -10px;\n}\n.popover.right {\n  margin-left: 10px;\n}\n.popover.bottom {\n  margin-top: 10px;\n}\n.popover.left {\n  margin-left: -10px;\n}\n.popover-title {\n  margin: 0;\n  padding: 8px 14px;\n  font-size: 14px;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-radius: 5px 5px 0 0;\n}\n.popover-content {\n  padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.popover > .arrow {\n  border-width: 11px;\n}\n.popover > .arrow:after {\n  border-width: 10px;\n  content: \"\";\n}\n.popover.top > .arrow {\n  left: 50%;\n  margin-left: -11px;\n  border-bottom-width: 0;\n  border-top-color: #999999;\n  border-top-color: rgba(0, 0, 0, 0.25);\n  bottom: -11px;\n}\n.popover.top > .arrow:after {\n  content: \" \";\n  bottom: 1px;\n  margin-left: -10px;\n  border-bottom-width: 0;\n  border-top-color: #fff;\n}\n.popover.right > .arrow {\n  top: 50%;\n  left: -11px;\n  margin-top: -11px;\n  border-left-width: 0;\n  border-right-color: #999999;\n  border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n  content: \" \";\n  left: 1px;\n  bottom: -10px;\n  border-left-width: 0;\n  border-right-color: #fff;\n}\n.popover.bottom > .arrow {\n  left: 50%;\n  margin-left: -11px;\n  border-top-width: 0;\n  border-bottom-color: #999999;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n  top: -11px;\n}\n.popover.bottom > .arrow:after {\n  content: \" \";\n  top: 1px;\n  margin-left: -10px;\n  border-top-width: 0;\n  border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n  top: 50%;\n  right: -11px;\n  margin-top: -11px;\n  border-right-width: 0;\n  border-left-color: #999999;\n  border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n  content: \" \";\n  right: 1px;\n  border-right-width: 0;\n  border-left-color: #fff;\n  bottom: -10px;\n}\n.carousel {\n  position: relative;\n}\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n}\n.carousel-inner > .item {\n  display: none;\n  position: relative;\n  -webkit-transition: 0.6s ease-in-out left;\n  -o-transition: 0.6s ease-in-out left;\n  transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n  .carousel-inner > .item {\n    -webkit-transition: -webkit-transform 0.6s ease-in-out;\n    -moz-transition: -moz-transform 0.6s ease-in-out;\n    -o-transition: -o-transform 0.6s ease-in-out;\n    transition: transform 0.6s ease-in-out;\n    -webkit-backface-visibility: hidden;\n    -moz-backface-visibility: hidden;\n    backface-visibility: hidden;\n    -webkit-perspective: 1000px;\n    -moz-perspective: 1000px;\n    perspective: 1000px;\n  }\n  .carousel-inner > .item.next,\n  .carousel-inner > .item.active.right {\n    -webkit-transform: translate3d(100%, 0, 0);\n    transform: translate3d(100%, 0, 0);\n    left: 0;\n  }\n  .carousel-inner > .item.prev,\n  .carousel-inner > .item.active.left {\n    -webkit-transform: translate3d(-100%, 0, 0);\n    transform: translate3d(-100%, 0, 0);\n    left: 0;\n  }\n  .carousel-inner > .item.next.left,\n  .carousel-inner > .item.prev.right,\n  .carousel-inner > .item.active {\n    -webkit-transform: translate3d(0, 0, 0);\n    transform: translate3d(0, 0, 0);\n    left: 0;\n  }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  display: block;\n}\n.carousel-inner > .active {\n  left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.carousel-inner > .next {\n  left: 100%;\n}\n.carousel-inner > .prev {\n  left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n  left: 0;\n}\n.carousel-inner > .active.left {\n  left: -100%;\n}\n.carousel-inner > .active.right {\n  left: 100%;\n}\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: 15%;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n  font-size: 20px;\n  color: #fff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n  background-color: rgba(0, 0, 0, 0);\n}\n.carousel-control.left {\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n  left: auto;\n  right: 0;\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n  outline: 0;\n  color: #fff;\n  text-decoration: none;\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n  position: absolute;\n  top: 50%;\n  margin-top: -10px;\n  z-index: 5;\n  display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n  left: 50%;\n  margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n  right: 50%;\n  margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n  width: 20px;\n  height: 20px;\n  line-height: 1;\n  font-family: serif;\n}\n.carousel-control .icon-prev:before {\n  content: '\\2039';\n}\n.carousel-control .icon-next:before {\n  content: '\\203a';\n}\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n}\n.carousel-indicators li {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  margin: 1px;\n  text-indent: -999px;\n  border: 1px solid #fff;\n  border-radius: 10px;\n  cursor: pointer;\n  background-color: #000 \\9;\n  background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n  margin: 0;\n  width: 12px;\n  height: 12px;\n  background-color: #fff;\n}\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #fff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n  text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-prev,\n  .carousel-control .icon-next {\n    width: 30px;\n    height: 30px;\n    margin-top: -10px;\n    font-size: 30px;\n  }\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .icon-prev {\n    margin-left: -10px;\n  }\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-next {\n    margin-right: -10px;\n  }\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n  content: \" \";\n  display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n  clear: both;\n}\n.center-block {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n.hidden {\n  display: none !important;\n}\n.affix {\n  position: fixed;\n}\n@-ms-viewport {\n  width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n@media (max-width: 767px) {\n  .visible-xs {\n    display: block !important;\n  }\n  table.visible-xs {\n    display: table !important;\n  }\n  tr.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-xs,\n  td.visible-xs {\n    display: table-cell !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-block {\n    display: block !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline {\n    display: inline !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm {\n    display: block !important;\n  }\n  table.visible-sm {\n    display: table !important;\n  }\n  tr.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-sm,\n  td.visible-sm {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-block {\n    display: block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md {\n    display: block !important;\n  }\n  table.visible-md {\n    display: table !important;\n  }\n  tr.visible-md {\n    display: table-row !important;\n  }\n  th.visible-md,\n  td.visible-md {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-block {\n    display: block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg {\n    display: block !important;\n  }\n  table.visible-lg {\n    display: table !important;\n  }\n  tr.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-lg,\n  td.visible-lg {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-block {\n    display: block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (max-width: 767px) {\n  .hidden-xs {\n    display: none !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-sm {\n    display: none !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-md {\n    display: none !important;\n  }\n}\n@media (min-width: 1200px) {\n  .hidden-lg {\n    display: none !important;\n  }\n}\n.visible-print {\n  display: none !important;\n}\n@media print {\n  .visible-print {\n    display: block !important;\n  }\n  table.visible-print {\n    display: table !important;\n  }\n  tr.visible-print {\n    display: table-row !important;\n  }\n  th.visible-print,\n  td.visible-print {\n    display: table-cell !important;\n  }\n}\n.visible-print-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-block {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline-block {\n    display: inline-block !important;\n  }\n}\n@media print {\n  .hidden-print {\n    display: none !important;\n  }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n//    without disabling user zoom.\n//\n\nhtml {\n  font-family: sans-serif; // 1\n  -ms-text-size-adjust: 100%; // 2\n  -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n  margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; // 1\n  vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n  background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n  outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n  font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n  font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n  border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n  margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n  overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n//    Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; // 1\n  font: inherit; // 2\n  margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n  overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n//    and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n//    `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; // 2\n  cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n  line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; // 1\n  padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; // 1\n  box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n  border: 0; // 1\n  padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n  overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n  font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n    *,\n    *:before,\n    *:after {\n        background: transparent !important;\n        color: #000 !important; // Black prints faster: h5bp.com/s\n        box-shadow: none !important;\n        text-shadow: none !important;\n    }\n\n    a,\n    a:visited {\n        text-decoration: underline;\n    }\n\n    a[href]:after {\n        content: \" (\" attr(href) \")\";\n    }\n\n    abbr[title]:after {\n        content: \" (\" attr(title) \")\";\n    }\n\n    // Don't show links that are fragment identifiers,\n    // or use the `javascript:` pseudo protocol\n    a[href^=\"#\"]:after,\n    a[href^=\"javascript:\"]:after {\n        content: \"\";\n    }\n\n    pre,\n    blockquote {\n        border: 1px solid #999;\n        page-break-inside: avoid;\n    }\n\n    thead {\n        display: table-header-group; // h5bp.com/t\n    }\n\n    tr,\n    img {\n        page-break-inside: avoid;\n    }\n\n    img {\n        max-width: 100% !important;\n    }\n\n    p,\n    h2,\n    h3 {\n        orphans: 3;\n        widows: 3;\n    }\n\n    h2,\n    h3 {\n        page-break-after: avoid;\n    }\n\n    // Bootstrap specific changes start\n\n    // Bootstrap components\n    .navbar {\n        display: none;\n    }\n    .btn,\n    .dropup > .btn {\n        > .caret {\n            border-top-color: #000 !important;\n        }\n    }\n    .label {\n        border: 1px solid #000;\n    }\n\n    .table {\n        border-collapse: collapse !important;\n\n        td,\n        th {\n            background-color: #fff !important;\n        }\n    }\n    .table-bordered {\n        th,\n        td {\n            border: 1px solid #ddd !important;\n        }\n    }\n\n    // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// <a href=\"#\"><span class=\"glyphicon glyphicon-star\"></span> Star</a>\n\n// Import the fonts\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('@{icon-font-path}@{icon-font-name}.eot');\n  src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n       url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n       url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n       url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n       url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk               { &:before { content: \"\\002a\"; } }\n.glyphicon-plus                   { &:before { content: \"\\002b\"; } }\n.glyphicon-euro,\n.glyphicon-eur                    { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus                  { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud                  { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope               { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil                 { &:before { content: \"\\270f\"; } }\n.glyphicon-glass                  { &:before { content: \"\\e001\"; } }\n.glyphicon-music                  { &:before { content: \"\\e002\"; } }\n.glyphicon-search                 { &:before { content: \"\\e003\"; } }\n.glyphicon-heart                  { &:before { content: \"\\e005\"; } }\n.glyphicon-star                   { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty             { &:before { content: \"\\e007\"; } }\n.glyphicon-user                   { &:before { content: \"\\e008\"; } }\n.glyphicon-film                   { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large               { &:before { content: \"\\e010\"; } }\n.glyphicon-th                     { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list                { &:before { content: \"\\e012\"; } }\n.glyphicon-ok                     { &:before { content: \"\\e013\"; } }\n.glyphicon-remove                 { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in                { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out               { &:before { content: \"\\e016\"; } }\n.glyphicon-off                    { &:before { content: \"\\e017\"; } }\n.glyphicon-signal                 { &:before { content: \"\\e018\"; } }\n.glyphicon-cog                    { &:before { content: \"\\e019\"; } }\n.glyphicon-trash                  { &:before { content: \"\\e020\"; } }\n.glyphicon-home                   { &:before { content: \"\\e021\"; } }\n.glyphicon-file                   { &:before { content: \"\\e022\"; } }\n.glyphicon-time                   { &:before { content: \"\\e023\"; } }\n.glyphicon-road                   { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt           { &:before { content: \"\\e025\"; } }\n.glyphicon-download               { &:before { content: \"\\e026\"; } }\n.glyphicon-upload                 { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox                  { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle            { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat                 { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh                { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt               { &:before { content: \"\\e032\"; } }\n.glyphicon-lock                   { &:before { content: \"\\e033\"; } }\n.glyphicon-flag                   { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones             { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off             { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down            { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up              { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode                 { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode                { &:before { content: \"\\e040\"; } }\n.glyphicon-tag                    { &:before { content: \"\\e041\"; } }\n.glyphicon-tags                   { &:before { content: \"\\e042\"; } }\n.glyphicon-book                   { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark               { &:before { content: \"\\e044\"; } }\n.glyphicon-print                  { &:before { content: \"\\e045\"; } }\n.glyphicon-camera                 { &:before { content: \"\\e046\"; } }\n.glyphicon-font                   { &:before { content: \"\\e047\"; } }\n.glyphicon-bold                   { &:before { content: \"\\e048\"; } }\n.glyphicon-italic                 { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height            { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width             { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left             { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center           { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right            { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify          { &:before { content: \"\\e055\"; } }\n.glyphicon-list                   { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left            { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right           { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video         { &:before { content: \"\\e059\"; } }\n.glyphicon-picture                { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker             { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust                 { &:before { content: \"\\e063\"; } }\n.glyphicon-tint                   { &:before { content: \"\\e064\"; } }\n.glyphicon-edit                   { &:before { content: \"\\e065\"; } }\n.glyphicon-share                  { &:before { content: \"\\e066\"; } }\n.glyphicon-check                  { &:before { content: \"\\e067\"; } }\n.glyphicon-move                   { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward          { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward          { &:before { content: \"\\e070\"; } }\n.glyphicon-backward               { &:before { content: \"\\e071\"; } }\n.glyphicon-play                   { &:before { content: \"\\e072\"; } }\n.glyphicon-pause                  { &:before { content: \"\\e073\"; } }\n.glyphicon-stop                   { &:before { content: \"\\e074\"; } }\n.glyphicon-forward                { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward           { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward           { &:before { content: \"\\e077\"; } }\n.glyphicon-eject                  { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left           { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right          { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign              { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign             { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign            { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign                { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign          { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign              { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot             { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle          { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle              { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle             { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left             { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right            { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up               { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down             { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt              { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full            { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small           { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign       { &:before { content: \"\\e101\"; } }\n.glyphicon-gift                   { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf                   { &:before { content: \"\\e103\"; } }\n.glyphicon-fire                   { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open               { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close              { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign           { &:before { content: \"\\e107\"; } }\n.glyphicon-plane                  { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar               { &:before { content: \"\\e109\"; } }\n.glyphicon-random                 { &:before { content: \"\\e110\"; } }\n.glyphicon-comment                { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet                 { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up             { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down           { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet                { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart          { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close           { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open            { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical        { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal      { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd                    { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn               { &:before { content: \"\\e122\"; } }\n.glyphicon-bell                   { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate            { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up              { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down            { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right             { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left              { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up                { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down              { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right     { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left      { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up        { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down      { &:before { content: \"\\e134\"; } }\n.glyphicon-globe                  { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench                 { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks                  { &:before { content: \"\\e137\"; } }\n.glyphicon-filter                 { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase              { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen             { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard              { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip              { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty            { &:before { content: \"\\e143\"; } }\n.glyphicon-link                   { &:before { content: \"\\e144\"; } }\n.glyphicon-phone                  { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin                { &:before { content: \"\\e146\"; } }\n.glyphicon-usd                    { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp                    { &:before { content: \"\\e149\"; } }\n.glyphicon-sort                   { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet       { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt   { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order          { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt      { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes     { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked              { &:before { content: \"\\e157\"; } }\n.glyphicon-expand                 { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down          { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up            { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in                 { &:before { content: \"\\e161\"; } }\n.glyphicon-flash                  { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out                { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window             { &:before { content: \"\\e164\"; } }\n.glyphicon-record                 { &:before { content: \"\\e165\"; } }\n.glyphicon-save                   { &:before { content: \"\\e166\"; } }\n.glyphicon-open                   { &:before { content: \"\\e167\"; } }\n.glyphicon-saved                  { &:before { content: \"\\e168\"; } }\n.glyphicon-import                 { &:before { content: \"\\e169\"; } }\n.glyphicon-export                 { &:before { content: \"\\e170\"; } }\n.glyphicon-send                   { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk            { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved           { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove          { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save            { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open            { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card            { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer               { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery                { &:before { content: \"\\e179\"; } }\n.glyphicon-header                 { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed             { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone               { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt              { &:before { content: \"\\e183\"; } }\n.glyphicon-tower                  { &:before { content: \"\\e184\"; } }\n.glyphicon-stats                  { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video               { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video               { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles              { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo           { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby            { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1              { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1              { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1              { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark         { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark      { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download         { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload           { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer           { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous         { &:before { content: \"\\e200\"; } }\n.glyphicon-cd                     { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file              { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file              { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up               { &:before { content: \"\\e204\"; } }\n.glyphicon-copy                   { &:before { content: \"\\e205\"; } }\n.glyphicon-paste                  { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door                   { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key                    { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert                  { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer              { &:before { content: \"\\e210\"; } }\n.glyphicon-king                   { &:before { content: \"\\e211\"; } }\n.glyphicon-queen                  { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn                   { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop                 { &:before { content: \"\\e214\"; } }\n.glyphicon-knight                 { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula           { &:before { content: \"\\e216\"; } }\n.glyphicon-tent                   { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard             { &:before { content: \"\\e218\"; } }\n.glyphicon-bed                    { &:before { content: \"\\e219\"; } }\n.glyphicon-apple                  { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase                  { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass              { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp                   { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate              { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank             { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors               { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin                { &:before { content: \"\\e227\"; } }\n.glyphicon-btc                    { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt                    { &:before { content: \"\\e227\"; } }\n.glyphicon-yen                    { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy                    { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble                  { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub                    { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale                  { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly              { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted       { &:before { content: \"\\e232\"; } }\n.glyphicon-education              { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal      { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical        { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger         { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window           { &:before { content: \"\\e237\"; } }\n.glyphicon-oil                    { &:before { content: \"\\e238\"; } }\n.glyphicon-grain                  { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses             { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size              { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color             { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background        { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top       { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom    { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left      { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical  { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right     { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right         { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left          { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom        { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top           { &:before { content: \"\\e253\"; } }\n.glyphicon-console                { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript            { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript              { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left              { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right             { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down              { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up                { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n  .box-sizing(border-box);\n}\n*:before,\n*:after {\n  .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n  font-size: 10px;\n  -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n  font-family: @font-family-base;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @text-color;\n  background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\n\n// Links\n\na {\n  color: @link-color;\n  text-decoration: none;\n\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: @link-hover-decoration;\n  }\n\n  &:focus {\n    .tab-focus();\n  }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n  margin: 0;\n}\n\n\n// Images\n\nimg {\n  vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n  .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n  border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n  padding: @thumbnail-padding;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(all .2s ease-in-out);\n\n  // Keep them at most 100% wide\n  .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n  border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n  margin-top:    @line-height-computed;\n  margin-bottom: @line-height-computed;\n  border: 0;\n  border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0,0,0,0);\n  border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n  &:active,\n  &:focus {\n    position: static;\n    width: auto;\n    height: auto;\n    margin: 0;\n    overflow: visible;\n    clip: auto;\n  }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n  cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n       -o-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n  -webkit-animation-fill-mode: @fill-mode;\n          animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  // Firefox\n  &::-moz-placeholder {\n    color: @color;\n    opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n  }\n  &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n  -webkit-transform: scale(@ratio);\n      -ms-transform: scale(@ratio); // IE9 only\n       -o-transform: scale(@ratio);\n          transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n  -webkit-transform: scale(@ratioX, @ratioY);\n      -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n       -o-transform: scale(@ratioX, @ratioY);\n          transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n  -webkit-transform: scaleX(@ratio);\n      -ms-transform: scaleX(@ratio); // IE9 only\n       -o-transform: scaleX(@ratio);\n          transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n  -webkit-transform: scaleY(@ratio);\n      -ms-transform: scaleY(@ratio); // IE9 only\n       -o-transform: scaleY(@ratio);\n          transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n  -webkit-transform: skewX(@x) skewY(@y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n       -o-transform: skewX(@x) skewY(@y);\n          transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n       -o-transform: translate(@x, @y);\n          transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n       -o-transform: rotate(@degrees);\n          transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n       -o-transform: rotateX(@degrees);\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n       -o-transform: rotateY(@degrees);\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n  -webkit-transition: @transition;\n       -o-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n  -webkit-transition-timing-function: @timing-function;\n          transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n  // Default\n  outline: thin dotted;\n  // WebKit\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n  display: @display;\n  max-width: 100%; // Part 1: Set a maximum relative to the parent\n  height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n  background-image: url(\"@{file-1x}\");\n\n  @media\n  only screen and (-webkit-min-device-pixel-ratio: 2),\n  only screen and (   min--moz-device-pixel-ratio: 2),\n  only screen and (     -o-min-device-pixel-ratio: 2/1),\n  only screen and (        min-device-pixel-ratio: 2),\n  only screen and (                min-resolution: 192dpi),\n  only screen and (                min-resolution: 2dppx) {\n    background-image: url(\"@{file-2x}\");\n    background-size: @width-1x @height-1x;\n  }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  font-family: @headings-font-family;\n  font-weight: @headings-font-weight;\n  line-height: @headings-line-height;\n  color: @headings-color;\n\n  small,\n  .small {\n    font-weight: normal;\n    line-height: 1;\n    color: @headings-small-color;\n  }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n  margin-top: @line-height-computed;\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 65%;\n  }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n  margin-top: (@line-height-computed / 2);\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 75%;\n  }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n  margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n  margin-bottom: @line-height-computed;\n  font-size: floor((@font-size-base * 1.15));\n  font-weight: 300;\n  line-height: 1.4;\n\n  @media (min-width: @screen-sm-min) {\n    font-size: (@font-size-base * 1.5);\n  }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n  font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n  background-color: @state-warning-bg;\n  padding: .2em;\n}\n\n// Alignment\n.text-left           { text-align: left; }\n.text-right          { text-align: right; }\n.text-center         { text-align: center; }\n.text-justify        { text-align: justify; }\n.text-nowrap         { white-space: nowrap; }\n\n// Transformation\n.text-lowercase      { text-transform: lowercase; }\n.text-uppercase      { text-transform: uppercase; }\n.text-capitalize     { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n  color: @text-muted;\n}\n.text-primary {\n  .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n  .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n  .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n  .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n  .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n  // Given the contrast here, this is the only class to have its color inverted\n  // automatically.\n  color: #fff;\n  .bg-variant(@brand-primary);\n}\n.bg-success {\n  .bg-variant(@state-success-bg);\n}\n.bg-info {\n  .bg-variant(@state-info-bg);\n}\n.bg-warning {\n  .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n  .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n  padding-bottom: ((@line-height-computed / 2) - 1);\n  margin: (@line-height-computed * 2) 0 @line-height-computed;\n  border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n  margin-top: 0;\n  margin-bottom: (@line-height-computed / 2);\n  ul,\n  ol {\n    margin-bottom: 0;\n  }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n  .list-unstyled();\n  margin-left: -5px;\n\n  > li {\n    display: inline-block;\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n// Description Lists\ndl {\n  margin-top: 0; // Remove browser default\n  margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n  line-height: @line-height-base;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n  dd {\n    &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n  }\n\n  @media (min-width: @dl-horizontal-breakpoint) {\n    dt {\n      float: left;\n      width: (@dl-horizontal-offset - 20);\n      clear: left;\n      text-align: right;\n      .text-overflow();\n    }\n    dd {\n      margin-left: @dl-horizontal-offset;\n    }\n  }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n  font-size: 90%;\n  .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n  padding: (@line-height-computed / 2) @line-height-computed;\n  margin: 0 0 @line-height-computed;\n  font-size: @blockquote-font-size;\n  border-left: 5px solid @blockquote-border-color;\n\n  p,\n  ul,\n  ol {\n    &:last-child {\n      margin-bottom: 0;\n    }\n  }\n\n  // Note: Deprecated small and .small as of v3.1.0\n  // Context: https://github.com/twbs/bootstrap/issues/11660\n  footer,\n  small,\n  .small {\n    display: block;\n    font-size: 80%; // back to default font-size\n    line-height: @line-height-base;\n    color: @blockquote-small-color;\n\n    &:before {\n      content: '\\2014 \\00A0'; // em dash, nbsp\n    }\n  }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid @blockquote-border-color;\n  border-left: 0;\n  text-align: right;\n\n  // Account for citation\n  footer,\n  small,\n  .small {\n    &:before { content: ''; }\n    &:after {\n      content: '\\00A0 \\2014'; // nbsp, em dash\n    }\n  }\n}\n\n// Addresses\naddress {\n  margin-bottom: @line-height-computed;\n  font-style: normal;\n  line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n  color: @color;\n  a&:hover,\n  a&:focus {\n    color: darken(@color, 10%);\n  }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n  background-color: @color;\n  a&:hover,\n  a&:focus {\n    background-color: darken(@color, 10%);\n  }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n  font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @code-color;\n  background-color: @code-bg;\n  border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @kbd-color;\n  background-color: @kbd-bg;\n  border-radius: @border-radius-small;\n  box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n  kbd {\n    padding: 0;\n    font-size: 100%;\n    font-weight: bold;\n    box-shadow: none;\n  }\n}\n\n// Blocks of code\npre {\n  display: block;\n  padding: ((@line-height-computed - 1) / 2);\n  margin: 0 0 (@line-height-computed / 2);\n  font-size: (@font-size-base - 1); // 14px to 13px\n  line-height: @line-height-base;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: @pre-color;\n  background-color: @pre-bg;\n  border: 1px solid @pre-border-color;\n  border-radius: @border-radius-base;\n\n  // Account for some code outputs that place code tags in pre tags\n  code {\n    padding: 0;\n    font-size: inherit;\n    color: inherit;\n    white-space: pre-wrap;\n    background-color: transparent;\n    border-radius: 0;\n  }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n  max-height: @pre-scrollable-max-height;\n  overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n  .container-fixed();\n\n  @media (min-width: @screen-sm-min) {\n    width: @container-sm;\n  }\n  @media (min-width: @screen-md-min) {\n    width: @container-md;\n  }\n  @media (min-width: @screen-lg-min) {\n    width: @container-lg;\n  }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n  .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n  .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n  .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n  .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n  .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left:  floor((@gutter / 2));\n  padding-right: ceil((@gutter / 2));\n  &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n  margin-left:  ceil((@gutter / -2));\n  margin-right: floor((@gutter / -2));\n  &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  float: left;\n  width: percentage((@columns / @grid-columns));\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n  margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n  left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n  right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-sm-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-offset(@columns) {\n  @media (min-width: @screen-sm-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-push(@columns) {\n  @media (min-width: @screen-sm-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-pull(@columns) {\n  @media (min-width: @screen-sm-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-md-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-offset(@columns) {\n  @media (min-width: @screen-md-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-push(@columns) {\n  @media (min-width: @screen-md-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-pull(@columns) {\n  @media (min-width: @screen-md-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-lg-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-offset(@columns) {\n  @media (min-width: @screen-lg-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-push(@columns) {\n  @media (min-width: @screen-lg-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-pull(@columns) {\n  @media (min-width: @screen-lg-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n  // Common styles for all sizes of grid columns, widths 1-12\n  .col(@index) { // initial\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      position: relative;\n      // Prevent columns from collapsing when empty\n      min-height: 1px;\n      // Inner gutter via padding\n      padding-left:  ceil((@grid-gutter-width / 2));\n      padding-right: floor((@grid-gutter-width / 2));\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n  .col(@index) { // initial\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      float: left;\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n  .col-@{class}-@{index} {\n    width: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n  .col-@{class}-push-@{index} {\n    left: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n  .col-@{class}-push-0 {\n    left: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n  .col-@{class}-pull-@{index} {\n    right: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n  .col-@{class}-pull-0 {\n    right: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n  .col-@{class}-offset-@{index} {\n    margin-left: percentage((@index / @grid-columns));\n  }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n  .calc-grid-column(@index, @class, @type);\n  // next iteration\n  .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n  .float-grid-columns(@class);\n  .loop-grid-columns(@grid-columns, @class, width);\n  .loop-grid-columns(@grid-columns, @class, pull);\n  .loop-grid-columns(@grid-columns, @class, push);\n  .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n  background-color: @table-bg;\n}\ncaption {\n  padding-top: @table-cell-padding;\n  padding-bottom: @table-cell-padding;\n  color: @text-muted;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: @line-height-computed;\n  // Cells\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-cell-padding;\n        line-height: @line-height-base;\n        vertical-align: top;\n        border-top: 1px solid @table-border-color;\n      }\n    }\n  }\n  // Bottom align for column headings\n  > thead > tr > th {\n    vertical-align: bottom;\n    border-bottom: 2px solid @table-border-color;\n  }\n  // Remove top border from thead by default\n  > caption + thead,\n  > colgroup + thead,\n  > thead:first-child {\n    > tr:first-child {\n      > th,\n      > td {\n        border-top: 0;\n      }\n    }\n  }\n  // Account for multiple tbody instances\n  > tbody + tbody {\n    border-top: 2px solid @table-border-color;\n  }\n\n  // Nesting\n  .table {\n    background-color: @body-bg;\n  }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-condensed-cell-padding;\n      }\n    }\n  }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n  border: 1px solid @table-border-color;\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        border: 1px solid @table-border-color;\n      }\n    }\n  }\n  > thead > tr {\n    > th,\n    > td {\n      border-bottom-width: 2px;\n    }\n  }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n  > tbody > tr:nth-of-type(odd) {\n    background-color: @table-bg-accent;\n  }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n  > tbody > tr:hover {\n    background-color: @table-bg-hover;\n  }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n  position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n  float: none;\n  display: table-column;\n}\ntable {\n  td,\n  th {\n    &[class*=\"col-\"] {\n      position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n      float: none;\n      display: table-cell;\n    }\n  }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n  overflow-x: auto;\n  min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n  @media screen and (max-width: @screen-xs-max) {\n    width: 100%;\n    margin-bottom: (@line-height-computed * 0.75);\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid @table-border-color;\n\n    // Tighten up spacing\n    > .table {\n      margin-bottom: 0;\n\n      // Ensure the content doesn't wrap\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th,\n          > td {\n            white-space: nowrap;\n          }\n        }\n      }\n    }\n\n    // Special overrides for the bordered tables\n    > .table-bordered {\n      border: 0;\n\n      // Nuke the appropriate borders so that the parent can handle them\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th:first-child,\n          > td:first-child {\n            border-left: 0;\n          }\n          > th:last-child,\n          > td:last-child {\n            border-right: 0;\n          }\n        }\n      }\n\n      // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n      // chances are there will be only one `tr` in a `thead` and that would\n      // remove the border altogether.\n      > tbody,\n      > tfoot {\n        > tr:last-child {\n          > th,\n          > td {\n            border-bottom: 0;\n          }\n        }\n      }\n\n    }\n  }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n  // Exact selectors below required to override `.table-striped` and prevent\n  // inheritance to nested tables.\n  .table > thead > tr,\n  .table > tbody > tr,\n  .table > tfoot > tr {\n    > td.@{state},\n    > th.@{state},\n    &.@{state} > td,\n    &.@{state} > th {\n      background-color: @background;\n    }\n  }\n\n  // Hover states for `.table-hover`\n  // Note: this is not available for cells or rows within `thead` or `tfoot`.\n  .table-hover > tbody > tr {\n    > td.@{state}:hover,\n    > th.@{state}:hover,\n    &.@{state}:hover > td,\n    &:hover > .@{state},\n    &.@{state}:hover > th {\n      background-color: darken(@background, 5%);\n    }\n  }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n  // so we reset that to ensure it behaves more like a standard block element.\n  // See https://github.com/twbs/bootstrap/issues/12359.\n  min-width: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: @line-height-computed;\n  font-size: (@font-size-base * 1.5);\n  line-height: inherit;\n  color: @legend-color;\n  border: 0;\n  border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n  display: inline-block;\n  max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n  .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9; // IE8-9\n  line-height: normal;\n}\n\ninput[type=\"file\"] {\n  display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  .tab-focus();\n}\n\n// Adjust output element\noutput {\n  display: block;\n  padding-top: (@padding-base-vertical + 1);\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n  background-color: @input-bg;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid @input-border;\n  border-radius: @input-border-radius; // Note: This has no effect on <select>s in some browsers, due to the limited stylability of <select>s in CSS.\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n  .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n  // Customize the `:focus` state to imitate native WebKit styles.\n  .form-control-focus();\n\n  // Placeholder\n  .placeholder();\n\n  // Unstyle the caret on `<select>`s in IE10+.\n  &::-ms-expand {\n    border: 0;\n    background-color: transparent;\n  }\n\n  // Disabled and read-only inputs\n  //\n  // HTML5 says that controls under a fieldset > legend:first-child won't be\n  // disabled if the fieldset is disabled. Due to implementation difficulty, we\n  // don't honor that edge case; we style them as disabled anyway.\n  &[disabled],\n  &[readonly],\n  fieldset[disabled] & {\n    background-color: @input-bg-disabled;\n    opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n  }\n\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n\n  // Reset height for `textarea`s\n  textarea& {\n    height: auto;\n  }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n//\n// Note that as of 8.3, iOS doesn't support `datetime` or `week`.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"],\n  input[type=\"time\"],\n  input[type=\"datetime-local\"],\n  input[type=\"month\"] {\n    &.form-control {\n      line-height: @input-height-base;\n    }\n\n    &.input-sm,\n    .input-group-sm & {\n      line-height: @input-height-small;\n    }\n\n    &.input-lg,\n    .input-group-lg & {\n      line-height: @input-height-large;\n    }\n  }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n  margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n\n  label {\n    min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n    padding-left: 20px;\n    margin-bottom: 0;\n    font-weight: normal;\n    cursor: pointer;\n  }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-left: -20px;\n  margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n  position: relative;\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because <label>s don't inherit their parent's `cursor`.\n//\n// Note: Neither radios nor checkboxes can be readonly.\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  &[disabled],\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used directly on <label>s\n.radio-inline,\n.checkbox-inline {\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used on elements with <label> descendants\n.radio,\n.checkbox {\n  &.disabled,\n  fieldset[disabled] & {\n    label {\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n\n// Static form control text\n//\n// Apply class to a `p` element to make any string of text align with labels in\n// a horizontal form layout.\n\n.form-control-static {\n  // Size it appropriately next to real form controls\n  padding-top: (@padding-base-vertical + 1);\n  padding-bottom: (@padding-base-vertical + 1);\n  // Remove default margin from `p`\n  margin-bottom: 0;\n  min-height: (@line-height-computed + @font-size-base);\n\n  &.input-lg,\n  &.input-sm {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n\n\n// Form control sizing\n//\n// Build on `.form-control` with modifier classes to decrease or increase the\n// height and font-size of form controls.\n//\n// The `.form-group-* form-control` variations are sadly duplicated to avoid the\n// issue documented in https://github.com/twbs/bootstrap/issues/15074.\n\n.input-sm {\n  .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);\n}\n.form-group-sm {\n  .form-control {\n    height: @input-height-small;\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    line-height: @line-height-small;\n    border-radius: @input-border-radius-small;\n  }\n  select.form-control {\n    height: @input-height-small;\n    line-height: @input-height-small;\n  }\n  textarea.form-control,\n  select[multiple].form-control {\n    height: auto;\n  }\n  .form-control-static {\n    height: @input-height-small;\n    min-height: (@line-height-computed + @font-size-small);\n    padding: (@padding-small-vertical + 1) @padding-small-horizontal;\n    font-size: @font-size-small;\n    line-height: @line-height-small;\n  }\n}\n\n.input-lg {\n  .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);\n}\n.form-group-lg {\n  .form-control {\n    height: @input-height-large;\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    line-height: @line-height-large;\n    border-radius: @input-border-radius-large;\n  }\n  select.form-control {\n    height: @input-height-large;\n    line-height: @input-height-large;\n  }\n  textarea.form-control,\n  select[multiple].form-control {\n    height: auto;\n  }\n  .form-control-static {\n    height: @input-height-large;\n    min-height: (@line-height-computed + @font-size-large);\n    padding: (@padding-large-vertical + 1) @padding-large-horizontal;\n    font-size: @font-size-large;\n    line-height: @line-height-large;\n  }\n}\n\n\n// Form control feedback states\n//\n// Apply contextual and semantic states to individual form controls.\n\n.has-feedback {\n  // Enable absolute positioning\n  position: relative;\n\n  // Ensure icons don't overlap text\n  .form-control {\n    padding-right: (@input-height-base * 1.25);\n  }\n}\n// Feedback icon (requires .glyphicon classes)\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2; // Ensure icon is above input groups\n  display: block;\n  width: @input-height-base;\n  height: @input-height-base;\n  line-height: @input-height-base;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n  width: @input-height-large;\n  height: @input-height-large;\n  line-height: @input-height-large;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n  width: @input-height-small;\n  height: @input-height-small;\n  line-height: @input-height-small;\n}\n\n// Feedback states\n.has-success {\n  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);\n}\n.has-warning {\n  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);\n}\n.has-error {\n  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);\n}\n\n// Reposition feedback icon if input has visible label above\n.has-feedback label {\n\n  & ~ .form-control-feedback {\n    top: (@line-height-computed + 5); // Height of the `label` and its margin\n  }\n  &.sr-only ~ .form-control-feedback {\n    top: 0;\n  }\n}\n\n\n// Help text\n//\n// Apply to any element you wish to create light text for placement immediately\n// below a form control. Use for general help, formatting, or instructional text.\n\n.help-block {\n  display: block; // account for any element using help-block\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: lighten(@text-color, 25%); // lighten the text some for contrast\n}\n\n\n// Inline forms\n//\n// Make forms appear inline(-block) by adding the `.form-inline` class. Inline\n// forms begin stacked on extra small (mobile) devices and then go inline when\n// viewports reach <768px.\n//\n// Requires wrapping inputs and labels with `.form-group` for proper display of\n// default HTML form controls and our custom form controls (e.g., input groups).\n//\n// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.\n\n.form-inline {\n\n  // Kick in the inline\n  @media (min-width: @screen-sm-min) {\n    // Inline-block all the things for \"inline\"\n    .form-group {\n      display: inline-block;\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // In navbar-form, allow folks to *not* use `.form-group`\n    .form-control {\n      display: inline-block;\n      width: auto; // Prevent labels from stacking above inputs in `.form-group`\n      vertical-align: middle;\n    }\n\n    // Make static controls behave like regular ones\n    .form-control-static {\n      display: inline-block;\n    }\n\n    .input-group {\n      display: inline-table;\n      vertical-align: middle;\n\n      .input-group-addon,\n      .input-group-btn,\n      .form-control {\n        width: auto;\n      }\n    }\n\n    // Input groups need that 100% width though\n    .input-group > .form-control {\n      width: 100%;\n    }\n\n    .control-label {\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // Remove default margin on radios/checkboxes that were used for stacking, and\n    // then undo the floating of radios and checkboxes to match.\n    .radio,\n    .checkbox {\n      display: inline-block;\n      margin-top: 0;\n      margin-bottom: 0;\n      vertical-align: middle;\n\n      label {\n        padding-left: 0;\n      }\n    }\n    .radio input[type=\"radio\"],\n    .checkbox input[type=\"checkbox\"] {\n      position: relative;\n      margin-left: 0;\n    }\n\n    // Re-override the feedback icon.\n    .has-feedback .form-control-feedback {\n      top: 0;\n    }\n  }\n}\n\n\n// Horizontal forms\n//\n// Horizontal forms are built on grid classes and allow you to create forms with\n// labels on the left and inputs on the right.\n\n.form-horizontal {\n\n  // Consistent vertical alignment of radios and checkboxes\n  //\n  // Labels also get some reset styles, but that is scoped to a media query below.\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline {\n    margin-top: 0;\n    margin-bottom: 0;\n    padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n  }\n  // Account for padding we're adding to ensure the alignment and of help text\n  // and other content below items\n  .radio,\n  .checkbox {\n    min-height: (@line-height-computed + (@padding-base-vertical + 1));\n  }\n\n  // Make form groups behave like rows\n  .form-group {\n    .make-row();\n  }\n\n  // Reset spacing and right align labels, but scope to media queries so that\n  // labels on narrow viewports stack the same as a default form example.\n  @media (min-width: @screen-sm-min) {\n    .control-label {\n      text-align: right;\n      margin-bottom: 0;\n      padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n    }\n  }\n\n  // Validation states\n  //\n  // Reposition the icon because it's now within a grid column and columns have\n  // `position: relative;` on them. Also accounts for the grid gutter padding.\n  .has-feedback .form-control-feedback {\n    right: floor((@grid-gutter-width / 2));\n  }\n\n  // Form group sizes\n  //\n  // Quick utility class for applying `.input-lg` and `.input-sm` styles to the\n  // inputs and labels within a `.form-group`.\n  .form-group-lg {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: (@padding-large-vertical + 1);\n        font-size: @font-size-large;\n      }\n    }\n  }\n  .form-group-sm {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: (@padding-small-vertical + 1);\n        font-size: @font-size-small;\n      }\n    }\n  }\n}\n","// Form validation states\n//\n// Used in forms.less to generate the form validation CSS for warnings, errors,\n// and successes.\n\n.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {\n  // Color the label and help text\n  .help-block,\n  .control-label,\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline,\n  &.radio label,\n  &.checkbox label,\n  &.radio-inline label,\n  &.checkbox-inline label  {\n    color: @text-color;\n  }\n  // Set the border and box shadow on specific inputs to match\n  .form-control {\n    border-color: @border-color;\n    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work\n    &:focus {\n      border-color: darken(@border-color, 10%);\n      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);\n      .box-shadow(@shadow);\n    }\n  }\n  // Set validation states also for addons\n  .input-group-addon {\n    color: @text-color;\n    border-color: @border-color;\n    background-color: @background-color;\n  }\n  // Optional feedback icon\n  .form-control-feedback {\n    color: @text-color;\n  }\n}\n\n\n// Form control focus state\n//\n// Generate a customized focus state and for any input with the specified color,\n// which defaults to the `@input-border-focus` variable.\n//\n// We highly encourage you to not customize the default value, but instead use\n// this to tweak colors on an as-needed basis. This aesthetic change is based on\n// WebKit's default styles, but applicable to a wider range of browsers. Its\n// usability and accessibility should be taken into account with any change.\n//\n// Example usage: change the default blue border and shadow to white for better\n// contrast against a dark gray background.\n.form-control-focus(@color: @input-border-focus) {\n  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);\n  &:focus {\n    border-color: @color;\n    outline: 0;\n    .box-shadow(~\"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}\");\n  }\n}\n\n// Form control sizing\n//\n// Relative text size, padding, and border-radii changes for form controls. For\n// horizontal sizing, wrap controls in the predefined grid classes. `<select>`\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  height: @input-height;\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n\n  select& {\n    height: @input-height;\n    line-height: @input-height;\n  }\n\n  textarea&,\n  select[multiple]& {\n    height: auto;\n  }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n  display: inline-block;\n  margin-bottom: 0; // For input.btn\n  font-weight: @btn-font-weight;\n  text-align: center;\n  vertical-align: middle;\n  touch-action: manipulation;\n  cursor: pointer;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  white-space: nowrap;\n  .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n  .user-select(none);\n\n  &,\n  &:active,\n  &.active {\n    &:focus,\n    &.focus {\n      .tab-focus();\n    }\n  }\n\n  &:hover,\n  &:focus,\n  &.focus {\n    color: @btn-default-color;\n    text-decoration: none;\n  }\n\n  &:active,\n  &.active {\n    outline: 0;\n    background-image: none;\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n    .opacity(.65);\n    .box-shadow(none);\n  }\n\n  a& {\n    &.disabled,\n    fieldset[disabled] & {\n      pointer-events: none; // Future-proof disabling of clicks on `<a>` elements\n    }\n  }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n  .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n  .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n  .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n  .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n  .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n  .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n  color: @link-color;\n  font-weight: normal;\n  border-radius: 0;\n\n  &,\n  &:active,\n  &.active,\n  &[disabled],\n  fieldset[disabled] & {\n    background-color: transparent;\n    .box-shadow(none);\n  }\n  &,\n  &:hover,\n  &:focus,\n  &:active {\n    border-color: transparent;\n  }\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: @link-hover-decoration;\n    background-color: transparent;\n  }\n  &[disabled],\n  fieldset[disabled] & {\n    &:hover,\n    &:focus {\n      color: @btn-link-disabled-color;\n      text-decoration: none;\n    }\n  }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n  // line-height: ensure even-numbered height of button next to large input\n  .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n  // line-height: ensure proper height of button next to small input\n  .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n  .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n  display: block;\n  width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n  &.btn-block {\n    width: 100%;\n  }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n  color: @color;\n  background-color: @background;\n  border-color: @border;\n\n  &:focus,\n  &.focus {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 25%);\n  }\n  &:hover {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 12%);\n  }\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 12%);\n\n    &:hover,\n    &:focus,\n    &.focus {\n      color: @color;\n      background-color: darken(@background, 17%);\n          border-color: darken(@border, 25%);\n    }\n  }\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    background-image: none;\n  }\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &:hover,\n    &:focus,\n    &.focus {\n      background-color: @background;\n          border-color: @border;\n    }\n  }\n\n  .badge {\n    color: @background;\n    background-color: @color;\n  }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n  opacity: @opacity;\n  // IE8 filter\n  @opacity-ie: (@opacity * 100);\n  filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n  opacity: 0;\n  .transition(opacity .15s linear);\n  &.in {\n    opacity: 1;\n  }\n}\n\n.collapse {\n  display: none;\n\n  &.in      { display: block; }\n  tr&.in    { display: table-row; }\n  tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  .transition-property(~\"height, visibility\");\n  .transition-duration(.35s);\n  .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top:   @caret-width-base dashed;\n  border-top:   @caret-width-base solid ~\"\\9\"; // IE8\n  border-right: @caret-width-base solid transparent;\n  border-left:  @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n  position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n  outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: @zindex-dropdown;\n  display: none; // none by default, but block on \"open\" of the menu\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0; // override default ul\n  list-style: none;\n  font-size: @font-size-base;\n  text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n  background-color: @dropdown-bg;\n  border: 1px solid @dropdown-fallback-border; // IE8 fallback\n  border: 1px solid @dropdown-border;\n  border-radius: @border-radius-base;\n  .box-shadow(0 6px 12px rgba(0,0,0,.175));\n  background-clip: padding-box;\n\n  // Aligns the dropdown menu to right\n  //\n  // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n  &.pull-right {\n    right: 0;\n    left: auto;\n  }\n\n  // Dividers (basically an hr) within the dropdown\n  .divider {\n    .nav-divider(@dropdown-divider-bg);\n  }\n\n  // Links within the dropdown menu\n  > li > a {\n    display: block;\n    padding: 3px 20px;\n    clear: both;\n    font-weight: normal;\n    line-height: @line-height-base;\n    color: @dropdown-link-color;\n    white-space: nowrap; // prevent links from randomly breaking onto new lines\n  }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @dropdown-link-hover-color;\n    background-color: @dropdown-link-hover-bg;\n  }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-active-color;\n    text-decoration: none;\n    outline: 0;\n    background-color: @dropdown-link-active-bg;\n  }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-disabled-color;\n  }\n\n  // Nuke hover/focus effects\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    background-color: transparent;\n    background-image: none; // Remove CSS gradient\n    .reset-filter();\n    cursor: @cursor-disabled;\n  }\n}\n\n// Open state for the dropdown\n.open {\n  // Show the menu\n  > .dropdown-menu {\n    display: block;\n  }\n\n  // Remove the outline when :focus is triggered\n  > a {\n    outline: 0;\n  }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n  left: auto; // Reset the default from `.dropdown-menu`\n  right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: @font-size-small;\n  line-height: @line-height-base;\n  color: @dropdown-header-color;\n  white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n  // Reverse the caret\n  .caret {\n    border-top: 0;\n    border-bottom: @caret-width-base dashed;\n    border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n    content: \"\";\n  }\n  // Different positioning for bottom up menu\n  .dropdown-menu {\n    top: auto;\n    bottom: 100%;\n    margin-bottom: 2px;\n  }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-right {\n    .dropdown-menu {\n      .dropdown-menu-right();\n    }\n    // Necessary for overrides of the default right aligned menu.\n    // Will remove come v4 in all likelihood.\n    .dropdown-menu-left {\n      .dropdown-menu-left();\n    }\n  }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n  height: 1px;\n  margin: ((@line-height-computed / 2) - 1) 0;\n  overflow: hidden;\n  background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle; // match .btn alignment given font-size hack above\n  > .btn {\n    position: relative;\n    float: left;\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active,\n    &.active {\n      z-index: 2;\n    }\n  }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n  .btn + .btn,\n  .btn + .btn-group,\n  .btn-group + .btn,\n  .btn-group + .btn-group {\n    margin-left: -1px;\n  }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n  margin-left: -5px; // Offset the first child's margin\n  &:extend(.clearfix all);\n\n  .btn,\n  .btn-group,\n  .input-group {\n    float: left;\n  }\n  > .btn,\n  > .btn-group,\n  > .input-group {\n    margin-left: 5px;\n  }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n  margin-left: 0;\n  &:not(:last-child):not(.dropdown-toggle) {\n    .border-right-radius(0);\n  }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-right-radius(0);\n  }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n  .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n  // Show no shadow for `.btn-link` since it has no other button styles.\n  &.btn-link {\n    .box-shadow(none);\n  }\n}\n\n\n// Reposition the caret\n.btn .caret {\n  margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n  border-width: @caret-width-large @caret-width-large 0;\n  border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n  border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n  > .btn,\n  > .btn-group,\n  > .btn-group > .btn {\n    display: block;\n    float: none;\n    width: 100%;\n    max-width: 100%;\n  }\n\n  // Clear floats so dropdown menus can be properly placed\n  > .btn-group {\n    &:extend(.clearfix all);\n    > .btn {\n      float: none;\n    }\n  }\n\n  > .btn + .btn,\n  > .btn + .btn-group,\n  > .btn-group + .btn,\n  > .btn-group + .btn-group {\n    margin-top: -1px;\n    margin-left: 0;\n  }\n}\n\n.btn-group-vertical > .btn {\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n  &:first-child:not(:last-child) {\n    .border-top-radius(@btn-border-radius-base);\n    .border-bottom-radius(0);\n  }\n  &:last-child:not(:first-child) {\n    .border-top-radius(0);\n    .border-bottom-radius(@btn-border-radius-base);\n  }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-bottom-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n  > .btn,\n  > .btn-group {\n    float: none;\n    display: table-cell;\n    width: 1%;\n  }\n  > .btn-group .btn {\n    width: 100%;\n  }\n\n  > .btn-group .dropdown-menu {\n    left: auto;\n  }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n  > .btn,\n  > .btn-group > .btn {\n    input[type=\"radio\"],\n    input[type=\"checkbox\"] {\n      position: absolute;\n      clip: rect(0,0,0,0);\n      pointer-events: none;\n    }\n  }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n  border-top-right-radius: @radius;\n   border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n  border-bottom-right-radius: @radius;\n     border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n  border-bottom-right-radius: @radius;\n   border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n  border-bottom-left-radius: @radius;\n     border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n  position: relative; // For dropdowns\n  display: table;\n  border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n  // Undo padding and float of grid classes\n  &[class*=\"col-\"] {\n    float: none;\n    padding-left: 0;\n    padding-right: 0;\n  }\n\n  .form-control {\n    // Ensure that the input is always above the *appended* addon button for\n    // proper border colors.\n    position: relative;\n    z-index: 2;\n\n    // IE9 fubars the placeholder attribute in text inputs and the arrows on\n    // select elements in input groups. To fix it, we float the input. Details:\n    // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n    float: left;\n\n    width: 100%;\n    margin-bottom: 0;\n    \n    &:focus {\n      z-index: 3;\n    }\n  }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: 1;\n  color: @input-color;\n  text-align: center;\n  background-color: @input-group-addon-bg;\n  border: 1px solid @input-group-addon-border-color;\n  border-radius: @input-border-radius;\n\n  // Sizing\n  &.input-sm {\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    border-radius: @input-border-radius-small;\n  }\n  &.input-lg {\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    border-radius: @input-border-radius-large;\n  }\n\n  // Nuke default margins from checkboxes and radios to vertically center within.\n  input[type=\"radio\"],\n  input[type=\"checkbox\"] {\n    margin-top: 0;\n  }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  .border-right-radius(0);\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  .border-left-radius(0);\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n  position: relative;\n  // Jankily prevent input button groups from wrapping with `white-space` and\n  // `font-size` in combination with `inline-block` on buttons.\n  font-size: 0;\n  white-space: nowrap;\n\n  // Negative margin for spacing, position for bringing hovered/focused/actived\n  // element above the siblings.\n  > .btn {\n    position: relative;\n    + .btn {\n      margin-left: -1px;\n    }\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active {\n      z-index: 2;\n    }\n  }\n\n  // Negative margin to only have a 1px border between the two\n  &:first-child {\n    > .btn,\n    > .btn-group {\n      margin-right: -1px;\n    }\n  }\n  &:last-child {\n    > .btn,\n    > .btn-group {\n      z-index: 2;\n      margin-left: -1px;\n    }\n  }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n  margin-bottom: 0;\n  padding-left: 0; // Override default ul/ol\n  list-style: none;\n  &:extend(.clearfix all);\n\n  > li {\n    position: relative;\n    display: block;\n\n    > a {\n      position: relative;\n      display: block;\n      padding: @nav-link-padding;\n      &:hover,\n      &:focus {\n        text-decoration: none;\n        background-color: @nav-link-hover-bg;\n      }\n    }\n\n    // Disabled state sets text to gray and nukes hover/tab effects\n    &.disabled > a {\n      color: @nav-disabled-link-color;\n\n      &:hover,\n      &:focus {\n        color: @nav-disabled-link-hover-color;\n        text-decoration: none;\n        background-color: transparent;\n        cursor: @cursor-disabled;\n      }\n    }\n  }\n\n  // Open dropdowns\n  .open > a {\n    &,\n    &:hover,\n    &:focus {\n      background-color: @nav-link-hover-bg;\n      border-color: @link-color;\n    }\n  }\n\n  // Nav dividers (deprecated with v3.0.1)\n  //\n  // This should have been removed in v3 with the dropping of `.nav-list`, but\n  // we missed it. We don't currently support this anywhere, but in the interest\n  // of maintaining backward compatibility in case you use it, it's deprecated.\n  .nav-divider {\n    .nav-divider();\n  }\n\n  // Prevent IE8 from misplacing imgs\n  //\n  // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n  > li > a > img {\n    max-width: none;\n  }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n  border-bottom: 1px solid @nav-tabs-border-color;\n  > li {\n    float: left;\n    // Make the list-items overlay the bottom border\n    margin-bottom: -1px;\n\n    // Actual tabs (as links)\n    > a {\n      margin-right: 2px;\n      line-height: @line-height-base;\n      border: 1px solid transparent;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n      &:hover {\n        border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n      }\n    }\n\n    // Active state, and its :hover to override normal :hover\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-tabs-active-link-hover-color;\n        background-color: @nav-tabs-active-link-hover-bg;\n        border: 1px solid @nav-tabs-active-link-hover-border-color;\n        border-bottom-color: transparent;\n        cursor: default;\n      }\n    }\n  }\n  // pulling this in mainly for less shorthand\n  &.nav-justified {\n    .nav-justified();\n    .nav-tabs-justified();\n  }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n  > li {\n    float: left;\n\n    // Links rendered as pills\n    > a {\n      border-radius: @nav-pills-border-radius;\n    }\n    + li {\n      margin-left: 2px;\n    }\n\n    // Active state\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-pills-active-link-hover-color;\n        background-color: @nav-pills-active-link-hover-bg;\n      }\n    }\n  }\n}\n\n\n// Stacked pills\n.nav-stacked {\n  > li {\n    float: none;\n    + li {\n      margin-top: 2px;\n      margin-left: 0; // no need for this gap between nav items\n    }\n  }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n  width: 100%;\n\n  > li {\n    float: none;\n    > a {\n      text-align: center;\n      margin-bottom: 5px;\n    }\n  }\n\n  > .dropdown .dropdown-menu {\n    top: auto;\n    left: auto;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li {\n      display: table-cell;\n      width: 1%;\n      > a {\n        margin-bottom: 0;\n      }\n    }\n  }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n  border-bottom: 0;\n\n  > li > a {\n    // Override margin from .nav-tabs\n    margin-right: 0;\n    border-radius: @border-radius-base;\n  }\n\n  > .active > a,\n  > .active > a:hover,\n  > .active > a:focus {\n    border: 1px solid @nav-tabs-justified-link-border-color;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li > a {\n      border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n    }\n    > .active > a,\n    > .active > a:hover,\n    > .active > a:focus {\n      border-bottom-color: @nav-tabs-justified-active-link-border-color;\n    }\n  }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n  > .tab-pane {\n    display: none;\n  }\n  > .active {\n    display: block;\n  }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n  // make dropdown border overlap tab border\n  margin-top: -1px;\n  // Remove the top rounded corners here since there is a hard edge above the menu\n  .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n  position: relative;\n  min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n  margin-bottom: @navbar-margin-bottom;\n  border: 1px solid transparent;\n\n  // Prevent floats from breaking the navbar\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: @navbar-border-radius;\n  }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n  }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n  overflow-x: visible;\n  padding-right: @navbar-padding-horizontal;\n  padding-left:  @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n  &:extend(.clearfix all);\n  -webkit-overflow-scrolling: touch;\n\n  &.in {\n    overflow-y: auto;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n\n    &.collapse {\n      display: block !important;\n      height: auto !important;\n      padding-bottom: 0; // Override default setting\n      overflow: visible !important;\n    }\n\n    &.in {\n      overflow-y: visible;\n    }\n\n    // Undo the collapse side padding for navbars with containers to ensure\n    // alignment of right-aligned contents.\n    .navbar-fixed-top &,\n    .navbar-static-top &,\n    .navbar-fixed-bottom & {\n      padding-left: 0;\n      padding-right: 0;\n    }\n  }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  .navbar-collapse {\n    max-height: @navbar-collapse-max-height;\n\n    @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n      max-height: 200px;\n    }\n  }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n  > .navbar-header,\n  > .navbar-collapse {\n    margin-right: -@navbar-padding-horizontal;\n    margin-left:  -@navbar-padding-horizontal;\n\n    @media (min-width: @grid-float-breakpoint) {\n      margin-right: 0;\n      margin-left:  0;\n    }\n  }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n  z-index: @zindex-navbar;\n  border-width: 0 0 1px;\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: @zindex-navbar-fixed;\n\n  // Undo the rounded corners\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0; // override .navbar defaults\n  border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n  float: left;\n  padding: @navbar-padding-vertical @navbar-padding-horizontal;\n  font-size: @font-size-large;\n  line-height: @line-height-computed;\n  height: @navbar-height;\n\n  &:hover,\n  &:focus {\n    text-decoration: none;\n  }\n\n  > img {\n    display: block;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    .navbar > .container &,\n    .navbar > .container-fluid & {\n      margin-left: -@navbar-padding-horizontal;\n    }\n  }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: @navbar-padding-horizontal;\n  padding: 9px 10px;\n  .navbar-vertical-align(34px);\n  background-color: transparent;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  border-radius: @border-radius-base;\n\n  // We remove the `outline` here, but later compensate by attaching `:hover`\n  // styles to `:focus`.\n  &:focus {\n    outline: 0;\n  }\n\n  // Bars\n  .icon-bar {\n    display: block;\n    width: 22px;\n    height: 2px;\n    border-radius: 1px;\n  }\n  .icon-bar + .icon-bar {\n    margin-top: 4px;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    display: none;\n  }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n  margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n  > li > a {\n    padding-top:    10px;\n    padding-bottom: 10px;\n    line-height: @line-height-computed;\n  }\n\n  @media (max-width: @grid-float-breakpoint-max) {\n    // Dropdowns get custom display when collapsed\n    .open .dropdown-menu {\n      position: static;\n      float: none;\n      width: auto;\n      margin-top: 0;\n      background-color: transparent;\n      border: 0;\n      box-shadow: none;\n      > li > a,\n      .dropdown-header {\n        padding: 5px 15px 5px 25px;\n      }\n      > li > a {\n        line-height: @line-height-computed;\n        &:hover,\n        &:focus {\n          background-image: none;\n        }\n      }\n    }\n  }\n\n  // Uncollapse the nav\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin: 0;\n\n    > li {\n      float: left;\n      > a {\n        padding-top:    @navbar-padding-vertical;\n        padding-bottom: @navbar-padding-vertical;\n      }\n    }\n  }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n  margin-left: -@navbar-padding-horizontal;\n  margin-right: -@navbar-padding-horizontal;\n  padding: 10px @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n\n  // Mixin behavior for optimum display\n  .form-inline();\n\n  .form-group {\n    @media (max-width: @grid-float-breakpoint-max) {\n      margin-bottom: 5px;\n\n      &:last-child {\n        margin-bottom: 0;\n      }\n    }\n  }\n\n  // Vertically center in expanded, horizontal navbar\n  .navbar-vertical-align(@input-height-base);\n\n  // Undo 100% width for pull classes\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    .box-shadow(none);\n  }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  margin-bottom: 0;\n  .border-top-radius(@navbar-border-radius);\n  .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n  .navbar-vertical-align(@input-height-base);\n\n  &.btn-sm {\n    .navbar-vertical-align(@input-height-small);\n  }\n  &.btn-xs {\n    .navbar-vertical-align(22);\n  }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n  .navbar-vertical-align(@line-height-computed);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin-left: @navbar-padding-horizontal;\n    margin-right: @navbar-padding-horizontal;\n  }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-left  { .pull-left(); }\n  .navbar-right {\n    .pull-right();\n    margin-right: -@navbar-padding-horizontal;\n\n    ~ .navbar-right {\n      margin-right: 0;\n    }\n  }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  background-color: @navbar-default-bg;\n  border-color: @navbar-default-border;\n\n  .navbar-brand {\n    color: @navbar-default-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-brand-hover-color;\n      background-color: @navbar-default-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-default-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-default-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-hover-color;\n        background-color: @navbar-default-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-active-color;\n        background-color: @navbar-default-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n        background-color: @navbar-default-link-disabled-bg;\n      }\n    }\n  }\n\n  .navbar-toggle {\n    border-color: @navbar-default-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-default-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-default-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: @navbar-default-border;\n  }\n\n  // Dropdown menu items\n  .navbar-nav {\n    // Remove background color from open dropdown\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-default-link-active-bg;\n        color: @navbar-default-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display when collapsed\n      .open .dropdown-menu {\n        > li > a {\n          color: @navbar-default-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-hover-color;\n            background-color: @navbar-default-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-active-color;\n            background-color: @navbar-default-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-disabled-color;\n            background-color: @navbar-default-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n\n  // Links in navbars\n  //\n  // Add a class to ensure links outside the navbar nav are colored correctly.\n\n  .navbar-link {\n    color: @navbar-default-link-color;\n    &:hover {\n      color: @navbar-default-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-default-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n      }\n    }\n  }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n  background-color: @navbar-inverse-bg;\n  border-color: @navbar-inverse-border;\n\n  .navbar-brand {\n    color: @navbar-inverse-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-brand-hover-color;\n      background-color: @navbar-inverse-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-inverse-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-inverse-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-hover-color;\n        background-color: @navbar-inverse-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-active-color;\n        background-color: @navbar-inverse-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n        background-color: @navbar-inverse-link-disabled-bg;\n      }\n    }\n  }\n\n  // Darken the responsive nav toggle\n  .navbar-toggle {\n    border-color: @navbar-inverse-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-inverse-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-inverse-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: darken(@navbar-inverse-bg, 7%);\n  }\n\n  // Dropdowns\n  .navbar-nav {\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-inverse-link-active-bg;\n        color: @navbar-inverse-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display\n      .open .dropdown-menu {\n        > .dropdown-header {\n          border-color: @navbar-inverse-border;\n        }\n        .divider {\n          background-color: @navbar-inverse-border;\n        }\n        > li > a {\n          color: @navbar-inverse-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-hover-color;\n            background-color: @navbar-inverse-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-active-color;\n            background-color: @navbar-inverse-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-disabled-color;\n            background-color: @navbar-inverse-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n  .navbar-link {\n    color: @navbar-inverse-link-color;\n    &:hover {\n      color: @navbar-inverse-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-inverse-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n      }\n    }\n  }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n  margin-top: ((@navbar-height - @element-height) / 2);\n  margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n  .clearfix();\n}\n.center-block {\n  .center-block();\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n  display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n  position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n  padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n  margin-bottom: @line-height-computed;\n  list-style: none;\n  background-color: @breadcrumb-bg;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline-block;\n\n    + li:before {\n      content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n      padding: 0 5px;\n      color: @breadcrumb-color;\n    }\n  }\n\n  > .active {\n    color: @breadcrumb-active-color;\n  }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline; // Remove list-style and block-level defaults\n    > a,\n    > span {\n      position: relative;\n      float: left; // Collapse white-space\n      padding: @padding-base-vertical @padding-base-horizontal;\n      line-height: @line-height-base;\n      text-decoration: none;\n      color: @pagination-color;\n      background-color: @pagination-bg;\n      border: 1px solid @pagination-border;\n      margin-left: -1px;\n    }\n    &:first-child {\n      > a,\n      > span {\n        margin-left: 0;\n        .border-left-radius(@border-radius-base);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius-base);\n      }\n    }\n  }\n\n  > li > a,\n  > li > span {\n    &:hover,\n    &:focus {\n      z-index: 2;\n      color: @pagination-hover-color;\n      background-color: @pagination-hover-bg;\n      border-color: @pagination-hover-border;\n    }\n  }\n\n  > .active > a,\n  > .active > span {\n    &,\n    &:hover,\n    &:focus {\n      z-index: 3;\n      color: @pagination-active-color;\n      background-color: @pagination-active-bg;\n      border-color: @pagination-active-border;\n      cursor: default;\n    }\n  }\n\n  > .disabled {\n    > span,\n    > span:hover,\n    > span:focus,\n    > a,\n    > a:hover,\n    > a:focus {\n      color: @pagination-disabled-color;\n      background-color: @pagination-disabled-bg;\n      border-color: @pagination-disabled-border;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n  .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n  .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  > li {\n    > a,\n    > span {\n      padding: @padding-vertical @padding-horizontal;\n      font-size: @font-size;\n      line-height: @line-height;\n    }\n    &:first-child {\n      > a,\n      > span {\n        .border-left-radius(@border-radius);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius);\n      }\n    }\n  }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  list-style: none;\n  text-align: center;\n  &:extend(.clearfix all);\n  li {\n    display: inline;\n    > a,\n    > span {\n      display: inline-block;\n      padding: 5px 14px;\n      background-color: @pager-bg;\n      border: 1px solid @pager-border;\n      border-radius: @pager-border-radius;\n    }\n\n    > a:hover,\n    > a:focus {\n      text-decoration: none;\n      background-color: @pager-hover-bg;\n    }\n  }\n\n  .next {\n    > a,\n    > span {\n      float: right;\n    }\n  }\n\n  .previous {\n    > a,\n    > span {\n      float: left;\n    }\n  }\n\n  .disabled {\n    > a,\n    > a:hover,\n    > a:focus,\n    > span {\n      color: @pager-disabled-color;\n      background-color: @pager-bg;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: @label-color;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n\n  // Add hover effects, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @label-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Empty labels collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for labels in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n  .label-variant(@label-default-bg);\n}\n\n.label-primary {\n  .label-variant(@label-primary-bg);\n}\n\n.label-success {\n  .label-variant(@label-success-bg);\n}\n\n.label-info {\n  .label-variant(@label-info-bg);\n}\n\n.label-warning {\n  .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n  .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n  background-color: @color;\n\n  &[href] {\n    &:hover,\n    &:focus {\n      background-color: darken(@color, 10%);\n    }\n  }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: @font-size-small;\n  font-weight: @badge-font-weight;\n  color: @badge-color;\n  line-height: @badge-line-height;\n  vertical-align: middle;\n  white-space: nowrap;\n  text-align: center;\n  background-color: @badge-bg;\n  border-radius: @badge-border-radius;\n\n  // Empty badges collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for badges in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n\n  .btn-xs &,\n  .btn-group-xs > .btn & {\n    top: 0;\n    padding: 1px 5px;\n  }\n\n  // Hover state, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @badge-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Account for badges in navs\n  .list-group-item.active > &,\n  .nav-pills > .active > a > & {\n    color: @badge-active-color;\n    background-color: @badge-active-bg;\n  }\n\n  .list-group-item > & {\n    float: right;\n  }\n\n  .list-group-item > & + & {\n    margin-right: 5px;\n  }\n\n  .nav-pills > li > a > & {\n    margin-left: 3px;\n  }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n  padding-top:    @jumbotron-padding;\n  padding-bottom: @jumbotron-padding;\n  margin-bottom: @jumbotron-padding;\n  color: @jumbotron-color;\n  background-color: @jumbotron-bg;\n\n  h1,\n  .h1 {\n    color: @jumbotron-heading-color;\n  }\n\n  p {\n    margin-bottom: (@jumbotron-padding / 2);\n    font-size: @jumbotron-font-size;\n    font-weight: 200;\n  }\n\n  > hr {\n    border-top-color: darken(@jumbotron-bg, 10%);\n  }\n\n  .container &,\n  .container-fluid & {\n    border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n    padding-left:  (@grid-gutter-width / 2);\n    padding-right: (@grid-gutter-width / 2);\n  }\n\n  .container {\n    max-width: 100%;\n  }\n\n  @media screen and (min-width: @screen-sm-min) {\n    padding-top:    (@jumbotron-padding * 1.6);\n    padding-bottom: (@jumbotron-padding * 1.6);\n\n    .container &,\n    .container-fluid & {\n      padding-left:  (@jumbotron-padding * 2);\n      padding-right: (@jumbotron-padding * 2);\n    }\n\n    h1,\n    .h1 {\n      font-size: @jumbotron-heading-font-size;\n    }\n  }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n  display: block;\n  padding: @thumbnail-padding;\n  margin-bottom: @line-height-computed;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(border .2s ease-in-out);\n\n  > img,\n  a > img {\n    &:extend(.img-responsive);\n    margin-left: auto;\n    margin-right: auto;\n  }\n\n  // Add a hover state for linked versions only\n  a&:hover,\n  a&:focus,\n  a&.active {\n    border-color: @link-color;\n  }\n\n  // Image captions\n  .caption {\n    padding: @thumbnail-caption-padding;\n    color: @thumbnail-caption-color;\n  }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n  padding: @alert-padding;\n  margin-bottom: @line-height-computed;\n  border: 1px solid transparent;\n  border-radius: @alert-border-radius;\n\n  // Headings for larger alerts\n  h4 {\n    margin-top: 0;\n    // Specified for the h4 to prevent conflicts of changing @headings-color\n    color: inherit;\n  }\n\n  // Provide class for links that match alerts\n  .alert-link {\n    font-weight: @alert-link-font-weight;\n  }\n\n  // Improve alignment and spacing of inner content\n  > p,\n  > ul {\n    margin-bottom: 0;\n  }\n\n  > p + p {\n    margin-top: 5px;\n  }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n  padding-right: (@alert-padding + 20);\n\n  // Adjust close link position\n  .close {\n    position: relative;\n    top: -2px;\n    right: -21px;\n    color: inherit;\n  }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n  .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n  .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n  .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n  .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n  background-color: @background;\n  border-color: @border;\n  color: @text-color;\n\n  hr {\n    border-top-color: darken(@border, 5%);\n  }\n  .alert-link {\n    color: darken(@text-color, 10%);\n  }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n  overflow: hidden;\n  height: @line-height-computed;\n  margin-bottom: @line-height-computed;\n  background-color: @progress-bg;\n  border-radius: @progress-border-radius;\n  .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: @font-size-small;\n  line-height: @line-height-computed;\n  color: @progress-bar-color;\n  text-align: center;\n  background-color: @progress-bar-bg;\n  .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n  .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  #gradient > .striped();\n  background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n  .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n  .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n  .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n  .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n  .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n    background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Opera 12\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n  background-color: @color;\n\n  // Deprecated parent class requirement as of v3.2.0\n  .progress-striped & {\n    #gradient > .striped();\n  }\n}\n",".media {\n  // Proper spacing between instances of .media\n  margin-top: 15px;\n\n  &:first-child {\n    margin-top: 0;\n  }\n}\n\n.media,\n.media-body {\n  zoom: 1;\n  overflow: hidden;\n}\n\n.media-body {\n  width: 10000px;\n}\n\n.media-object {\n  display: block;\n\n  // Fix collapse in webkit from max-width: 100% and display: table-cell.\n  &.img-thumbnail {\n    max-width: none;\n  }\n}\n\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n\n.media-middle {\n  vertical-align: middle;\n}\n\n.media-bottom {\n  vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on <ul>, <ol>, or <div>.\n\n.list-group {\n  // No need to set list-style: none; since .list-group-item is block level\n  margin-bottom: 20px;\n  padding-left: 0; // reset padding because ul and ol\n}\n\n\n// Individual list items\n//\n// Use on `li`s or `div`s within the `.list-group` parent.\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  // Place the border on the list items and negative margin up for better styling\n  margin-bottom: -1px;\n  background-color: @list-group-bg;\n  border: 1px solid @list-group-border;\n\n  // Round the first and last items\n  &:first-child {\n    .border-top-radius(@list-group-border-radius);\n  }\n  &:last-child {\n    margin-bottom: 0;\n    .border-bottom-radius(@list-group-border-radius);\n  }\n}\n\n\n// Interactive list items\n//\n// Use anchor or button elements instead of `li`s or `div`s to create interactive items.\n// Includes an extra `.active` modifier class for showing selected items.\n\na.list-group-item,\nbutton.list-group-item {\n  color: @list-group-link-color;\n\n  .list-group-item-heading {\n    color: @list-group-link-heading-color;\n  }\n\n  // Hover state\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @list-group-link-hover-color;\n    background-color: @list-group-hover-bg;\n  }\n}\n\nbutton.list-group-item {\n  width: 100%;\n  text-align: left;\n}\n\n.list-group-item {\n  // Disabled state\n  &.disabled,\n  &.disabled:hover,\n  &.disabled:focus {\n    background-color: @list-group-disabled-bg;\n    color: @list-group-disabled-color;\n    cursor: @cursor-disabled;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-disabled-text-color;\n    }\n  }\n\n  // Active class on item itself, not parent\n  &.active,\n  &.active:hover,\n  &.active:focus {\n    z-index: 2; // Place active items above their siblings for proper border styling\n    color: @list-group-active-color;\n    background-color: @list-group-active-bg;\n    border-color: @list-group-active-border;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading,\n    .list-group-item-heading > small,\n    .list-group-item-heading > .small {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-active-text-color;\n    }\n  }\n}\n\n\n// Contextual variants\n//\n// Add modifier classes to change text and background color on individual items.\n// Organizationally, this must come after the `:hover` states.\n\n.list-group-item-variant(success; @state-success-bg; @state-success-text);\n.list-group-item-variant(info; @state-info-bg; @state-info-text);\n.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);\n.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);\n\n\n// Custom content options\n//\n// Extra classes for creating well-formatted content within `.list-group-item`s.\n\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n","// List Groups\n\n.list-group-item-variant(@state; @background; @color) {\n  .list-group-item-@{state} {\n    color: @color;\n    background-color: @background;\n\n    a&,\n    button& {\n      color: @color;\n\n      .list-group-item-heading {\n        color: inherit;\n      }\n\n      &:hover,\n      &:focus {\n        color: @color;\n        background-color: darken(@background, 5%);\n      }\n      &.active,\n      &.active:hover,\n      &.active:focus {\n        color: #fff;\n        background-color: @color;\n        border-color: @color;\n      }\n    }\n  }\n}\n","//\n// Panels\n// --------------------------------------------------\n\n\n// Base class\n.panel {\n  margin-bottom: @line-height-computed;\n  background-color: @panel-bg;\n  border: 1px solid transparent;\n  border-radius: @panel-border-radius;\n  .box-shadow(0 1px 1px rgba(0,0,0,.05));\n}\n\n// Panel contents\n.panel-body {\n  padding: @panel-body-padding;\n  &:extend(.clearfix all);\n}\n\n// Optional heading\n.panel-heading {\n  padding: @panel-heading-padding;\n  border-bottom: 1px solid transparent;\n  .border-top-radius((@panel-border-radius - 1));\n\n  > .dropdown .dropdown-toggle {\n    color: inherit;\n  }\n}\n\n// Within heading, strip any `h*` tag of its default margins for spacing.\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: ceil((@font-size-base * 1.125));\n  color: inherit;\n\n  > a,\n  > small,\n  > .small,\n  > small > a,\n  > .small > a {\n    color: inherit;\n  }\n}\n\n// Optional footer (stays gray in every modifier class)\n.panel-footer {\n  padding: @panel-footer-padding;\n  background-color: @panel-footer-bg;\n  border-top: 1px solid @panel-inner-border;\n  .border-bottom-radius((@panel-border-radius - 1));\n}\n\n\n// List groups in panels\n//\n// By default, space out list group content from panel headings to account for\n// any kind of custom content between the two.\n\n.panel {\n  > .list-group,\n  > .panel-collapse > .list-group {\n    margin-bottom: 0;\n\n    .list-group-item {\n      border-width: 1px 0;\n      border-radius: 0;\n    }\n\n    // Add border top radius for first one\n    &:first-child {\n      .list-group-item:first-child {\n        border-top: 0;\n        .border-top-radius((@panel-border-radius - 1));\n      }\n    }\n\n    // Add border bottom radius for last one\n    &:last-child {\n      .list-group-item:last-child {\n        border-bottom: 0;\n        .border-bottom-radius((@panel-border-radius - 1));\n      }\n    }\n  }\n  > .panel-heading + .panel-collapse > .list-group {\n    .list-group-item:first-child {\n      .border-top-radius(0);\n    }\n  }\n}\n// Collapse space between when there's no additional content.\n.panel-heading + .list-group {\n  .list-group-item:first-child {\n    border-top-width: 0;\n  }\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n\n// Tables in panels\n//\n// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and\n// watch it go full width.\n\n.panel {\n  > .table,\n  > .table-responsive > .table,\n  > .panel-collapse > .table {\n    margin-bottom: 0;\n\n    caption {\n      padding-left: @panel-body-padding;\n      padding-right: @panel-body-padding;\n    }\n  }\n  // Add border top radius for first one\n  > .table:first-child,\n  > .table-responsive:first-child > .table:first-child {\n    .border-top-radius((@panel-border-radius - 1));\n\n    > thead:first-child,\n    > tbody:first-child {\n      > tr:first-child {\n        border-top-left-radius: (@panel-border-radius - 1);\n        border-top-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-top-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-top-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  // Add border bottom radius for last one\n  > .table:last-child,\n  > .table-responsive:last-child > .table:last-child {\n    .border-bottom-radius((@panel-border-radius - 1));\n\n    > tbody:last-child,\n    > tfoot:last-child {\n      > tr:last-child {\n        border-bottom-left-radius: (@panel-border-radius - 1);\n        border-bottom-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-bottom-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-bottom-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  > .panel-body + .table,\n  > .panel-body + .table-responsive,\n  > .table + .panel-body,\n  > .table-responsive + .panel-body {\n    border-top: 1px solid @table-border-color;\n  }\n  > .table > tbody:first-child > tr:first-child th,\n  > .table > tbody:first-child > tr:first-child td {\n    border-top: 0;\n  }\n  > .table-bordered,\n  > .table-responsive > .table-bordered {\n    border: 0;\n    > thead,\n    > tbody,\n    > tfoot {\n      > tr {\n        > th:first-child,\n        > td:first-child {\n          border-left: 0;\n        }\n        > th:last-child,\n        > td:last-child {\n          border-right: 0;\n        }\n      }\n    }\n    > thead,\n    > tbody {\n      > tr:first-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n    > tbody,\n    > tfoot {\n      > tr:last-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n  }\n  > .table-responsive {\n    border: 0;\n    margin-bottom: 0;\n  }\n}\n\n\n// Collapsable panels (aka, accordion)\n//\n// Wrap a series of panels in `.panel-group` to turn them into an accordion with\n// the help of our collapse JavaScript plugin.\n\n.panel-group {\n  margin-bottom: @line-height-computed;\n\n  // Tighten up margin so it's only between panels\n  .panel {\n    margin-bottom: 0;\n    border-radius: @panel-border-radius;\n\n    + .panel {\n      margin-top: 5px;\n    }\n  }\n\n  .panel-heading {\n    border-bottom: 0;\n\n    + .panel-collapse > .panel-body,\n    + .panel-collapse > .list-group {\n      border-top: 1px solid @panel-inner-border;\n    }\n  }\n\n  .panel-footer {\n    border-top: 0;\n    + .panel-collapse .panel-body {\n      border-bottom: 1px solid @panel-inner-border;\n    }\n  }\n}\n\n\n// Contextual variations\n.panel-default {\n  .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);\n}\n.panel-primary {\n  .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);\n}\n.panel-success {\n  .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);\n}\n.panel-info {\n  .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);\n}\n.panel-warning {\n  .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);\n}\n.panel-danger {\n  .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);\n}\n","// Panels\n\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n  border-color: @border;\n\n  & > .panel-heading {\n    color: @heading-text-color;\n    background-color: @heading-bg-color;\n    border-color: @heading-border;\n\n    + .panel-collapse > .panel-body {\n      border-top-color: @border;\n    }\n    .badge {\n      color: @heading-bg-color;\n      background-color: @heading-text-color;\n    }\n  }\n  & > .panel-footer {\n    + .panel-collapse > .panel-body {\n      border-bottom-color: @border;\n    }\n  }\n}\n","// Embeds responsive\n//\n// Credit: Nicolas Gallagher and SUIT CSS.\n\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\n\n  .embed-responsive-item,\n  iframe,\n  embed,\n  object,\n  video {\n    position: absolute;\n    top: 0;\n    left: 0;\n    bottom: 0;\n    height: 100%;\n    width: 100%;\n    border: 0;\n  }\n}\n\n// Modifier class for 16:9 aspect ratio\n.embed-responsive-16by9 {\n  padding-bottom: 56.25%;\n}\n\n// Modifier class for 4:3 aspect ratio\n.embed-responsive-4by3 {\n  padding-bottom: 75%;\n}\n","//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: @well-bg;\n  border: 1px solid @well-border;\n  border-radius: @border-radius-base;\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));\n  blockquote {\n    border-color: #ddd;\n    border-color: rgba(0,0,0,.15);\n  }\n}\n\n// Sizes\n.well-lg {\n  padding: 24px;\n  border-radius: @border-radius-large;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: @border-radius-small;\n}\n","//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n  float: right;\n  font-size: (@font-size-base * 1.5);\n  font-weight: @close-font-weight;\n  line-height: 1;\n  color: @close-color;\n  text-shadow: @close-text-shadow;\n  .opacity(.2);\n\n  &:hover,\n  &:focus {\n    color: @close-color;\n    text-decoration: none;\n    cursor: pointer;\n    .opacity(.5);\n  }\n\n  // Additional properties for button version\n  // iOS requires the button element instead of an anchor tag.\n  // If you want the anchor version, it requires `href=\"#\"`.\n  // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n  button& {\n    padding: 0;\n    cursor: pointer;\n    background: transparent;\n    border: 0;\n    -webkit-appearance: none;\n  }\n}\n","//\n// Modals\n// --------------------------------------------------\n\n// .modal-open      - body class for killing the scroll\n// .modal           - container to scroll within\n// .modal-dialog    - positioning shell for the actual modal\n// .modal-content   - actual modal w/ bg and corners and shit\n\n// Kill the scroll on the body\n.modal-open {\n  overflow: hidden;\n}\n\n// Container that the modal scrolls within\n.modal {\n  display: none;\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal;\n  -webkit-overflow-scrolling: touch;\n\n  // Prevent Chrome on Windows from adding a focus outline. For details, see\n  // https://github.com/twbs/bootstrap/pull/10951.\n  outline: 0;\n\n  // When fading in the modal, animate it to slide down\n  &.fade .modal-dialog {\n    .translate(0, -25%);\n    .transition-transform(~\"0.3s ease-out\");\n  }\n  &.in .modal-dialog { .translate(0, 0) }\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n// Shell div to position the modal with bottom padding\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n\n// Actual modal\n.modal-content {\n  position: relative;\n  background-color: @modal-content-bg;\n  border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)\n  border: 1px solid @modal-content-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 3px 9px rgba(0,0,0,.5));\n  background-clip: padding-box;\n  // Remove focus outline from opened modal\n  outline: 0;\n}\n\n// Modal background\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal-background;\n  background-color: @modal-backdrop-bg;\n  // Fade for backdrop\n  &.fade { .opacity(0); }\n  &.in { .opacity(@modal-backdrop-opacity); }\n}\n\n// Modal header\n// Top section of the modal w/ title and dismiss\n.modal-header {\n  padding: @modal-title-padding;\n  border-bottom: 1px solid @modal-header-border-color;\n  &:extend(.clearfix all);\n}\n// Close icon\n.modal-header .close {\n  margin-top: -2px;\n}\n\n// Title text within header\n.modal-title {\n  margin: 0;\n  line-height: @modal-title-line-height;\n}\n\n// Modal body\n// Where all modal content resides (sibling of .modal-header and .modal-footer)\n.modal-body {\n  position: relative;\n  padding: @modal-inner-padding;\n}\n\n// Footer (for actions)\n.modal-footer {\n  padding: @modal-inner-padding;\n  text-align: right; // right align buttons\n  border-top: 1px solid @modal-footer-border-color;\n  &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons\n\n  // Properly space out buttons\n  .btn + .btn {\n    margin-left: 5px;\n    margin-bottom: 0; // account for input[type=\"submit\"] which gets the bottom margin like all other inputs\n  }\n  // but override that for button groups\n  .btn-group .btn + .btn {\n    margin-left: -1px;\n  }\n  // and override it for block buttons as well\n  .btn-block + .btn-block {\n    margin-left: 0;\n  }\n}\n\n// Measure scrollbar width for padding body during modal show/hide\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n\n// Scale up the modal\n@media (min-width: @screen-sm-min) {\n  // Automatically set modal's width for larger viewports\n  .modal-dialog {\n    width: @modal-md;\n    margin: 30px auto;\n  }\n  .modal-content {\n    .box-shadow(0 5px 15px rgba(0,0,0,.5));\n  }\n\n  // Modal sizes\n  .modal-sm { width: @modal-sm; }\n}\n\n@media (min-width: @screen-md-min) {\n  .modal-lg { width: @modal-lg; }\n}\n","//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n  position: absolute;\n  z-index: @zindex-tooltip;\n  display: block;\n  // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.\n  // So reset our font and text properties to avoid inheriting weird values.\n  .reset-text();\n  font-size: @font-size-small;\n\n  .opacity(0);\n\n  &.in     { .opacity(@tooltip-opacity); }\n  &.top    { margin-top:  -3px; padding: @tooltip-arrow-width 0; }\n  &.right  { margin-left:  3px; padding: 0 @tooltip-arrow-width; }\n  &.bottom { margin-top:   3px; padding: @tooltip-arrow-width 0; }\n  &.left   { margin-left: -3px; padding: 0 @tooltip-arrow-width; }\n}\n\n// Wrapper for the tooltip content\n.tooltip-inner {\n  max-width: @tooltip-max-width;\n  padding: 3px 8px;\n  color: @tooltip-color;\n  text-align: center;\n  background-color: @tooltip-bg;\n  border-radius: @border-radius-base;\n}\n\n// Arrows\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1\n.tooltip {\n  &.top .tooltip-arrow {\n    bottom: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-left .tooltip-arrow {\n    bottom: 0;\n    right: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-right .tooltip-arrow {\n    bottom: 0;\n    left: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.right .tooltip-arrow {\n    top: 50%;\n    left: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-right-color: @tooltip-arrow-color;\n  }\n  &.left .tooltip-arrow {\n    top: 50%;\n    right: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-left-color: @tooltip-arrow-color;\n  }\n  &.bottom .tooltip-arrow {\n    top: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-left .tooltip-arrow {\n    top: 0;\n    right: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-right .tooltip-arrow {\n    top: 0;\n    left: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n}\n",".reset-text() {\n  font-family: @font-family-base;\n  // We deliberately do NOT reset font-size.\n  font-style: normal;\n  font-weight: normal;\n  letter-spacing: normal;\n  line-break: auto;\n  line-height: @line-height-base;\n  text-align: left; // Fallback for where `start` is not supported\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  white-space: normal;\n  word-break: normal;\n  word-spacing: normal;\n  word-wrap: normal;\n}\n","//\n// Popovers\n// --------------------------------------------------\n\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: @zindex-popover;\n  display: none;\n  max-width: @popover-max-width;\n  padding: 1px;\n  // Our parent element can be arbitrary since popovers are by default inserted as a sibling of their target element.\n  // So reset our font and text properties to avoid inheriting weird values.\n  .reset-text();\n  font-size: @font-size-base;\n\n  background-color: @popover-bg;\n  background-clip: padding-box;\n  border: 1px solid @popover-fallback-border-color;\n  border: 1px solid @popover-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 5px 10px rgba(0,0,0,.2));\n\n  // Offset the popover to account for the popover arrow\n  &.top     { margin-top: -@popover-arrow-width; }\n  &.right   { margin-left: @popover-arrow-width; }\n  &.bottom  { margin-top: @popover-arrow-width; }\n  &.left    { margin-left: -@popover-arrow-width; }\n}\n\n.popover-title {\n  margin: 0; // reset heading margin\n  padding: 8px 14px;\n  font-size: @font-size-base;\n  background-color: @popover-title-bg;\n  border-bottom: 1px solid darken(@popover-title-bg, 5%);\n  border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0;\n}\n\n.popover-content {\n  padding: 9px 14px;\n}\n\n// Arrows\n//\n// .arrow is outer, .arrow:after is inner\n\n.popover > .arrow {\n  &,\n  &:after {\n    position: absolute;\n    display: block;\n    width: 0;\n    height: 0;\n    border-color: transparent;\n    border-style: solid;\n  }\n}\n.popover > .arrow {\n  border-width: @popover-arrow-outer-width;\n}\n.popover > .arrow:after {\n  border-width: @popover-arrow-width;\n  content: \"\";\n}\n\n.popover {\n  &.top > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-bottom-width: 0;\n    border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-top-color: @popover-arrow-outer-color;\n    bottom: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      bottom: 1px;\n      margin-left: -@popover-arrow-width;\n      border-bottom-width: 0;\n      border-top-color: @popover-arrow-color;\n    }\n  }\n  &.right > .arrow {\n    top: 50%;\n    left: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-left-width: 0;\n    border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-right-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      left: 1px;\n      bottom: -@popover-arrow-width;\n      border-left-width: 0;\n      border-right-color: @popover-arrow-color;\n    }\n  }\n  &.bottom > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-top-width: 0;\n    border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-bottom-color: @popover-arrow-outer-color;\n    top: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      top: 1px;\n      margin-left: -@popover-arrow-width;\n      border-top-width: 0;\n      border-bottom-color: @popover-arrow-color;\n    }\n  }\n\n  &.left > .arrow {\n    top: 50%;\n    right: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-right-width: 0;\n    border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-left-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      right: 1px;\n      border-right-width: 0;\n      border-left-color: @popover-arrow-color;\n      bottom: -@popover-arrow-width;\n    }\n  }\n}\n","//\n// Carousel\n// --------------------------------------------------\n\n\n// Wrapper for the slide container and indicators\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n\n  > .item {\n    display: none;\n    position: relative;\n    .transition(.6s ease-in-out left);\n\n    // Account for jankitude on images\n    > img,\n    > a > img {\n      &:extend(.img-responsive);\n      line-height: 1;\n    }\n\n    // WebKit CSS3 transforms for supported devices\n    @media all and (transform-3d), (-webkit-transform-3d) {\n      .transition-transform(~'0.6s ease-in-out');\n      .backface-visibility(~'hidden');\n      .perspective(1000px);\n\n      &.next,\n      &.active.right {\n        .translate3d(100%, 0, 0);\n        left: 0;\n      }\n      &.prev,\n      &.active.left {\n        .translate3d(-100%, 0, 0);\n        left: 0;\n      }\n      &.next.left,\n      &.prev.right,\n      &.active {\n        .translate3d(0, 0, 0);\n        left: 0;\n      }\n    }\n  }\n\n  > .active,\n  > .next,\n  > .prev {\n    display: block;\n  }\n\n  > .active {\n    left: 0;\n  }\n\n  > .next,\n  > .prev {\n    position: absolute;\n    top: 0;\n    width: 100%;\n  }\n\n  > .next {\n    left: 100%;\n  }\n  > .prev {\n    left: -100%;\n  }\n  > .next.left,\n  > .prev.right {\n    left: 0;\n  }\n\n  > .active.left {\n    left: -100%;\n  }\n  > .active.right {\n    left: 100%;\n  }\n\n}\n\n// Left/right controls for nav\n// ---------------------------\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: @carousel-control-width;\n  .opacity(@carousel-control-opacity);\n  font-size: @carousel-control-font-size;\n  color: @carousel-control-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  background-color: rgba(0, 0, 0, 0); // Fix IE9 click-thru bug\n  // We can't have this transition here because WebKit cancels the carousel\n  // animation if you trip this while in the middle of another animation.\n\n  // Set gradients for backgrounds\n  &.left {\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));\n  }\n  &.right {\n    left: auto;\n    right: 0;\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));\n  }\n\n  // Hover/focus state\n  &:hover,\n  &:focus {\n    outline: 0;\n    color: @carousel-control-color;\n    text-decoration: none;\n    .opacity(.9);\n  }\n\n  // Toggles\n  .icon-prev,\n  .icon-next,\n  .glyphicon-chevron-left,\n  .glyphicon-chevron-right {\n    position: absolute;\n    top: 50%;\n    margin-top: -10px;\n    z-index: 5;\n    display: inline-block;\n  }\n  .icon-prev,\n  .glyphicon-chevron-left {\n    left: 50%;\n    margin-left: -10px;\n  }\n  .icon-next,\n  .glyphicon-chevron-right {\n    right: 50%;\n    margin-right: -10px;\n  }\n  .icon-prev,\n  .icon-next {\n    width:  20px;\n    height: 20px;\n    line-height: 1;\n    font-family: serif;\n  }\n\n\n  .icon-prev {\n    &:before {\n      content: '\\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)\n    }\n  }\n  .icon-next {\n    &:before {\n      content: '\\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)\n    }\n  }\n}\n\n// Optional indicator pips\n//\n// Add an unordered list with the following class and add a list item for each\n// slide your carousel holds.\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n\n  li {\n    display: inline-block;\n    width:  10px;\n    height: 10px;\n    margin: 1px;\n    text-indent: -999px;\n    border: 1px solid @carousel-indicator-border-color;\n    border-radius: 10px;\n    cursor: pointer;\n\n    // IE8-9 hack for event handling\n    //\n    // Internet Explorer 8-9 does not support clicks on elements without a set\n    // `background-color`. We cannot use `filter` since that's not viewed as a\n    // background color by the browser. Thus, a hack is needed.\n    // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer\n    //\n    // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we\n    // set alpha transparency for the best results possible.\n    background-color: #000 \\9; // IE8\n    background-color: rgba(0,0,0,0); // IE9\n  }\n  .active {\n    margin: 0;\n    width:  12px;\n    height: 12px;\n    background-color: @carousel-indicator-active-bg;\n  }\n}\n\n// Optional captions\n// -----------------------------\n// Hidden by default for smaller viewports\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: @carousel-caption-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  & .btn {\n    text-shadow: none; // No shadow for button elements in carousel-caption\n  }\n}\n\n\n// Scale up controls for tablets and up\n@media screen and (min-width: @screen-sm-min) {\n\n  // Scale up the controls a smidge\n  .carousel-control {\n    .glyphicon-chevron-left,\n    .glyphicon-chevron-right,\n    .icon-prev,\n    .icon-next {\n      width: (@carousel-control-font-size * 1.5);\n      height: (@carousel-control-font-size * 1.5);\n      margin-top: (@carousel-control-font-size / -2);\n      font-size: (@carousel-control-font-size * 1.5);\n    }\n    .glyphicon-chevron-left,\n    .icon-prev {\n      margin-left: (@carousel-control-font-size / -2);\n    }\n    .glyphicon-chevron-right,\n    .icon-next {\n      margin-right: (@carousel-control-font-size / -2);\n    }\n  }\n\n  // Show and left align the captions\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n\n  // Move up the indicators\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n","// Clearfix\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n//    contenteditable attribute is included anywhere else in the document.\n//    Otherwise it causes space to appear at the top and bottom of elements\n//    that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n//    `:before` to contain the top-margins of child elements.\n//\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n\n.clearfix() {\n  &:before,\n  &:after {\n    content: \" \"; // 1\n    display: table; // 2\n  }\n  &:after {\n    clear: both;\n  }\n}\n","// Center-align a block level element\n\n.center-block() {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n","// CSS image replacement\n//\n// Heads up! v3 launched with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (has been removed in v4)\n.hide-text() {\n  font: ~\"0/0\" a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n// New mixin to use as of v3.0.1\n.text-hide() {\n  .hide-text();\n}\n","//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n\n// IE10 in Windows (Phone) 8\n//\n// Support for responsive views via media queries is kind of borked in IE10, for\n// Surface/desktop in split view and for Windows Phone 8. This particular fix\n// must be accompanied by a snippet of JavaScript to sniff the user agent and\n// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at\n// our Getting Started page for more information on this bug.\n//\n// For more information, see the following:\n//\n// Issue: https://github.com/twbs/bootstrap/issues/10497\n// Docs: http://getbootstrap.com/getting-started/#support-ie10-width\n// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/\n// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/\n\n@-ms-viewport {\n  width: device-width;\n}\n\n\n// Visibility utilities\n// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  .responsive-invisibility();\n}\n\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n\n.visible-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-visibility();\n  }\n}\n.visible-xs-block {\n  @media (max-width: @screen-xs-max) {\n    display: block !important;\n  }\n}\n.visible-xs-inline {\n  @media (max-width: @screen-xs-max) {\n    display: inline !important;\n  }\n}\n.visible-xs-inline-block {\n  @media (max-width: @screen-xs-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-visibility();\n  }\n}\n.visible-sm-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: block !important;\n  }\n}\n.visible-sm-inline {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline !important;\n  }\n}\n.visible-sm-inline-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-visibility();\n  }\n}\n.visible-md-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: block !important;\n  }\n}\n.visible-md-inline {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline !important;\n  }\n}\n.visible-md-inline-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-visibility();\n  }\n}\n.visible-lg-block {\n  @media (min-width: @screen-lg-min) {\n    display: block !important;\n  }\n}\n.visible-lg-inline {\n  @media (min-width: @screen-lg-min) {\n    display: inline !important;\n  }\n}\n.visible-lg-inline-block {\n  @media (min-width: @screen-lg-min) {\n    display: inline-block !important;\n  }\n}\n\n.hidden-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-invisibility();\n  }\n}\n\n\n// Print utilities\n//\n// Media queries are placed on the inside to be mixin-friendly.\n\n// Note: Deprecated .visible-print as of v3.2.0\n.visible-print {\n  .responsive-invisibility();\n\n  @media print {\n    .responsive-visibility();\n  }\n}\n.visible-print-block {\n  display: none !important;\n\n  @media print {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n\n  @media print {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n\n  @media print {\n    display: inline-block !important;\n  }\n}\n\n.hidden-print {\n  @media print {\n    .responsive-invisibility();\n  }\n}\n","// Responsive utilities\n\n//\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n  display: block !important;\n  table&  { display: table !important; }\n  tr&     { display: table-row !important; }\n  th&,\n  td&     { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n  display: none !important;\n}\n"]}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.min.css
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.min.css	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.min.css	(revision 5095)
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap v3.3.6 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
+/*# sourceMappingURL=bootstrap.min.css.map */
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.min.css.map
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.min.css.map	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/css/bootstrap.min.css.map	(revision 5095)
@@ -0,0 +1,1 @@
+{"version":3,"sources":["less/normalize.less","less/print.less","bootstrap.css","dist/css/bootstrap.css","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":";;;;4EAQA,KACE,YAAA,WACA,yBAAA,KACA,qBAAA,KAOF,KACE,OAAA,EAaF,QAAA,MAAA,QAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,KAAA,IAAA,QAAA,QAaE,QAAA,MAQF,MAAA,OAAA,SAAA,MAIE,QAAA,aACA,eAAA,SAQF,sBACE,QAAA,KACA,OAAA,EAQF,SAAA,SAEE,QAAA,KAUF,EACE,iBAAA,YAQF,SAAA,QAEE,QAAA,EAUF,YACE,cAAA,IAAA,OAOF,EAAA,OAEE,YAAA,IAOF,IACE,WAAA,OAQF,GACE,OAAA,MAAA,EACA,UAAA,IAOF,KACE,MAAA,KACA,WAAA,KAOF,MACE,UAAA,IAOF,IAAA,IAEE,SAAA,SACA,UAAA,IACA,YAAA,EACA,eAAA,SAGF,IACE,IAAA,MAGF,IACE,OAAA,OAUF,IACE,OAAA,EAOF,eACE,SAAA,OAUF,OACE,OAAA,IAAA,KAOF,GACE,OAAA,EAAA,mBAAA,YAAA,gBAAA,YACA,WAAA,YAOF,IACE,SAAA,KAOF,KAAA,IAAA,IAAA,KAIE,YAAA,UAAA,UACA,UAAA,IAkBF,OAAA,MAAA,SAAA,OAAA,SAKE,OAAA,EACA,KAAA,QACA,MAAA,QAOF,OACE,SAAA,QAUF,OAAA,OAEE,eAAA,KAWF,OAAA,wBAAA,kBAAA,mBAIE,mBAAA,OACA,OAAA,QAOF,iBAAA,qBAEE,OAAA,QAOF,yBAAA,wBAEE,QAAA,EACA,OAAA,EAQF,MACE,YAAA,OAWF,qBAAA,kBAEE,mBAAA,WAAA,gBAAA,WAAA,WAAA,WACA,QAAA,EASF,8CAAA,8CAEE,OAAA,KAQF,mBACE,mBAAA,YACA,gBAAA,YAAA,WAAA,YAAA,mBAAA,UASF,iDAAA,8CAEE,mBAAA,KAOF,SACE,QAAA,MAAA,OAAA,MACA,OAAA,EAAA,IACA,OAAA,IAAA,MAAA,OAQF,OACE,QAAA,EACA,OAAA,EAOF,SACE,SAAA,KAQF,SACE,YAAA,IAUF,MACE,eAAA,EACA,gBAAA,SAGF,GAAA,GAEE,QAAA,uFCjUF,aA7FI,EAAA,OAAA,QAGI,MAAA,eACA,YAAA,eACA,WAAA,cAAA,mBAAA,eACA,WAAA,eAGJ,EAAA,UAEI,gBAAA,UAGJ,cACI,QAAA,KAAA,WAAA,IAGJ,kBACI,QAAA,KAAA,YAAA,IAKJ,6BAAA,mBAEI,QAAA,GAGJ,WAAA,IAEI,OAAA,IAAA,MAAA,KC4KL,kBAAA,MDvKK,MC0KL,QAAA,mBDrKK,IE8KN,GDLC,kBAAA,MDrKK,ICwKL,UAAA,eCUD,GF5KM,GE2KN,EF1KM,QAAA,ECuKL,OAAA,ECSD,GF3KM,GCsKL,iBAAA,MD/JK,QCkKL,QAAA,KCSD,YFtKU,oBCiKT,iBAAA,eD7JK,OCgKL,OAAA,IAAA,MAAA,KD5JK,OC+JL,gBAAA,mBCSD,UFpKU,UC+JT,iBAAA,eDzJS,mBEkKV,mBDLC,OAAA,IAAA,MAAA,gBEjPD,WACA,YAAA,uBFsPD,IAAA,+CE7OC,IAAK,sDAAuD,4BAA6B,iDAAkD,gBAAiB,gDAAiD,eAAgB,+CAAgD,mBAAoB,2EAA4E,cAE7W,WACA,SAAA,SACA,IAAA,IACA,QAAA,aACA,YAAA,uBACA,WAAA,OACA,YAAA,IACA,YAAA,EAIkC,uBAAA,YAAW,wBAAA,UACX,2BAAW,QAAA,QAEX,uBDuPlC,QAAS,QCtPyB,sBFiPnC,uBEjP8C,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,2BAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,6BAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,2BAAW,QAAA,QACX,qBAAW,QAAA,QACX,0BAAW,QAAA,QACX,qBAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,2BAAW,QAAA,QACX,sBAAW,QAAA,QACX,yBAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,+BAAW,QAAA,QACX,2BAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,8BAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,6BAAW,QAAA,QACX,6BAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,sBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,2BAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,yBAAW,QAAA,QACX,8BAAW,QAAA,QACX,6BAAW,QAAA,QACX,6BAAW,QAAA,QACX,+BAAW,QAAA,QACX,8BAAW,QAAA,QACX,gCAAW,QAAA,QACX,uBAAW,QAAA,QACX,8BAAW,QAAA,QACX,+BAAW,QAAA,QACX,iCAAW,QAAA,QACX,0BAAW,QAAA,QACX,6BAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,gCAAW,QAAA,QACX,gCAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,0BAAW,QAAA,QACX,+BAAW,QAAA,QACX,+BAAW,QAAA,QACX,wBAAW,QAAA,QACX,+BAAW,QAAA,QACX,gCAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,8BAAW,QAAA,QACX,0BAAW,QAAA,QACX,gCAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,gCAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,6BAAW,QAAA,QACX,8BAAW,QAAA,QACX,2BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,8BAAW,QAAA,QACX,+BAAW,QAAA,QACX,mCAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,2BAAW,QAAA,QACX,4BAAW,QAAA,QACX,+BAAW,QAAA,QACX,wBAAW,QAAA,QACX,2BAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,yBAAW,QAAA,QACX,6BAAW,QAAA,QACX,+BAAW,QAAA,QACX,0BAAW,QAAA,QACX,gCAAW,QAAA,QACX,+BAAW,QAAA,QACX,8BAAW,QAAA,QACX,kCAAW,QAAA,QACX,oCAAW,QAAA,QACX,sBAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,8BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,0BAAW,QAAA,QACX,4BAAW,QAAA,QACX,qCAAW,QAAA,QACX,oCAAW,QAAA,QACX,kCAAW,QAAA,QACX,oCAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,8BAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,0BAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,uBAAW,QAAA,QACX,mCAAW,QAAA,QACX,uCAAW,QAAA,QACX,gCAAW,QAAA,QACX,oCAAW,QAAA,QACX,qCAAW,QAAA,QACX,yCAAW,QAAA,QACX,4BAAW,QAAA,QACX,yBAAW,QAAA,QACX,gCAAW,QAAA,QACX,8BAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,0BAAW,QAAA,QACX,6BAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,8BAAW,QAAA,QACX,+BAAW,QAAA,QACX,gCAAW,QAAA,QACX,8BAAW,QAAA,QACX,8BAAW,QAAA,QACX,8BAAW,QAAA,QACX,2BAAW,QAAA,QACX,0BAAW,QAAA,QACX,yBAAW,QAAA,QACX,6BAAW,QAAA,QACX,2BAAW,QAAA,QACX,4BAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,2BAAW,QAAA,QACX,2BAAW,QAAA,QACX,4BAAW,QAAA,QACX,+BAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,iCAAW,QAAA,QACX,oCAAW,QAAA,QACX,iCAAW,QAAA,QACX,+BAAW,QAAA,QACX,+BAAW,QAAA,QACX,iCAAW,QAAA,QACX,qBAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QASX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,4BAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,yBAAW,QAAA,QACX,yBAAW,QAAA,QACX,+BAAW,QAAA,QACX,uBAAW,QAAA,QACX,6BAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,4BAAW,QAAA,QACX,uBAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,2BAAW,QAAA,QACX,0BAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,4BAAW,QAAA,QACX,mCAAW,QAAA,QACX,4BAAW,QAAA,QACX,oCAAW,QAAA,QACX,kCAAW,QAAA,QACX,iCAAW,QAAA,QACX,+BAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,kCAAW,QAAA,QACX,mCAAW,QAAA,QACX,sCAAW,QAAA,QACX,0CAAW,QAAA,QACX,oCAAW,QAAA,QACX,wCAAW,QAAA,QACX,qCAAW,QAAA,QACX,iCAAW,QAAA,QACX,gCAAW,QAAA,QACX,kCAAW,QAAA,QACX,+BAAW,QAAA,QACX,0BAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QCtS/C,0BCgEE,QAAA,QHi+BF,EDNC,mBAAA,WGxhCI,gBAAiB,WFiiCZ,WAAY,WGl+BZ,OADL,QJg+BJ,mBAAA,WGthCI,gBAAiB,WACpB,WAAA,WHyhCD,KGrhCC,UAAW,KAEX,4BAAA,cAEA,KACA,YAAA,iBAAA,UAAA,MAAA,WHuhCD,UAAA,KGnhCC,YAAa,WF4hCb,MAAO,KACP,iBAAkB,KExhClB,OADA,MAEA,OHqhCD,SG/gCC,YAAa,QACb,UAAA,QACA,YAAA,QAEA,EFwhCA,MAAO,QEthCL,gBAAA,KAIF,QH8gCD,QKnkCC,MAAA,QAEA,gBAAA,ULskCD,QGxgCC,QAAS,KAAK,OACd,QAAA,IAAA,KAAA,yBH0gCD,eAAA,KGngCC,OHsgCD,OAAA,ECSD,IACE,eAAgB,ODDjB,4BMhlCC,0BLmlCF,gBKplCE,iBADA,eH4EA,QAAS,MACT,UAAA,KHwgCD,OAAA,KGjgCC,aACA,cAAA,IAEA,eACA,QAAA,aC6FA,UAAA,KACK,OAAA,KACG,QAAA,IEvLR,YAAA,WACA,iBAAA,KACA,OAAA,IAAA,MAAA,KNgmCD,cAAA,IGlgCC,mBAAoB,IAAI,IAAI,YAC5B,cAAA,IAAA,IAAA,YHogCD,WAAA,IAAA,IAAA,YG7/BC,YACA,cAAA,IAEA,GHggCD,WAAA,KGx/BC,cAAe,KACf,OAAA,EACA,WAAA,IAAA,MAAA,KAEA,SACA,SAAA,SACA,MAAA,IACA,OAAA,IACA,QAAA,EH0/BD,OAAA,KGl/BC,SAAA,OF2/BA,KAAM,cEz/BJ,OAAA,EAEA,0BACA,yBACA,SAAA,OACA,MAAA,KHo/BH,OAAA,KGz+BC,OAAQ,EACR,SAAA,QH2+BD,KAAA,KCSD,cACE,OAAQ,QAQV,IACA,IMnpCE,IACA,IACA,IACA,INyoCF,GACA,GACA,GACA,GACA,GACA,GDAC,YAAA,QOnpCC,YAAa,IN4pCb,YAAa,IACb,MAAO,QAoBT,WAZA,UAaA,WAZA,UM7pCI,WN8pCJ,UM7pCI,WN8pCJ,UM7pCI,WN8pCJ,UDMC,WCLD,UACA,UAZA,SAaA,UAZA,SAaA,UAZA,SAaA,UAZA,SAaA,UAZA,SAaA,UAZA,SMrpCE,YAAa,INyqCb,YAAa,EACb,MAAO,KAGT,IMzqCE,IAJF,IN4qCA,GAEA,GDLC,GCSC,WAAY,KACZ,cAAe,KASjB,WANA,UDCC,WCCD,UM7qCA,WN+qCA,UACA,UANA,SM7qCI,UN+qCJ,SM5qCA,UN8qCA,SAQE,UAAW,IAGb,IMrrCE,IAJF,INwrCA,GAEA,GDLC,GCSC,WAAY,KACZ,cAAe,KASjB,WANA,UDCC,WCCD,UMxrCA,WN0rCA,UACA,UANA,SMzrCI,UN2rCJ,SMvrCA,UNyrCA,SMzrCU,UAAA,IACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KAOR,IADF,GPusCC,UAAA,KCSD,EM1sCE,OAAA,EAAA,EAAA,KAEA,MPqsCD,cAAA,KOhsCC,UAAW,KAwOX,YAAa,IA1OX,YAAA,IPusCH,yBO9rCC,MNusCE,UAAW,MMlsCf,OAAA,MAEE,UAAA,IAKF,MP2rCC,KO3rCsB,QAAA,KP8rCtB,iBAAA,QO7rCsB,WPgsCtB,WAAA,KO/rCsB,YPksCtB,WAAA,MOjsCsB,aPosCtB,WAAA,OOnsCsB,cPssCtB,WAAA,QOnsCsB,aPssCtB,YAAA,OOrsCsB,gBPwsCtB,eAAA,UOvsCsB,gBP0sCtB,eAAA,UOtsCC,iBPysCD,eAAA,WQ5yCC,YR+yCD,MAAA,KCSD,cOrzCI,MAAA,QAHF,qBDwGF,qBP8sCC,MAAA,QCSD,cO5zCI,MAAA,QAHF,qBD2GF,qBPktCC,MAAA,QCSD,WOn0CI,MAAA,QAHF,kBD8GF,kBPstCC,MAAA,QCSD,cO10CI,MAAA,QAHF,qBDiHF,qBP0tCC,MAAA,QCSD,aOj1CI,MAAA,QDwHF,oBAHF,oBExHE,MAAA,QACA,YR21CA,MAAO,KQz1CL,iBAAA,QAHF,mBF8HF,mBP4tCC,iBAAA,QCSD,YQh2CI,iBAAA,QAHF,mBFiIF,mBPguCC,iBAAA,QCSD,SQv2CI,iBAAA,QAHF,gBFoIF,gBPouCC,iBAAA,QCSD,YQ92CI,iBAAA,QAHF,mBFuIF,mBPwuCC,iBAAA,QCSD,WQr3CI,iBAAA,QF6IF,kBADF,kBAEE,iBAAA,QPuuCD,aO9tCC,eAAgB,INuuChB,OAAQ,KAAK,EAAE,KMruCf,cAAA,IAAA,MAAA,KAFF,GPmuCC,GCSC,WAAY,EACZ,cAAe,KM/tCf,MP2tCD,MO5tCD,MAPI,MASF,cAAA,EAIF,eALE,aAAA,EACA,WAAA,KPmuCD,aO/tCC,aAAc,EAKZ,YAAA,KACA,WAAA,KP8tCH,gBOxtCC,QAAS,aACT,cAAA,IACA,aAAA,IAEF,GNiuCE,WAAY,EM/tCZ,cAAA,KAGA,GADF,GP2tCC,YAAA,WOvtCC,GP0tCD,YAAA,IOpnCD,GAvFM,YAAA,EAEA,yBACA,kBGtNJ,MAAA,KACA,MAAA,MACA,SAAA,OVs6CC,MAAA,KO9nCC,WAAY,MAhFV,cAAA,SPitCH,YAAA,OOvsCD,kBNitCE,YAAa,OM3sCjB,0BPusCC,YOtsCC,OAAA,KA9IqB,cAAA,IAAA,OAAA,KAmJvB,YACE,UAAA,IACA,eAAA,UAEA,WPusCD,QAAA,KAAA,KOlsCG,OAAA,EAAA,EAAA,KN2sCF,UAAW,OACX,YAAa,IAAI,MAAM,KMrtCzB,yBPgtCC,wBOhtCD,yBN0tCE,cAAe,EMpsCb,kBAFA,kBACA,iBPmsCH,QAAA,MOhsCG,UAAA,INysCF,YAAa,WACb,MAAO,KMjsCT,yBP4rCC,yBO5rCD,wBAEE,QAAA,cAEA,oBACA,sBACA,cAAA,KP8rCD,aAAA,EOxrCG,WAAA,MNisCF,aAAc,IAAI,MAAM,KACxB,YAAa,EMjsCX,kCNmsCJ,kCMpsCe,iCACX,oCNosCJ,oCDLC,mCCUC,QAAS,GMlsCX,iCNosCA,iCM1sCM,gCAOJ,mCNosCF,mCDLC,kCO9rCC,QAAA,cPmsCD,QWx+CC,cAAe,KVi/Cf,WAAY,OACZ,YAAa,WU9+Cb,KX0+CD,IWt+CD,IACE,KACA,YAAA,MAAA,OAAA,SAAA,cAAA,UAEA,KACA,QAAA,IAAA,IXw+CD,UAAA,IWp+CC,MAAO,QACP,iBAAA,QACA,cAAA,IAEA,IACA,QAAA,IAAA,IACA,UAAA,IV6+CA,MU7+CA,KXs+CD,iBAAA,KW5+CC,cAAe,IASb,mBAAA,MAAA,EAAA,KAAA,EAAA,gBACA,WAAA,MAAA,EAAA,KAAA,EAAA,gBAEA,QV8+CF,QU9+CE,EXs+CH,UAAA,KWj+CC,YAAa,IACb,mBAAA,KACA,WAAA,KAEA,IACA,QAAA,MACA,QAAA,MACA,OAAA,EAAA,EAAA,KACA,UAAA,KACA,YAAA,WACA,MAAA,KACA,WAAA,UXm+CD,UAAA,WW9+CC,iBAAkB,QAehB,OAAA,IAAA,MAAA,KACA,cAAA,IAEA,SACA,QAAA,EACA,UAAA,QXk+CH,MAAA,QW79CC,YAAa,SACb,iBAAA,YACA,cAAA,EC1DF,gBCHE,WAAA,MACA,WAAA,OAEA,Wb+hDD,cAAA,KYzhDC,aAAA,KAqEA,aAAc,KAvEZ,YAAA,KZgiDH,yBY3hDC,WAkEE,MAAO,OZ89CV,yBY7hDC,WA+DE,MAAO,OZm+CV,0BY1hDC,WCvBA,MAAA,QAGA,iBbojDD,cAAA,KYvhDC,aAAc,KCvBd,aAAA,KACA,YAAA,KCAE,KACE,aAAA,MAEA,YAAA,MAGA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UdijDL,SAAA,ScjiDG,WAAA,IACE,cAAA,KdmiDL,aAAA,Kc3hDG,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Ud8hDH,MAAA,Kc9hDG,WdiiDH,MAAA,KcjiDG,WdoiDH,MAAA,acpiDG,WduiDH,MAAA,acviDG,Ud0iDH,MAAA,Ic1iDG,Ud6iDH,MAAA,ac7iDG,UdgjDH,MAAA,achjDG,UdmjDH,MAAA,IcnjDG,UdsjDH,MAAA,actjDG,UdyjDH,MAAA,aczjDG,Ud4jDH,MAAA,Ic5jDG,Ud+jDH,MAAA,achjDG,UdmjDH,MAAA,YcnjDG,gBdsjDH,MAAA,KctjDG,gBdyjDH,MAAA,aczjDG,gBd4jDH,MAAA,ac5jDG,ed+jDH,MAAA,Ic/jDG,edkkDH,MAAA,aclkDG,edqkDH,MAAA,acrkDG,edwkDH,MAAA,IcxkDG,ed2kDH,MAAA,ac3kDG,ed8kDH,MAAA,ac9kDG,edilDH,MAAA,IcjlDG,edolDH,MAAA,ac/kDG,edklDH,MAAA,YcjmDG,edomDH,MAAA,KcpmDG,gBdumDH,KAAA,KcvmDG,gBd0mDH,KAAA,ac1mDG,gBd6mDH,KAAA,ac7mDG,edgnDH,KAAA,IchnDG,edmnDH,KAAA,acnnDG,edsnDH,KAAA,actnDG,edynDH,KAAA,IcznDG,ed4nDH,KAAA,ac5nDG,ed+nDH,KAAA,ac/nDG,edkoDH,KAAA,IcloDG,edqoDH,KAAA,achoDG,edmoDH,KAAA,YcpnDG,edunDH,KAAA,KcvnDG,kBd0nDH,YAAA,Kc1nDG,kBd6nDH,YAAA,ac7nDG,kBdgoDH,YAAA,achoDG,iBdmoDH,YAAA,IcnoDG,iBdsoDH,YAAA,actoDG,iBdyoDH,YAAA,aczoDG,iBd4oDH,YAAA,Ic5oDG,iBd+oDH,YAAA,ac/oDG,iBdkpDH,YAAA,aclpDG,iBdqpDH,YAAA,IcrpDG,iBdwpDH,YAAA,acxpDG,iBd2pDH,YAAA,Yc7rDG,iBACE,YAAA,EAOJ,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Ud2rDD,MAAA,Kc3rDC,Wd8rDD,MAAA,Kc9rDC,WdisDD,MAAA,acjsDC,WdosDD,MAAA,acpsDC,UdusDD,MAAA,IcvsDC,Ud0sDD,MAAA,ac1sDC,Ud6sDD,MAAA,ac7sDC,UdgtDD,MAAA,IchtDC,UdmtDD,MAAA,acntDC,UdstDD,MAAA,acttDC,UdytDD,MAAA,IcztDC,Ud4tDD,MAAA,ac7sDC,UdgtDD,MAAA,YchtDC,gBdmtDD,MAAA,KcntDC,gBdstDD,MAAA,acttDC,gBdytDD,MAAA,acztDC,ed4tDD,MAAA,Ic5tDC,ed+tDD,MAAA,ac/tDC,edkuDD,MAAA,acluDC,edquDD,MAAA,IcruDC,edwuDD,MAAA,acxuDC,ed2uDD,MAAA,ac3uDC,ed8uDD,MAAA,Ic9uDC,edivDD,MAAA,ac5uDC,ed+uDD,MAAA,Yc9vDC,ediwDD,MAAA,KcjwDC,gBdowDD,KAAA,KcpwDC,gBduwDD,KAAA,acvwDC,gBd0wDD,KAAA,ac1wDC,ed6wDD,KAAA,Ic7wDC,edgxDD,KAAA,achxDC,edmxDD,KAAA,acnxDC,edsxDD,KAAA,IctxDC,edyxDD,KAAA,aczxDC,ed4xDD,KAAA,ac5xDC,ed+xDD,KAAA,Ic/xDC,edkyDD,KAAA,ac7xDC,edgyDD,KAAA,YcjxDC,edoxDD,KAAA,KcpxDC,kBduxDD,YAAA,KcvxDC,kBd0xDD,YAAA,ac1xDC,kBd6xDD,YAAA,ac7xDC,iBdgyDD,YAAA,IchyDC,iBdmyDD,YAAA,acnyDC,iBdsyDD,YAAA,actyDC,iBdyyDD,YAAA,IczyDC,iBd4yDD,YAAA,ac5yDC,iBd+yDD,YAAA,ac/yDC,iBdkzDD,YAAA,IclzDC,iBdqzDD,YAAA,acrzDC,iBdwzDD,YAAA,YY/yDD,iBE3CE,YAAA,GAQF,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Udy1DD,MAAA,Kcz1DC,Wd41DD,MAAA,Kc51DC,Wd+1DD,MAAA,ac/1DC,Wdk2DD,MAAA,acl2DC,Udq2DD,MAAA,Icr2DC,Udw2DD,MAAA,acx2DC,Ud22DD,MAAA,ac32DC,Ud82DD,MAAA,Ic92DC,Udi3DD,MAAA,acj3DC,Udo3DD,MAAA,acp3DC,Udu3DD,MAAA,Icv3DC,Ud03DD,MAAA,ac32DC,Ud82DD,MAAA,Yc92DC,gBdi3DD,MAAA,Kcj3DC,gBdo3DD,MAAA,acp3DC,gBdu3DD,MAAA,acv3DC,ed03DD,MAAA,Ic13DC,ed63DD,MAAA,ac73DC,edg4DD,MAAA,ach4DC,edm4DD,MAAA,Icn4DC,eds4DD,MAAA,act4DC,edy4DD,MAAA,acz4DC,ed44DD,MAAA,Ic54DC,ed+4DD,MAAA,ac14DC,ed64DD,MAAA,Yc55DC,ed+5DD,MAAA,Kc/5DC,gBdk6DD,KAAA,Kcl6DC,gBdq6DD,KAAA,acr6DC,gBdw6DD,KAAA,acx6DC,ed26DD,KAAA,Ic36DC,ed86DD,KAAA,ac96DC,edi7DD,KAAA,acj7DC,edo7DD,KAAA,Icp7DC,edu7DD,KAAA,acv7DC,ed07DD,KAAA,ac17DC,ed67DD,KAAA,Ic77DC,edg8DD,KAAA,ac37DC,ed87DD,KAAA,Yc/6DC,edk7DD,KAAA,Kcl7DC,kBdq7DD,YAAA,Kcr7DC,kBdw7DD,YAAA,acx7DC,kBd27DD,YAAA,ac37DC,iBd87DD,YAAA,Ic97DC,iBdi8DD,YAAA,acj8DC,iBdo8DD,YAAA,acp8DC,iBdu8DD,YAAA,Icv8DC,iBd08DD,YAAA,ac18DC,iBd68DD,YAAA,ac78DC,iBdg9DD,YAAA,Ich9DC,iBdm9DD,YAAA,acn9DC,iBds9DD,YAAA,YY18DD,iBE9CE,YAAA,GAQF,0BACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Udu/DD,MAAA,Kcv/DC,Wd0/DD,MAAA,Kc1/DC,Wd6/DD,MAAA,ac7/DC,WdggED,MAAA,achgEC,UdmgED,MAAA,IcngEC,UdsgED,MAAA,actgEC,UdygED,MAAA,aczgEC,Ud4gED,MAAA,Ic5gEC,Ud+gED,MAAA,ac/gEC,UdkhED,MAAA,aclhEC,UdqhED,MAAA,IcrhEC,UdwhED,MAAA,aczgEC,Ud4gED,MAAA,Yc5gEC,gBd+gED,MAAA,Kc/gEC,gBdkhED,MAAA,aclhEC,gBdqhED,MAAA,acrhEC,edwhED,MAAA,IcxhEC,ed2hED,MAAA,ac3hEC,ed8hED,MAAA,ac9hEC,ediiED,MAAA,IcjiEC,edoiED,MAAA,acpiEC,eduiED,MAAA,acviEC,ed0iED,MAAA,Ic1iEC,ed6iED,MAAA,acxiEC,ed2iED,MAAA,Yc1jEC,ed6jED,MAAA,Kc7jEC,gBdgkED,KAAA,KchkEC,gBdmkED,KAAA,acnkEC,gBdskED,KAAA,actkEC,edykED,KAAA,IczkEC,ed4kED,KAAA,ac5kEC,ed+kED,KAAA,ac/kEC,edklED,KAAA,IcllEC,edqlED,KAAA,acrlEC,edwlED,KAAA,acxlEC,ed2lED,KAAA,Ic3lEC,ed8lED,KAAA,aczlEC,ed4lED,KAAA,Yc7kEC,edglED,KAAA,KchlEC,kBdmlED,YAAA,KcnlEC,kBdslED,YAAA,actlEC,kBdylED,YAAA,aczlEC,iBd4lED,YAAA,Ic5lEC,iBd+lED,YAAA,ac/lEC,iBdkmED,YAAA,aclmEC,iBdqmED,YAAA,IcrmEC,iBdwmED,YAAA,acxmEC,iBd2mED,YAAA,ac3mEC,iBd8mED,YAAA,Ic9mEC,iBdinED,YAAA,acjnEC,iBdonED,YAAA,YevrED,iBACA,YAAA,GAGA,MACA,iBAAA,YAEA,Qf0rED,YAAA,IexrEC,eAAgB,IAChB,MAAA,Kf0rED,WAAA,KenrEC,GACA,WAAA,KfurED,OezrEC,MAAO,KdosEP,UAAW,KACX,cAAe,KcxrET,mBd2rER,mBc1rEQ,mBAHA,mBACA,mBd2rER,mBDHC,QAAA,IepsEC,YAAa,WAoBX,eAAA,IACA,WAAA,IAAA,MAAA,KArBJ,mBdmtEE,eAAgB,OAChB,cAAe,IAAI,MAAM,KDJ1B,uCCMD,uCcttEA,wCdutEA,wCcnrEI,2CANI,2CfqrEP,WAAA,Ee1qEG,mBf6qEH,WAAA,IAAA,MAAA,KCWD,cACE,iBAAkB,KchqEpB,6BdmqEA,6BclqEE,6BAZM,6BfuqEP,6BCMD,6BDHC,QAAA,ICWD,gBACE,OAAQ,IAAI,MAAM,Kc3qEpB,4Bd8qEA,4Bc9qEA,4BAQQ,4Bf+pEP,4BCMD,4Bc9pEM,OAAA,IAAA,MAAA,KAYF,4BAFJ,4BfqpEC,oBAAA,IexoEG,yCf2oEH,iBAAA,QejoEC,4BACA,iBAAA,QfqoED,uBe/nEG,SAAA,Od0oEF,QAAS,aczoEL,MAAA,KAEA,sBfkoEL,sBgB9wEC,SAAA,OfyxEA,QAAS,WACT,MAAO,KAST,0BetxEE,0BfgxEF,0BAGA,0BezxEM,0BAMJ,0BfixEF,0BAGA,0BACA,0BDNC,0BCAD,0BAGA,0BASE,iBAAkB,QDLnB,sCgBnyEC,sCAAA,oCf0yEF,sCevxEM,sCf4xEJ,iBAAkB,QASpB,2Be3yEE,2BfqyEF,2BAGA,2Be9yEM,2BAMJ,2BfsyEF,2BAGA,2BACA,2BDNC,2BCAD,2BAGA,2BASE,iBAAkB,QDLnB,uCgBxzEC,uCAAA,qCf+zEF,uCe5yEM,uCfizEJ,iBAAkB,QASpB,wBeh0EE,wBf0zEF,wBAGA,wBen0EM,wBAMJ,wBf2zEF,wBAGA,wBACA,wBDNC,wBCAD,wBAGA,wBASE,iBAAkB,QDLnB,oCgB70EC,oCAAA,kCfo1EF,oCej0EM,oCfs0EJ,iBAAkB,QASpB,2Ber1EE,2Bf+0EF,2BAGA,2Bex1EM,2BAMJ,2Bfg1EF,2BAGA,2BACA,2BDNC,2BCAD,2BAGA,2BASE,iBAAkB,QDLnB,uCgBl2EC,uCAAA,qCfy2EF,uCet1EM,uCf21EJ,iBAAkB,QASpB,0Be12EE,0Bfo2EF,0BAGA,0Be72EM,0BAMJ,0Bfq2EF,0BAGA,0BACA,0BDNC,0BCAD,0BAGA,0BASE,iBAAkB,QDLnB,sCejtEC,sCADF,oCdytEA,sCe32EM,sCDoJJ,iBAAA,QA6DF,kBACE,WAAY,KA3DV,WAAA,KAEA,oCACA,kBACA,MAAA,KfqtED,cAAA,Ke9pEC,WAAY,OAnDV,mBAAA,yBfotEH,OAAA,IAAA,MAAA,KCWD,yBACE,cAAe,Ec7qEjB,qCdgrEA,qCcltEI,qCARM,qCfmtET,qCCMD,qCDHC,YAAA,OCWD,kCACE,OAAQ,EcxrEV,0Dd2rEA,0Dc3rEA,0DAzBU,0Df6sET,0DCMD,0DAME,YAAa,EchsEf,yDdmsEA,yDcnsEA,yDArBU,yDfitET,yDCMD,yDAME,aAAc,EDLjB,yDe3sEW,yDEzNV,yDjBm6EC,yDiBl6ED,cAAA,GAMA,SjBm6ED,UAAA,EiBh6EC,QAAS,EACT,OAAA,EACA,OAAA,EAEA,OACA,QAAA,MACA,MAAA,KACA,QAAA,EACA,cAAA,KACA,UAAA,KjBk6ED,YAAA,QiB/5EC,MAAO,KACP,OAAA,EACA,cAAA,IAAA,MAAA,QAEA,MjBi6ED,QAAA,aiBt5EC,UAAW,Kb4BX,cAAA,IACG,YAAA,IJ83EJ,mBiBt5EC,mBAAoB,WhBi6EjB,gBAAiB,WgB/5EpB,WAAA,WjB05ED,qBiBx5EC,kBAGA,OAAQ,IAAI,EAAE,EACd,WAAA,MjBu5ED,YAAA,OiBl5EC,iBACA,QAAA,MAIF,kBhB45EE,QAAS,MgB15ET,MAAA,KAIF,iBAAA,ahB25EE,OAAQ,KIh+ER,uBL29ED,2BK19EC,wBY2EA,QAAS,KAAK,OACd,QAAA,IAAA,KAAA,yBACA,eAAA,KAEA,OACA,QAAA,MjBi5ED,YAAA,IiBv3EC,UAAW,KACX,YAAA,WACA,MAAA,KAEA,cACA,QAAA,MACA,MAAA,KACA,OAAA,KACA,QAAA,IAAA,KACA,UAAA,KACA,YAAA,WACA,MAAA,KbxDA,iBAAA,KACQ,iBAAA,KAyHR,OAAA,IAAA,MAAA,KACK,cAAA,IACG,mBAAA,MAAA,EAAA,IAAA,IAAA,iBJ0zET,WAAA,MAAA,EAAA,IAAA,IAAA,iBkBl8EC,mBAAA,aAAA,YAAA,KAAA,mBAAA,YAAA,KACE,cAAA,aAAA,YAAA,KAAA,WAAA,YAAA,KACA,WAAA,aAAA,YAAA,KAAA,WAAA,YAAA,KdWM,oBJ27ET,aAAA,QI15EC,QAAA,EACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,qBACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,qBAEF,gCAA0B,MAAA,KJ65E3B,QAAA,EI55EiC,oCJ+5EjC,MAAA,KiBl4EG,yCACA,MAAA,KAQF,0BhBw4EA,iBAAkB,YAClB,OAAQ,EgBr4EN,wBjB+3EH,wBiB53EC,iChBu4EA,iBAAkB,KgBr4EhB,QAAA,EAIF,wBACE,iCjB43EH,OAAA,YiB/2EC,sBjBk3ED,OAAA,KiBh2EG,mBhB42EF,mBAAoB,KAEtB,qDgB72EM,8BjBs2EH,8BiBn2EC,wCAAA,+BhB+2EA,YAAa,KgB72EX,iCjB22EH,iCiBx2EC,2CAAA,kChB42EF,0BACA,0BACA,oCACA,2BAKE,YAAa,KgBl3EX,iCjBg3EH,iCACF,2CiBt2EC,kChBy2EA,0BACA,0BACA,oCACA,2BgB32EA,YAAA,MhBm3EF,YgBz2EE,cAAA,KAGA,UADA,OjBm2ED,SAAA,SiBv2EC,QAAS,MhBk3ET,WAAY,KgB12EV,cAAA,KAGA,gBADA,aAEA,WAAA,KjBm2EH,aAAA,KiBh2EC,cAAe,EhB22Ef,YAAa,IACb,OAAQ,QgBt2ER,+BjBk2ED,sCiBp2EC,yBACA,gCAIA,SAAU,ShB02EV,WAAY,MgBx2EZ,YAAA,MAIF,oBAAA,cAEE,WAAA,KAGA,iBADA,cAEA,SAAA,SACA,QAAA,aACA,aAAA,KjB+1ED,cAAA,EiB71EC,YAAa,IhBw2Eb,eAAgB,OgBt2EhB,OAAA,QAUA,kCjBs1ED,4BCWC,WAAY,EACZ,YAAa,KgBz1Eb,wCAAA,qCjBq1ED,8BCOD,+BgBl2EI,2BhBi2EJ,4BAME,OAAQ,YDNT,0BiBz1EG,uBAMF,oCAAA,iChB+1EA,OAAQ,YDNT,yBiBt1EK,sBAaJ,mCAFF,gCAGE,OAAA,YAGA,qBjB20ED,WAAA,KiBz0EC,YAAA,IhBo1EA,eAAgB,IgBl1Ed,cAAA,EjB40EH,8BiB9zED,8BCnQE,cAAA,EACA,aAAA,EAEA,UACA,OAAA,KlBokFD,QAAA,IAAA,KkBlkFC,UAAA,KACE,YAAA,IACA,cAAA,IAGF,gBjB4kFA,OAAQ,KiB1kFN,YAAA,KD2PA,0BAFJ,kBAGI,OAAA,KAEA,6BACA,OAAA,KjB20EH,QAAA,IAAA,KiBj1EC,UAAW,KAST,YAAA,IACA,cAAA,IAVJ,mChBg2EE,OAAQ,KgBl1EN,YAAA,KAGA,6CAjBJ,qCAkBI,OAAA,KAEA,oCACA,OAAA,KjB20EH,WAAA,KiBv0EC,QAAS,IAAI,KC/Rb,UAAA,KACA,YAAA,IAEA,UACA,OAAA,KlBymFD,QAAA,KAAA,KkBvmFC,UAAA,KACE,YAAA,UACA,cAAA,IAGF,gBjBinFA,OAAQ,KiB/mFN,YAAA,KDuRA,0BAFJ,kBAGI,OAAA,KAEA,6BACA,OAAA,KjBo1EH,QAAA,KAAA,KiB11EC,UAAW,KAST,YAAA,UACA,cAAA,IAVJ,mChBy2EE,OAAQ,KgB31EN,YAAA,KAGA,6CAjBJ,qCAkBI,OAAA,KAEA,oCACA,OAAA,KjBo1EH,WAAA,KiB30EC,QAAS,KAAK,KAEd,UAAA,KjB40ED,YAAA,UiBx0EG,cjB20EH,SAAA,SiBt0EC,4BACA,cAAA,OAEA,uBACA,SAAA,SACA,IAAA,EACA,MAAA,EACA,QAAA,EACA,QAAA,MACA,MAAA,KjBy0ED,OAAA,KiBv0EC,YAAa,KhBk1Eb,WAAY,OACZ,eAAgB,KDLjB,oDiBz0EC,uCADA,iCAGA,MAAO,KhBk1EP,OAAQ,KACR,YAAa,KDLd,oDiBz0EC,uCADA,iCAKA,MAAO,KhBg1EP,OAAQ,KACR,YAAa,KAKf,uBAEA,8BAJA,4BADA,yBAEA,oBAEA,2BDNC,4BkBvuFG,mCAJA,yBD0ZJ,gCbvWE,MAAA,QJ6rFD,2BkB1uFG,aAAA,QACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBd4CJ,WAAA,MAAA,EAAA,IAAA,IAAA,iBJksFD,iCiB31EC,aAAc,QC5YZ,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QlB2uFH,gCiBh2EC,MAAO,QCtYL,iBAAA,QlByuFH,aAAA,QCWD,oCACE,MAAO,QAKT,uBAEA,8BAJA,4BADA,yBAEA,oBAEA,2BDNC,4BkBrwFG,mCAJA,yBD6ZJ,gCb1WE,MAAA,QJ2tFD,2BkBxwFG,aAAA,QACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBd4CJ,WAAA,MAAA,EAAA,IAAA,IAAA,iBJguFD,iCiBt3EC,aAAc,QC/YZ,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QlBywFH,gCiB33EC,MAAO,QCzYL,iBAAA,QlBuwFH,aAAA,QCWD,oCACE,MAAO,QAKT,qBAEA,4BAJA,0BADA,uBAEA,kBAEA,yBDNC,0BkBnyFG,iCAJA,uBDgaJ,8Bb7WE,MAAA,QJyvFD,yBkBtyFG,aAAA,QACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBd4CJ,WAAA,MAAA,EAAA,IAAA,IAAA,iBJ8vFD,+BiBj5EC,aAAc,QClZZ,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QlBuyFH,8BiBt5EC,MAAO,QC5YL,iBAAA,QlBqyFH,aAAA,QiBj5EG,kCjBo5EH,MAAA,QiBj5EG,2CjBo5EH,IAAA,KiBz4EC,mDACA,IAAA,EAEA,YjB44ED,QAAA,MiBzzEC,WAAY,IAwEZ,cAAe,KAtIX,MAAA,QAEA,yBjB23EH,yBiBvvEC,QAAS,aA/HP,cAAA,EACA,eAAA,OjB03EH,2BiB5vEC,QAAS,aAxHP,MAAA,KjBu3EH,eAAA,OiBn3EG,kCACA,QAAA,aAmHJ,0BhB8wEE,QAAS,aACT,eAAgB,OgBv3Ed,wCjBg3EH,6CiBxwED,2CjB2wEC,MAAA,KiB/2EG,wCACA,MAAA,KAmGJ,4BhB0xEE,cAAe,EgBt3Eb,eAAA,OAGA,uBADA,oBjBg3EH,QAAA,aiBtxEC,WAAY,EhBiyEZ,cAAe,EgBv3EX,eAAA,OAsFN,6BAAA,0BAjFI,aAAA,EAiFJ,4CjB+xEC,sCiB12EG,SAAA,SjB62EH,YAAA,EiBl2ED,kDhB82EE,IAAK,GgBp2EL,2BjBi2EH,kCiBl2EG,wBAEA,+BAXF,YAAa,IhBs3Eb,WAAY,EgBr2EV,cAAA,EJviBF,2BIshBF,wBJrhBE,WAAA,KI4jBA,6BAyBA,aAAc,MAnCV,YAAA,MAEA,yBjB01EH,gCACF,YAAA,IiB13EG,cAAe,EAwCf,WAAA,OAwBJ,sDAdQ,MAAA,KjBg1EL,yBACF,+CiBr0EC,YAAA,KAEE,UAAW,MjBw0EZ,yBACF,+CmBt6FG,YAAa,IACf,UAAA,MAGA,KACA,QAAA,aACA,QAAA,IAAA,KAAA,cAAA,EACA,UAAA,KACA,YAAA,IACA,YAAA,WACA,WAAA,OC0CA,YAAA,OACA,eAAA,OACA,iBAAA,aACA,aAAA,ahB+JA,OAAA,QACG,oBAAA,KACC,iBAAA,KACI,gBAAA,KJiuFT,YAAA,KmBz6FG,iBAAA,KlBq7FF,OAAQ,IAAI,MAAM,YAClB,cAAe,IDHhB,kBKx8FC,kBAEA,WACA,kBJ28FF,kBADA,WkBl7FE,QAAA,KAAA,OlBy7FA,QAAS,IAAI,KAAK,yBAClB,eAAgB,KkBn7FhB,WnB46FD,WmB/6FG,WlB27FF,MAAO,KkBt7FL,gBAAA,Kf6BM,YADR,YJq5FD,iBAAA,KmB56FC,QAAA,ElBw7FA,mBAAoB,MAAM,EAAE,IAAI,IAAI,iBAC5B,WAAY,MAAM,EAAE,IAAI,IAAI,iBoBn+FpC,cAGA,ejB8DA,wBACQ,OAAA,YJ65FT,OAAA,kBmB56FG,mBAAA,KlBw7FM,WAAY,KkBt7FhB,QAAA,IASN,eC3DE,yBACA,eAAA,KpBo+FD,aoBj+FC,MAAA,KnB6+FA,iBAAkB,KmB3+FhB,aAAA,KpBq+FH,mBoBn+FO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBo+FH,mBoBj+FC,MAAA,KnB6+FA,iBAAkB,QAClB,aAAc,QmBz+FR,oBADJ,oBpBo+FH,mCoBj+FG,MAAA,KnB6+FF,iBAAkB,QAClB,aAAc,QmBz+FN,0BnB++FV,0BAHA,0BmB7+FM,0BnB++FN,0BAHA,0BDFC,yCoB3+FK,yCnB++FN,yCmB1+FE,MAAA,KnBk/FA,iBAAkB,QAClB,aAAc,QmB3+FZ,oBpBm+FH,oBoBn+FG,mCnBg/FF,iBAAkB,KmB5+FV,4BnBi/FV,4BAHA,4BDHC,6BCOD,6BAHA,6BkB99FA,sCClBM,sCnBi/FN,sCmB3+FI,iBAAA,KACA,aAAA,KDcJ,oBC9DE,MAAA,KACA,iBAAA,KpB6hGD,aoB1hGC,MAAA,KnBsiGA,iBAAkB,QmBpiGhB,aAAA,QpB8hGH,mBoB5hGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB6hGH,mBoB1hGC,MAAA,KnBsiGA,iBAAkB,QAClB,aAAc,QmBliGR,oBADJ,oBpB6hGH,mCoB1hGG,MAAA,KnBsiGF,iBAAkB,QAClB,aAAc,QmBliGN,0BnBwiGV,0BAHA,0BmBtiGM,0BnBwiGN,0BAHA,0BDFC,yCoBpiGK,yCnBwiGN,yCmBniGE,MAAA,KnB2iGA,iBAAkB,QAClB,aAAc,QmBpiGZ,oBpB4hGH,oBoB5hGG,mCnByiGF,iBAAkB,KmBriGV,4BnB0iGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBphGA,sCCrBM,sCnB0iGN,sCmBpiGI,iBAAA,QACA,aAAA,QDkBJ,oBClEE,MAAA,QACA,iBAAA,KpBslGD,aoBnlGC,MAAA,KnB+lGA,iBAAkB,QmB7lGhB,aAAA,QpBulGH,mBoBrlGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBslGH,mBoBnlGC,MAAA,KnB+lGA,iBAAkB,QAClB,aAAc,QmB3lGR,oBADJ,oBpBslGH,mCoBnlGG,MAAA,KnB+lGF,iBAAkB,QAClB,aAAc,QmB3lGN,0BnBimGV,0BAHA,0BmB/lGM,0BnBimGN,0BAHA,0BDFC,yCoB7lGK,yCnBimGN,yCmB5lGE,MAAA,KnBomGA,iBAAkB,QAClB,aAAc,QmB7lGZ,oBpBqlGH,oBoBrlGG,mCnBkmGF,iBAAkB,KmB9lGV,4BnBmmGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBzkGA,sCCzBM,sCnBmmGN,sCmB7lGI,iBAAA,QACA,aAAA,QDsBJ,oBCtEE,MAAA,QACA,iBAAA,KpB+oGD,UoB5oGC,MAAA,KnBwpGA,iBAAkB,QmBtpGhB,aAAA,QpBgpGH,gBoB9oGO,gBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB+oGH,gBoB5oGC,MAAA,KnBwpGA,iBAAkB,QAClB,aAAc,QmBppGR,iBADJ,iBpB+oGH,gCoB5oGG,MAAA,KnBwpGF,iBAAkB,QAClB,aAAc,QmBppGN,uBnB0pGV,uBAHA,uBmBxpGM,uBnB0pGN,uBAHA,uBDFC,sCoBtpGK,sCnB0pGN,sCmBrpGE,MAAA,KnB6pGA,iBAAkB,QAClB,aAAc,QmBtpGZ,iBpB8oGH,iBoB9oGG,gCnB2pGF,iBAAkB,KmBvpGV,yBnB4pGV,yBAHA,yBDHC,0BCOD,0BAHA,0BkB9nGA,mCC7BM,mCnB4pGN,mCmBtpGI,iBAAA,QACA,aAAA,QD0BJ,iBC1EE,MAAA,QACA,iBAAA,KpBwsGD,aoBrsGC,MAAA,KnBitGA,iBAAkB,QmB/sGhB,aAAA,QpBysGH,mBoBvsGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBwsGH,mBoBrsGC,MAAA,KnBitGA,iBAAkB,QAClB,aAAc,QmB7sGR,oBADJ,oBpBwsGH,mCoBrsGG,MAAA,KnBitGF,iBAAkB,QAClB,aAAc,QmB7sGN,0BnBmtGV,0BAHA,0BmBjtGM,0BnBmtGN,0BAHA,0BDFC,yCoB/sGK,yCnBmtGN,yCmB9sGE,MAAA,KnBstGA,iBAAkB,QAClB,aAAc,QmB/sGZ,oBpBusGH,oBoBvsGG,mCnBotGF,iBAAkB,KmBhtGV,4BnBqtGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBnrGA,sCCjCM,sCnBqtGN,sCmB/sGI,iBAAA,QACA,aAAA,QD8BJ,oBC9EE,MAAA,QACA,iBAAA,KpBiwGD,YoB9vGC,MAAA,KnB0wGA,iBAAkB,QmBxwGhB,aAAA,QpBkwGH,kBoBhwGO,kBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBiwGH,kBoB9vGC,MAAA,KnB0wGA,iBAAkB,QAClB,aAAc,QmBtwGR,mBADJ,mBpBiwGH,kCoB9vGG,MAAA,KnB0wGF,iBAAkB,QAClB,aAAc,QmBtwGN,yBnB4wGV,yBAHA,yBmB1wGM,yBnB4wGN,yBAHA,yBDFC,wCoBxwGK,wCnB4wGN,wCmBvwGE,MAAA,KnB+wGA,iBAAkB,QAClB,aAAc,QmBxwGZ,mBpBgwGH,mBoBhwGG,kCnB6wGF,iBAAkB,KmBzwGV,2BnB8wGV,2BAHA,2BDHC,4BCOD,4BAHA,4BkBxuGA,qCCrCM,qCnB8wGN,qCmBxwGI,iBAAA,QACA,aAAA,QDuCJ,mBACE,MAAA,QACA,iBAAA,KnBkuGD,UmB/tGC,YAAA,IlB2uGA,MAAO,QACP,cAAe,EAEjB,UG5wGE,iBemCE,iBflCM,oBJqwGT,6BmBhuGC,iBAAA,YlB4uGA,mBAAoB,KACZ,WAAY,KkBzuGlB,UAEF,iBAAA,gBnBguGD,gBmB9tGG,aAAA,YnBouGH,gBmBluGG,gBAIA,MAAA,QlB0uGF,gBAAiB,UACjB,iBAAkB,YDNnB,0BmBnuGK,0BAUN,mCATM,mClB8uGJ,MAAO,KmB7yGP,gBAAA,KAGA,mBADA,QpBsyGD,QAAA,KAAA,KmB5tGC,UAAW,KlBwuGX,YAAa,UmBpzGb,cAAA,IAGA,mBADA,QpB6yGD,QAAA,IAAA,KmB/tGC,UAAW,KlB2uGX,YAAa,ImB3zGb,cAAA,IAGA,mBADA,QpBozGD,QAAA,IAAA,ImB9tGC,UAAW,KACX,YAAA,IACA,cAAA,IAIF,WACE,QAAA,MnB8tGD,MAAA,KCYD,sBACE,WAAY,IqB53GZ,6BADF,4BtBq3GC,6BIhsGC,MAAA,KAEQ,MJosGT,QAAA,EsBx3GC,mBAAA,QAAA,KAAA,OACE,cAAA,QAAA,KAAA,OtB03GH,WAAA,QAAA,KAAA,OsBr3GC,StBw3GD,QAAA,EsBt3Ga,UtBy3Gb,QAAA,KsBx3Ga,atB23Gb,QAAA,MsB13Ga,etB63Gb,QAAA,UsBz3GC,kBACA,QAAA,gBlBwKA,YACQ,SAAA,SAAA,OAAA,EAOR,SAAA,OACQ,mCAAA,KAAA,8BAAA,KAGR,2BAAA,KACQ,4BAAA,KAAA,uBAAA,KJ8sGT,oBAAA,KuBx5GC,4BAA6B,OAAQ,WACrC,uBAAA,OAAA,WACA,oBAAA,OAAA,WAEA,OACA,QAAA,aACA,MAAA,EACA,OAAA,EACA,YAAA,IACA,eAAA,OvB05GD,WAAA,IAAA,OuBt5GC,WAAY,IAAI,QtBq6GhB,aAAc,IAAI,MAAM,YsBn6GxB,YAAA,IAAA,MAAA,YAKA,UADF,QvBu5GC,SAAA,SuBj5GC,uBACA,QAAA,EAEA,eACA,SAAA,SACA,IAAA,KACA,KAAA,EACA,QAAA,KACA,QAAA,KACA,MAAA,KACA,UAAA,MACA,QAAA,IAAA,EACA,OAAA,IAAA,EAAA,EACA,UAAA,KACA,WAAA,KACA,WAAA,KnBsBA,iBAAA,KACQ,wBAAA,YmBrBR,gBAAA,YtBk6GA,OsBl6GA,IAAA,MAAA,KvBq5GD,OAAA,IAAA,MAAA,gBuBh5GC,cAAA,IACE,mBAAA,EAAA,IAAA,KAAA,iBACA,WAAA,EAAA,IAAA,KAAA,iBAzBJ,0BCzBE,MAAA,EACA,KAAA,KAEA,wBxBu8GD,OAAA,IuBj7GC,OAAQ,IAAI,EAmCV,SAAA,OACA,iBAAA,QAEA,oBACA,QAAA,MACA,QAAA,IAAA,KACA,MAAA,KvBi5GH,YAAA,IuB34GC,YAAA,WtB25GA,MAAO,KsBz5GL,YAAA,OvB+4GH,0BuB74GG,0BAMF,MAAA,QtBu5GA,gBAAiB,KACjB,iBAAkB,QsBp5GhB,yBAEA,+BADA,+BvB04GH,MAAA,KuBh4GC,gBAAA,KtBg5GA,iBAAkB,QAClB,QAAS,EDZV,2BuB93GC,iCAAA,iCAEE,MAAA,KEzGF,iCF2GE,iCAEA,gBAAA,KvBg4GH,OAAA,YuB33GC,iBAAkB,YAGhB,iBAAA,KvB23GH,OAAA,0DuBt3GG,qBvBy3GH,QAAA,MuBh3GC,QACA,QAAA,EAQF,qBACE,MAAA,EACA,KAAA,KAIF,oBACE,MAAA,KACA,KAAA,EAEA,iBACA,QAAA,MACA,QAAA,IAAA,KvB22GD,UAAA,KuBv2GC,YAAa,WACb,MAAA,KACA,YAAA,OAEA,mBACA,SAAA,MACA,IAAA,EvBy2GD,MAAA,EuBr2GC,OAAQ,EACR,KAAA,EACA,QAAA,IAQF,2BtB+2GE,MAAO,EsB32GL,KAAA,KAEA,eACA,sCvB+1GH,QAAA,GuBt2GC,WAAY,EtBs3GZ,cAAe,IAAI,OsB32GjB,cAAA,IAAA,QAEA,uBvB+1GH,8CuB10GC,IAAK,KAXL,OAAA,KApEA,cAAA,IvB85GC,yBuB11GD,6BA1DA,MAAA,EACA,KAAA,KvBw5GD,kC0BviHG,MAAO,KzBujHP,KAAM,GyBnjHR,W1ByiHD,oB0B7iHC,SAAU,SzB6jHV,QAAS,ayBvjHP,eAAA,OAGA,yB1ByiHH,gBCgBC,SAAU,SACV,MAAO,KyBhjHT,gC1ByiHC,gCCYD,+BAFA,+ByBnjHA,uBANM,uBzB0jHN,sBAFA,sBAQE,QAAS,EyBrjHP,qB1B0iHH,2B0BriHD,2BACE,iC1BuiHD,YAAA,KCgBD,aACE,YAAa,KDZd,kB0B7iHD,wBAAA,0BzB8jHE,MAAO,KDZR,kB0BliHD,wBACE,0B1BoiHD,YAAA,I0B/hHC,yE1BkiHD,cAAA,E2BnlHC,4BACG,YAAA,EDsDL,mEzBgjHE,wBAAyB,E0B/lHzB,2BAAA,E3BolHD,6C0B/hHD,8CACE,uBAAA,E1BiiHD,0BAAA,E0B9hHC,sB1BiiHD,MAAA,KCgBD,8D0BlnHE,cAAA,E3BumHD,mE0B9hHD,oECjEE,wBAAA,EACG,2BAAA,EDqEL,oEzB6iHE,uBAAwB,EyB3iHxB,0BAAA,EAiBF,mCACE,iCACA,QAAA,EAEF,iCACE,cAAA,IACA,aAAA,IAKF,oCtB/CE,cAAA,KACQ,aAAA,KsBkDR,iCtBnDA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBsByDV,0CACE,mBAAA,K1B0gHD,WAAA,K0BtgHC,YACA,YAAA,EAGF,eACE,aAAA,IAAA,IAAA,E1BwgHD,oBAAA,ECgBD,uBACE,aAAc,EAAE,IAAI,IyB7gHlB,yBACA,+BACA,oC1BkgHH,QAAA,M0BzgHC,MAAO,KAcH,MAAA,K1B8/GL,UAAA,KCgBD,oCACE,MAAO,KyBvgHL,8BACA,oC1B4/GH,oC0Bv/GC,0CACE,WAAA,K1By/GH,YAAA,E2BlqHC,4DACC,cAAA,EAQA,sD3B+pHF,uBAAA,I0Bz/GC,wBAAA,IC/KA,2BAAA,EACC,0BAAA,EAQA,sD3BqqHF,uBAAA,E0B1/GC,wBAAyB,EACzB,2BAAA,I1B4/GD,0BAAA,ICgBD,uE0BzrHE,cAAA,E3B8qHD,4E0Bz/GD,6EC7LE,2BAAA,EACC,0BAAA,EDoMH,6EACE,uBAAA,EACA,wBAAA,EAEA,qB1Bu/GD,QAAA,M0B3/GC,MAAO,KzB2gHP,aAAc,MyBpgHZ,gBAAA,SAEA,0B1Bw/GH,gC0BjgHC,QAAS,WAYP,MAAA,K1Bw/GH,MAAA,G0Bp/GG,qC1Bu/GH,MAAA,KCgBD,+CACE,KAAM,KyBh/GF,gDAFA,6C1By+GL,2D0Bx+GK,wDEzOJ,SAAU,SACV,KAAA,cACA,eAAA,K5BotHD,a4BhtHC,SAAA,SACE,QAAA,MACA,gBAAA,S5BmtHH,0B4B3tHC,MAAO,KAeL,cAAA,EACA,aAAA,EAOA,2BACA,SAAA,S5B0sHH,QAAA,E4BxsHG,MAAA,KACE,MAAA,K5B0sHL,cAAA,ECgBD,iCACE,QAAS,EiBtrHT,8BACA,mCACA,sCACA,OAAA,KlB2qHD,QAAA,KAAA,KkBzqHC,UAAA,KjByrHA,YAAa,UACb,cAAe,IiBxrHb,oClB6qHH,yCkB1qHC,4CjB0rHA,OAAQ,KACR,YAAa,KDTd,8C4BltHD,mDAAA,sD3B6tHA,sCACA,2CiB5rHI,8CjBisHF,OAAQ,KiB7sHR,8BACA,mCACA,sCACA,OAAA,KlBksHD,QAAA,IAAA,KkBhsHC,UAAA,KjBgtHA,YAAa,IACb,cAAe,IiB/sHb,oClBosHH,yCkBjsHC,4CjBitHA,OAAQ,KACR,YAAa,KDTd,8C4BhuHD,mDAAA,sD3B2uHA,sCACA,2CiBntHI,8CjBwtHF,OAAQ,K2B5uHR,2B5BguHD,mB4BhuHC,iB3BivHA,QAAS,W2B5uHX,8D5BguHC,sD4BhuHD,oDAEE,cAAA,EAEA,mB5BkuHD,iB4B7tHC,MAAO,GACP,YAAA,OACA,eAAA,OAEA,mBACA,QAAA,IAAA,KACA,UAAA,KACA,YAAA,IACA,YAAA,EACA,MAAA,K5B+tHD,WAAA,O4B5tHC,iBAAA,KACE,OAAA,IAAA,MAAA,KACA,cAAA,I5B+tHH,4B4B5tHC,QAAA,IAAA,KACE,UAAA,KACA,cAAA,I5B+tHH,4B4BlvHC,QAAS,KAAK,K3BkwHd,UAAW,K2BxuHT,cAAA,IAKJ,wCAAA,qC3BwuHE,WAAY,EAEd,uCACA,+BACA,kC0Bh1HE,6CACG,8CC4GL,6D5BwtHC,wE4BvtHC,wBAAA,E5B0tHD,2BAAA,ECgBD,+BACE,aAAc,EAEhB,sCACA,8B2BnuHA,+D5BytHC,oDCWD,iC0Br1HE,4CACG,6CCiHH,uBAAA,E5B2tHD,0BAAA,E4BrtHC,8BAGA,YAAA,E5ButHD,iB4B3tHC,SAAU,SAUR,UAAA,E5BotHH,YAAA,O4BltHK,sB5BqtHL,SAAA,SCgBD,2BACE,YAAa,K2B3tHb,6BAAA,4B5B+sHD,4B4B5sHK,QAAA,EAGJ,kCAAA,wCAGI,aAAA,K5B+sHL,iC6B72HD,uCACE,QAAA,EACA,YAAA,K7Bg3HD,K6Bl3HC,aAAc,EAOZ,cAAA,EACA,WAAA,KARJ,QAWM,SAAA,SACA,QAAA,M7B+2HL,U6B72HK,SAAA,S5B63HJ,QAAS,M4B33HH,QAAA,KAAA,KAMJ,gB7B02HH,gB6Bz2HK,gBAAA,K7B42HL,iBAAA,KCgBD,mB4Bx3HQ,MAAA,KAGA,yBADA,yB7B62HP,MAAA,K6Br2HG,gBAAA,K5Bq3HF,OAAQ,YACR,iBAAkB,Y4Bl3Hd,aAzCN,mB7Bg5HC,mBwBn5HC,iBAAA,KACA,aAAA,QAEA,kBxBs5HD,OAAA,I6Bt5HC,OAAQ,IAAI,EA0DV,SAAA,O7B+1HH,iBAAA,Q6Br1HC,c7Bw1HD,UAAA,K6Bt1HG,UAEA,cAAA,IAAA,MAAA,KALJ,aASM,MAAA,KACA,cAAA,KAEA,e7Bu1HL,aAAA,I6Bt1HK,YAAA,WACE,OAAA,IAAA,MAAA,Y7Bw1HP,cAAA,IAAA,IAAA,EAAA,ECgBD,qBACE,aAAc,KAAK,KAAK,K4B/1HlB,sBAEA,4BADA,4BAEA,MAAA,K7Bo1HP,OAAA,Q6B/0HC,iBAAA,KAqDA,OAAA,IAAA,MAAA,KA8BA,oBAAA,YAnFA,wBAwDE,MAAA,K7B8xHH,cAAA,E6B5xHK,2BACA,MAAA,KA3DJ,6BAgEE,cAAA,IACA,WAAA,OAYJ,iDA0DE,IAAK,KAjED,KAAA,K7B6xHH,yB6B5tHD,2BA9DM,QAAA,W7B6xHL,MAAA,G6Bt2HD,6BAuFE,cAAA,GAvFF,6B5B23HA,aAAc,EACd,cAAe,IDZhB,kC6BzuHD,wCA3BA,wCATM,OAAA,IAAA,MAAA,K7BkxHH,yB6B9uHD,6B5B8vHE,cAAe,IAAI,MAAM,KACzB,cAAe,IAAI,IAAI,EAAE,EDZ1B,kC6Bj3HD,wC7Bk3HD,wC6Bh3HG,oBAAA,MAIE,c7Bk3HL,MAAA,K6B/2HK,gB7Bk3HL,cAAA,ICgBD,iBACE,YAAa,I4B13HP,uBAQR,6B7Bu2HC,6B6Br2HG,MAAA,K7Bw2HH,iBAAA,Q6Bt2HK,gBACA,MAAA,KAYN,mBACE,WAAA,I7B+1HD,YAAA,E6B51HG,e7B+1HH,MAAA,K6B71HK,kBACA,MAAA,KAPN,oBAYI,cAAA,IACA,WAAA,OAYJ,wCA0DE,IAAK,KAjED,KAAA,K7B81HH,yB6B7xHD,kBA9DM,QAAA,W7B81HL,MAAA,G6Br1HD,oBACA,cAAA,GAIE,oBACA,cAAA,EANJ,yB5B62HE,aAAc,EACd,cAAe,IDZhB,8B6B7yHD,oCA3BA,oCATM,OAAA,IAAA,MAAA,K7Bs1HH,yB6BlzHD,yB5Bk0HE,cAAe,IAAI,MAAM,KACzB,cAAe,IAAI,IAAI,EAAE,EDZ1B,8B6B30HD,oC7B40HD,oC6B10HG,oBAAA,MAGA,uB7B60HH,QAAA,K6Bl0HC,qBF3OA,QAAA,M3BkjID,yB8B3iIC,WAAY,KACZ,uBAAA,EACA,wBAAA,EAEA,Q9B6iID,SAAA,S8BriIC,WAAY,KA8nBZ,cAAe,KAhoBb,OAAA,IAAA,MAAA,Y9B4iIH,yB8B5hIC,QAgnBE,cAAe,K9Bi7GlB,yB8BphIC,eACA,MAAA,MAGA,iBACA,cAAA,KAAA,aAAA,KAEA,WAAA,Q9BqhID,2BAAA,M8BnhIC,WAAA,IAAA,MAAA,YACE,mBAAA,MAAA,EAAA,IAAA,EAAA,qB9BqhIH,WAAA,MAAA,EAAA,IAAA,EAAA,qB8B57GD,oBArlBI,WAAA,KAEA,yBAAA,iB9BqhID,MAAA,K8BnhIC,WAAA,EACE,mBAAA,KACA,WAAA,KAEA,0B9BqhIH,QAAA,gB8BlhIC,OAAA,eACE,eAAA,E9BohIH,SAAA,kBCkBD,oBACE,WAAY,QDZf,sC8BlhIK,mC9BihIH,oC8B5gIC,cAAe,E7B+hIf,aAAc,G6Bp+GlB,sCAnjBE,mC7B4hIA,WAAY,MDdX,4D8BtgID,sC9BugID,mCCkBG,WAAY,O6B9gId,kCANE,gC9BygIH,4B8B1gIG,0BAuiBF,aAAc,M7Bs/Gd,YAAa,MAEf,yBDZC,kC8B9gIK,gC9B6gIH,4B8B9gIG,0BAcF,aAAc,EAChB,YAAA,GAMF,mBA8gBE,QAAS,KAhhBP,aAAA,EAAA,EAAA,I9BqgIH,yB8BhgIC,mB7BkhIE,cAAe,G6B7gIjB,qBADA,kB9BmgID,SAAA,M8B5/HC,MAAO,EAggBP,KAAM,E7B+gHN,QAAS,KDdR,yB8BhgID,qB9BigID,kB8BhgIC,cAAA,GAGF,kBACE,IAAA,EACA,aAAA,EAAA,EAAA,I9BogID,qB8B7/HC,OAAQ,EACR,cAAA,EACA,aAAA,IAAA,EAAA,EAEA,cACA,MAAA,K9B+/HD,OAAA,K8B7/HC,QAAA,KAAA,K7B+gIA,UAAW,K6B7gIT,YAAA,KAIA,oBAbJ,oB9B2gIC,gBAAA,K8B1/HG,kB7B6gIF,QAAS,MDdR,yBACF,iC8Bn/HC,uCACA,YAAA,OAGA,eC9LA,SAAA,SACA,MAAA,MD+LA,QAAA,IAAA,KACA,WAAA,IACA,aAAA,KACA,cAAA,I9Bs/HD,iBAAA,Y8Bl/HC,iBAAA,KACE,OAAA,IAAA,MAAA,Y9Bo/HH,cAAA,I8B/+HG,qBACA,QAAA,EAEA,yB9Bk/HH,QAAA,M8BxgIC,MAAO,KAyBL,OAAA,I9Bk/HH,cAAA,I8BvjHD,mCAvbI,WAAA,I9Bm/HH,yB8Bz+HC,eACA,QAAA,MAGE,YACA,OAAA,MAAA,M9B4+HH,iB8B/8HC,YAAA,KA2YA,eAAgB,KAjaZ,YAAA,KAEA,yBACA,iCACA,SAAA,OACA,MAAA,KACA,MAAA,KAAA,WAAA,E9By+HH,iBAAA,Y8B9kHC,OAAQ,E7BimHR,mBAAoB,K6Bz/HhB,WAAA,KAGA,kDAqZN,sC9BqlHC,QAAA,IAAA,KAAA,IAAA,KCmBD,sC6B1/HQ,YAAA,KAmBR,4C9By9HD,4C8B1lHG,iBAAkB,M9B+lHnB,yB8B/lHD,YAtYI,MAAA,K9Bw+HH,OAAA,E8Bt+HK,eACA,MAAA,K9B0+HP,iB8B99HG,YAAa,KACf,eAAA,MAGA,aACA,QAAA,KAAA,K1B9NA,WAAA,IACQ,aAAA,M2B/DR,cAAA,IACA,YAAA,M/B+vID,WAAA,IAAA,MAAA,YiBzuHC,cAAe,IAAI,MAAM,YAwEzB,mBAAoB,MAAM,EAAE,IAAI,EAAE,qBAAyB,EAAE,IAAI,EAAE,qBAtI/D,WAAA,MAAA,EAAA,IAAA,EAAA,qBAAA,EAAA,IAAA,EAAA,qBAEA,yBjB2yHH,yBiBvqHC,QAAS,aA/HP,cAAA,EACA,eAAA,OjB0yHH,2BiB5qHC,QAAS,aAxHP,MAAA,KjBuyHH,eAAA,OiBnyHG,kCACA,QAAA,aAmHJ,0BhBssHE,QAAS,aACT,eAAgB,OgB/yHd,wCjBgyHH,6CiBxrHD,2CjB2rHC,MAAA,KiB/xHG,wCACA,MAAA,KAmGJ,4BhBktHE,cAAe,EgB9yHb,eAAA,OAGA,uBADA,oBjBgyHH,QAAA,aiBtsHC,WAAY,EhBytHZ,cAAe,EgB/yHX,eAAA,OAsFN,6BAAA,0BAjFI,aAAA,EAiFJ,4CjB+sHC,sCiB1xHG,SAAA,SjB6xHH,YAAA,E8BtgID,kDAmWE,IAAK,GAvWH,yBACE,yB9BihIL,cAAA,I8B//HD,oCAoVE,cAAe,GA1Vf,yBACA,aACA,MAAA,KACA,YAAA,E1BzPF,eAAA,EACQ,aAAA,EJswIP,YAAA,EACF,OAAA,E8BtgIG,mBAAoB,KACtB,WAAA,M9B0gID,8B8BtgIC,WAAY,EACZ,uBAAA,EHzUA,wBAAA,EAQA,mDACC,cAAA,E3B40IF,uBAAA,I8BlgIC,wBAAyB,IChVzB,2BAAA,EACA,0BAAA,EDkVA,YCnVA,WAAA,IACA,cAAA,IDqVA,mBCtVA,WAAA,KACA,cAAA,KD+VF,mBChWE,WAAA,KACA,cAAA,KDuWF,aAsSE,WAAY,KA1SV,cAAA,KAEA,yB9BkgID,aACF,MAAA,K8Br+HG,aAAc,KAhBhB,YAAA,MACA,yBE5WA,aF8WE,MAAA,eAFF,cAKI,MAAA,gB9B0/HH,aAAA,M8Bh/HD,4BACA,aAAA,GADF,gBAKI,iBAAA,Q9Bm/HH,aAAA,QCmBD,8B6BngIM,MAAA,KARN,oC9B6/HC,oC8B/+HG,MAAA,Q9Bk/HH,iBAAA,Y8B7+HK,6B9Bg/HL,MAAA,KCmBD,iC6B//HQ,MAAA,KAKF,uC9B4+HL,uCCmBC,MAAO,KACP,iBAAkB,Y6B5/HZ,sCAIF,4C9B0+HL,4CCmBC,MAAO,KACP,iBAAkB,Q6B1/HZ,wCAxCR,8C9BohIC,8C8Bt+HG,MAAA,K9By+HH,iBAAA,YCmBD,+B6Bz/HM,aAAA,KAGA,qCApDN,qC9B8hIC,iBAAA,KCmBD,yC6Bv/HI,iBAAA,KAOE,iCAAA,6B7Bq/HJ,aAAc,Q6Bj/HR,oCAiCN,0C9Bk8HD,0C8B9xHC,MAAO,KA7LC,iBAAA,QACA,yB7Bi/HR,sD6B/+HU,MAAA,KAKF,4D9B49HP,4DCmBC,MAAO,KACP,iBAAkB,Y6B5+HV,2DAIF,iE9B09HP,iECmBC,MAAO,KACP,iBAAkB,Q6B1+HV,6D9B69HX,mEADE,mE8B7jIC,MAAO,KA8GP,iBAAA,aAEE,6B9Bo9HL,MAAA,K8B/8HG,mC9Bk9HH,MAAA,KCmBD,0B6Bl+HM,MAAA,KAIA,gCAAA,gC7Bm+HJ,MAAO,K6Bz9HT,0CARQ,0CASN,mD9B08HD,mD8Bz8HC,MAAA,KAFF,gBAKI,iBAAA,K9B68HH,aAAA,QCmBD,8B6B79HM,MAAA,QARN,oC9Bu9HC,oC8Bz8HG,MAAA,K9B48HH,iBAAA,Y8Bv8HK,6B9B08HL,MAAA,QCmBD,iC6Bz9HQ,MAAA,QAKF,uC9Bs8HL,uCCmBC,MAAO,KACP,iBAAkB,Y6Bt9HZ,sCAIF,4C9Bo8HL,4CCmBC,MAAO,KACP,iBAAkB,Q6Bp9HZ,wCAxCR,8C9B8+HC,8C8B/7HG,MAAA,K9Bk8HH,iBAAA,YCmBD,+B6Bl9HM,aAAA,KAGA,qCArDN,qC9Bw/HC,iBAAA,KCmBD,yC6Bh9HI,iBAAA,KAME,iCAAA,6B7B+8HJ,aAAc,Q6B38HR,oCAuCN,0C9Bs5HD,0C8B93HC,MAAO,KAvDC,iBAAA,QAuDV,yBApDU,kE9By7HP,aAAA,Q8Bt7HO,0D9By7HP,iBAAA,QCmBD,sD6Bz8HU,MAAA,QAKF,4D9Bs7HP,4DCmBC,MAAO,KACP,iBAAkB,Y6Bt8HV,2DAIF,iE9Bo7HP,iECmBC,MAAO,KACP,iBAAkB,Q6Bp8HV,6D9Bu7HX,mEADE,mE8B7hIC,MAAO,KA+GP,iBAAA,aAEE,6B9Bm7HL,MAAA,Q8B96HG,mC9Bi7HH,MAAA,KCmBD,0B6Bj8HM,MAAA,QAIA,gCAAA,gC7Bk8HJ,MAAO,KgC1kJT,0CH0oBQ,0CGzoBN,mDjC2jJD,mDiC1jJC,MAAA,KAEA,YACA,QAAA,IAAA,KjC8jJD,cAAA,KiCnkJC,WAAY,KAQV,iBAAA,QjC8jJH,cAAA,IiC3jJK,eACA,QAAA,ajC+jJL,yBiC3kJC,QAAS,EAAE,IAkBT,MAAA,KjC4jJH,QAAA,SkC/kJC,oBACA,MAAA,KAEA,YlCklJD,QAAA,akCtlJC,aAAc,EAOZ,OAAA,KAAA,ElCklJH,cAAA,ICmBD,eiClmJM,QAAA,OAEA,iBACA,oBACA,SAAA,SACA,MAAA,KACA,QAAA,IAAA,KACA,YAAA,KACA,YAAA,WlCmlJL,MAAA,QkCjlJG,gBAAA,KjComJF,iBAAkB,KiCjmJZ,OAAA,IAAA,MAAA,KPVH,6B3B8lJJ,gCkChlJG,YAAA,EjCmmJF,uBAAwB,I0B1nJxB,0BAAA,I3B4mJD,4BkC3kJG,+BjC8lJF,wBAAyB,IACzB,2BAA4B,IiC3lJxB,uBAFA,uBAGA,0BAFA,0BlCilJL,QAAA,EkCzkJG,MAAA,QjC4lJF,iBAAkB,KAClB,aAAc,KAEhB,sBiC1lJM,4BAFA,4BjC6lJN,yBiC1lJM,+BAFA,+BAGA,QAAA,ElC8kJL,MAAA,KkCroJC,OAAQ,QjCwpJR,iBAAkB,QAClB,aAAc,QiCtlJV,wBAEA,8BADA,8BjCulJN,2BiCzlJM,iCjC0lJN,iCDZC,MAAA,KkClkJC,OAAQ,YjCqlJR,iBAAkB,KkChqJd,aAAA,KAEA,oBnCipJL,uBmC/oJG,QAAA,KAAA,KlCkqJF,UAAW,K0B7pJX,YAAA,U3B+oJD,gCmC9oJG,mClCiqJF,uBAAwB,I0B1qJxB,0BAAA,I3B4pJD,+BkC7kJD,kCjCgmJE,wBAAyB,IkChrJrB,2BAAA,IAEA,oBnCiqJL,uBmC/pJG,QAAA,IAAA,KlCkrJF,UAAW,K0B7qJX,YAAA,I3B+pJD,gCmC9pJG,mClCirJF,uBAAwB,I0B1rJxB,0BAAA,I3B4qJD,+BoC9qJD,kCACE,wBAAA,IACA,2BAAA,IAEA,OpCgrJD,aAAA,EoCprJC,OAAQ,KAAK,EAOX,WAAA,OpCgrJH,WAAA,KCmBD,UmChsJM,QAAA,OAEA,YACA,eACA,QAAA,apCirJL,QAAA,IAAA,KoC/rJC,iBAAkB,KnCktJlB,OAAQ,IAAI,MAAM,KmC/rJd,cAAA,KAnBN,kBpCosJC,kBCmBC,gBAAiB,KmC5rJb,iBAAA,KA3BN,eAAA,kBAkCM,MAAA,MAlCN,mBAAA,sBnCguJE,MAAO,KmCrrJH,mBAEA,yBADA,yBpCwqJL,sBqCrtJC,MAAO,KACP,OAAA,YACA,iBAAA,KAEA,OACA,QAAA,OACA,QAAA,KAAA,KAAA,KACA,UAAA,IACA,YAAA,IACA,YAAA,EACA,MAAA,KrCutJD,WAAA,OqCntJG,YAAA,OpCsuJF,eAAgB,SoCpuJZ,cAAA,MrCutJL,cqCrtJK,cAKJ,MAAA,KACE,gBAAA,KrCktJH,OAAA,QqC7sJG,aACA,QAAA,KAOJ,YCtCE,SAAA,StCkvJD,IAAA,KCmBD,eqChwJM,iBAAA,KALJ,2BD0CF,2BrC+sJC,iBAAA,QCmBD,eqCvwJM,iBAAA,QALJ,2BD8CF,2BrCktJC,iBAAA,QCmBD,eqC9wJM,iBAAA,QALJ,2BDkDF,2BrCqtJC,iBAAA,QCmBD,YqCrxJM,iBAAA,QALJ,wBDsDF,wBrCwtJC,iBAAA,QCmBD,eqC5xJM,iBAAA,QALJ,2BD0DF,2BrC2tJC,iBAAA,QCmBD,cqCnyJM,iBAAA,QCDJ,0BADF,0BAEE,iBAAA,QAEA,OACA,QAAA,aACA,UAAA,KACA,QAAA,IAAA,IACA,UAAA,KACA,YAAA,IACA,YAAA,EACA,MAAA,KACA,WAAA,OvCwxJD,YAAA,OuCrxJC,eAAA,OACE,iBAAA,KvCuxJH,cAAA,KuClxJG,aACA,QAAA,KAGF,YtCqyJA,SAAU,SsCnyJR,IAAA,KAMA,0BvC+wJH,eCmBC,IAAK,EsChyJD,QAAA,IAAA,IvCmxJL,cuCjxJK,cAKJ,MAAA,KtC+xJA,gBAAiB,KsC7xJf,OAAA,QvC+wJH,+BuC3wJC,4BACE,MAAA,QvC6wJH,iBAAA,KuCzwJG,wBvC4wJH,MAAA,MuCxwJG,+BvC2wJH,aAAA,IwCp0JC,uBACA,YAAA,IAEA,WACA,YAAA,KxCu0JD,eAAA,KwC50JC,cAAe,KvC+1Jf,MAAO,QuCt1JL,iBAAA,KAIA,eAbJ,cAcI,MAAA,QxCu0JH,awCr1JC,cAAe,KAmBb,UAAA,KxCq0JH,YAAA,ICmBD,cuCn1JI,iBAAA,QAEA,sBxCo0JH,4BwC91JC,cAAe,KA8Bb,aAAA,KxCm0JH,cAAA,IwChzJD,sBAfI,UAAA,KxCo0JD,oCwCj0JC,WvCo1JA,YAAa,KuCl1JX,eAAA,KxCo0JH,sBwC1zJD,4BvC60JE,cAAe,KuCj1Jb,aAAA,KC5CJ,ezC+2JD,cyC92JC,UAAA,MAGA,WACA,QAAA,MACA,QAAA,IACA,cAAA,KrCiLA,YAAA,WACK,iBAAA,KACG,OAAA,IAAA,MAAA,KJisJT,cAAA,IyC33JC,mBAAoB,OAAO,IAAI,YxC84J1B,cAAe,OAAO,IAAI,YwCj4J7B,WAAA,OAAA,IAAA,YAKF,iBzC82JD,eCmBC,aAAc,KACd,YAAa,KwC13JX,mBA1BJ,kBzCq4JC,kByC12JG,aAAA,QCzBJ,oBACE,QAAA,IACA,MAAA,KAEA,O1Cy4JD,QAAA,K0C74JC,cAAe,KAQb,OAAA,IAAA,MAAA,YAEA,cAAA,IAVJ,UAeI,WAAA,E1Cq4JH,MAAA,QCmBD,mByCl5JI,YAAA,IArBJ,SAyBI,U1Ck4JH,cAAA,ECmBD,WyC34JE,WAAA,IAFF,mBAAA,mBAMI,cAAA,KAEA,0BACA,0B1C43JH,SAAA,S0Cp3JC,IAAK,KCvDL,MAAA,MACA,MAAA,Q3C+6JD,e0Cz3JC,MAAO,QClDL,iBAAA,Q3C86JH,aAAA,Q2C36JG,kB3C86JH,iBAAA,Q2Ct7JC,2BACA,MAAA,Q3C07JD,Y0Ch4JC,MAAO,QCtDL,iBAAA,Q3Cy7JH,aAAA,Q2Ct7JG,e3Cy7JH,iBAAA,Q2Cj8JC,wBACA,MAAA,Q3Cq8JD,e0Cv4JC,MAAO,QC1DL,iBAAA,Q3Co8JH,aAAA,Q2Cj8JG,kB3Co8JH,iBAAA,Q2C58JC,2BACA,MAAA,Q3Cg9JD,c0C94JC,MAAO,QC9DL,iBAAA,Q3C+8JH,aAAA,Q2C58JG,iB3C+8JH,iBAAA,Q4Ch9JC,0BAAQ,MAAA,QACR,wCAAQ,K5Cs9JP,oBAAA,KAAA,E4Cl9JD,GACA,oBAAA,EAAA,GACA,mCAAQ,K5Cw9JP,oBAAA,KAAA,E4C19JD,GACA,oBAAA,EAAA,GACA,gCAAQ,K5Cw9JP,oBAAA,KAAA,E4Ch9JD,GACA,oBAAA,EAAA,GAGA,UACA,OAAA,KxCsCA,cAAA,KACQ,SAAA,OJ86JT,iBAAA,Q4Ch9JC,cAAe,IACf,mBAAA,MAAA,EAAA,IAAA,IAAA,eACA,WAAA,MAAA,EAAA,IAAA,IAAA,eAEA,cACA,MAAA,KACA,MAAA,EACA,OAAA,KACA,UAAA,KxCyBA,YAAA,KACQ,MAAA,KAyHR,WAAA,OACK,iBAAA,QACG,mBAAA,MAAA,EAAA,KAAA,EAAA,gBJk0JT,WAAA,MAAA,EAAA,KAAA,EAAA,gB4C78JC,mBAAoB,MAAM,IAAI,K3Cw+JzB,cAAe,MAAM,IAAI,K4Cv+J5B,WAAA,MAAA,IAAA,KDEF,sBCAE,gCDAF,iBAAA,yK5Ci9JD,iBAAA,oK4C18JC,iBAAiB,iK3Cs+JjB,wBAAyB,KAAK,KGlhK9B,gBAAA,KAAA,KJ4/JD,qBI1/JS,+BwCmDR,kBAAmB,qBAAqB,GAAG,OAAO,SErElD,aAAA,qBAAA,GAAA,OAAA,S9C+gKD,UAAA,qBAAA,GAAA,OAAA,S6C59JG,sBACA,iBAAA,Q7Cg+JH,wC4C38JC,iBAAkB,yKEzElB,iBAAA,oK9CuhKD,iBAAA,iK6Cp+JG,mBACA,iBAAA,Q7Cw+JH,qC4C/8JC,iBAAkB,yKE7ElB,iBAAA,oK9C+hKD,iBAAA,iK6C5+JG,sBACA,iBAAA,Q7Cg/JH,wC4Cn9JC,iBAAkB,yKEjFlB,iBAAA,oK9CuiKD,iBAAA,iK6Cp/JG,qBACA,iBAAA,Q7Cw/JH,uC+C/iKC,iBAAkB,yKAElB,iBAAA,oK/CgjKD,iBAAA,iK+C7iKG,O/CgjKH,WAAA,KC4BD,mB8CtkKE,WAAA,E/C+iKD,O+C3iKD,YACE,SAAA,O/C6iKD,KAAA,E+CziKC,Y/C4iKD,MAAA,Q+CxiKG,c/C2iKH,QAAA,MC4BD,4B8CjkKE,UAAA,KAGF,aAAA,mBAEE,aAAA,KAGF,YAAA,kB9CkkKE,cAAe,K8C3jKjB,YAHE,Y/CuiKD,a+CniKC,QAAA,W/CsiKD,eAAA,I+CliKC,c/CqiKD,eAAA,O+ChiKC,cACA,eAAA,OAMF,eACE,WAAA,EACA,cAAA,ICvDF,YAEE,aAAA,EACA,WAAA,KAQF,YACE,aAAA,EACA,cAAA,KAGA,iBACA,SAAA,SACA,QAAA,MhDglKD,QAAA,KAAA,KgD7kKC,cAAA,KrB3BA,iBAAA,KACC,OAAA,IAAA,MAAA,KqB6BD,6BACE,uBAAA,IrBvBF,wBAAA,I3BymKD,4BgDvkKC,cAAe,E/CmmKf,2BAA4B,I+CjmK5B,0BAAA,IAFF,kBAAA,uBAKI,MAAA,KAIF,2CAAA,gD/CmmKA,MAAO,K+C/lKL,wBAFA,wBhD4kKH,6BgD3kKG,6BAKF,MAAO,KACP,gBAAA,KACA,iBAAA,QAKA,uB/C+lKA,MAAO,KACP,WAAY,K+C5lKV,0BhDskKH,gCgDrkKG,gCALF,MAAA,K/CsmKA,OAAQ,YACR,iBAAkB,KDxBnB,mDgD/kKC,yDAAA,yD/C4mKA,MAAO,QDxBR,gDgDnkKC,sDAAA,sD/CgmKA,MAAO,K+C5lKL,wBAEA,8BADA,8BhDskKH,QAAA,EgD3kKC,MAAA,K/CumKA,iBAAkB,QAClB,aAAc,QAEhB,iDDpBC,wDCuBD,uDADA,uD+C5mKE,8DAYI,6D/C+lKN,uD+C3mKE,8D/C8mKF,6DAKE,MAAO,QDxBR,8CiD7qKG,oDADF,oDAEE,MAAA,QAEA,yBhD0sKF,MAAO,QgDxsKH,iBAAA,QAFF,0BAAA,+BAKI,MAAA,QAGF,mDAAA,wDhD2sKJ,MAAO,QDtBR,gCiDnrKO,gCAGF,qCAFE,qChD8sKN,MAAO,QACP,iBAAkB,QAEpB,iCgD1sKQ,uCAFA,uChD6sKR,sCDtBC,4CiDtrKO,4CArBN,MAAA,KACE,iBAAA,QACA,aAAA,QAEA,sBhDuuKF,MAAO,QgDruKH,iBAAA,QAFF,uBAAA,4BAKI,MAAA,QAGF,gDAAA,qDhDwuKJ,MAAO,QDtBR,6BiDhtKO,6BAGF,kCAFE,kChD2uKN,MAAO,QACP,iBAAkB,QAEpB,8BgDvuKQ,oCAFA,oChD0uKR,mCDtBC,yCiDntKO,yCArBN,MAAA,KACE,iBAAA,QACA,aAAA,QAEA,yBhDowKF,MAAO,QgDlwKH,iBAAA,QAFF,0BAAA,+BAKI,MAAA,QAGF,mDAAA,wDhDqwKJ,MAAO,QDtBR,gCiD7uKO,gCAGF,qCAFE,qChDwwKN,MAAO,QACP,iBAAkB,QAEpB,iCgDpwKQ,uCAFA,uChDuwKR,sCDtBC,4CiDhvKO,4CArBN,MAAA,KACE,iBAAA,QACA,aAAA,QAEA,wBhDiyKF,MAAO,QgD/xKH,iBAAA,QAFF,yBAAA,8BAKI,MAAA,QAGF,kDAAA,uDhDkyKJ,MAAO,QDtBR,+BiD1wKO,+BAGF,oCAFE,oChDqyKN,MAAO,QACP,iBAAkB,QAEpB,gCgDjyKQ,sCAFA,sChDoyKR,qCDtBC,2CiD7wKO,2CDkGN,MAAO,KACP,iBAAA,QACA,aAAA,QAEF,yBACE,WAAA,EACA,cAAA,IE1HF,sBACE,cAAA,EACA,YAAA,IAEA,O9C0DA,cAAA,KACQ,iBAAA,KJgvKT,OAAA,IAAA,MAAA,YkDtyKC,cAAe,IACf,mBAAA,EAAA,IAAA,IAAA,gBlDwyKD,WAAA,EAAA,IAAA,IAAA,gBkDlyKC,YACA,QAAA,KvBnBC,e3B0zKF,QAAA,KAAA,KkDzyKC,cAAe,IAAI,MAAM,YAMvB,uBAAA,IlDsyKH,wBAAA,IkDhyKC,0CACA,MAAA,QAEA,alDmyKD,WAAA,EkDvyKC,cAAe,EjDm0Kf,UAAW,KACX,MAAO,QDtBR,oBkD7xKC,sBjDqzKF,eiD3zKI,mBAKJ,qBAEE,MAAA,QvBvCA,cACC,QAAA,KAAA,K3By0KF,iBAAA,QkDxxKC,WAAY,IAAI,MAAM,KjDozKtB,2BAA4B,IiDjzK1B,0BAAA,IAHJ,mBAAA,mCAMM,cAAA,ElD2xKL,oCkDtxKG,oDjDkzKF,aAAc,IAAI,EiDhzKZ,cAAA,EvBtEL,4D3Bg2KF,4EkDpxKG,WAAA,EjDgzKF,uBAAwB,IiD9yKlB,wBAAA,IvBtEL,0D3B81KF,0EkD7yKC,cAAe,EvB1Df,2BAAA,IACC,0BAAA,IuB0FH,+EAEI,uBAAA,ElDixKH,wBAAA,EkD7wKC,wDlDgxKD,iBAAA,EC4BD,0BACE,iBAAkB,EiDryKpB,8BlD6wKC,ckD7wKD,gCjD0yKE,cAAe,EiD1yKjB,sCAQM,sBlD2wKL,wCC4BC,cAAe,K0Bx5Kf,aAAA,KuByGF,wDlDwxKC,0BC4BC,uBAAwB,IACxB,wBAAyB,IiDrzK3B,yFAoBQ,yFlD2wKP,2DkD5wKO,2DjDwyKN,uBAAwB,IACxB,wBAAyB,IAK3B,wGiDj0KA,wGjD+zKA,wGDtBC,wGCuBD,0EiDh0KA,0EjD8zKA,0EiDtyKU,0EjD8yKR,uBAAwB,IAK1B,uGiD30KA,uGjDy0KA,uGDtBC,uGCuBD,yEiD10KA,yEjDw0KA,yEiD5yKU,yEvB7HR,wBAAA,IuBiGF,sDlDwzKC,yBC4BC,2BAA4B,IAC5B,0BAA2B,IiD3yKrB,qFA1CR,qFAyCQ,wDlDsxKP,wDC4BC,2BAA4B,IAC5B,0BAA2B,IAG7B,oGDtBC,oGCwBD,oGiDj2KA,oGjD81KA,uEiDhzKU,uEjDkzKV,uEiDh2KA,uEjDs2KE,0BAA2B,IAG7B,mGDtBC,mGCwBD,mGiD32KA,mGjDw2KA,sEiDtzKU,sEjDwzKV,sEiD12KA,sEjDg3KE,2BAA4B,IiDrzK1B,0BlD8xKH,qCkDz1KD,0BAAA,qCA+DI,WAAA,IAAA,MAAA,KA/DJ,kDAAA,kDAmEI,WAAA,EAnEJ,uBAAA,yCjD83KE,OAAQ,EiDpzKA,+CjDwzKV,+CiDl4KA,+CjDo4KA,+CAEA,+CANA,+CDjBC,iECoBD,iEiDn4KA,iEjDq4KA,iEAEA,iEANA,iEAWE,YAAa,EiD9zKL,8CjDk0KV,8CiDh5KA,8CjDk5KA,8CAEA,8CANA,8CDjBC,gECoBD,gEiDj5KA,gEjDm5KA,gEAEA,gEANA,gEAWE,aAAc,EAIhB,+CiD95KA,+CjD45KA,+CiDr0KU,+CjDw0KV,iEiD/5KA,iEjD65KA,iEDtBC,iEC6BC,cAAe,EAEjB,8CiDt0KU,8CjDw0KV,8CiDx6KA,8CjDu6KA,gEDtBC,gECwBD,gEiDn0KI,gEACA,cAAA,EAUJ,yBACE,cAAA,ElDsyKD,OAAA,EkDlyKG,aACA,cAAA,KANJ,oBASM,cAAA,ElDqyKL,cAAA,IkDhyKG,2BlDmyKH,WAAA,IC4BD,4BiD3zKM,cAAA,EAKF,wDAvBJ,wDlDwzKC,WAAA,IAAA,MAAA,KkD/xKK,2BlDkyKL,WAAA,EmDrhLC,uDnDwhLD,cAAA,IAAA,MAAA,KmDrhLG,eACA,aAAA,KnDyhLH,8BmD3hLC,MAAA,KAMI,iBAAA,QnDwhLL,aAAA,KmDrhLK,0DACA,iBAAA,KAGJ,qCAEI,MAAA,QnDshLL,iBAAA,KmDviLC,yDnD0iLD,oBAAA,KmDviLG,eACA,aAAA,QnD2iLH,8BmD7iLC,MAAA,KAMI,iBAAA,QnD0iLL,aAAA,QmDviLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnDwiLL,iBAAA,KmDzjLC,yDnD4jLD,oBAAA,QmDzjLG,eACA,aAAA,QnD6jLH,8BmD/jLC,MAAA,QAMI,iBAAA,QnD4jLL,aAAA,QmDzjLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnD0jLL,iBAAA,QmD3kLC,yDnD8kLD,oBAAA,QmD3kLG,YACA,aAAA,QnD+kLH,2BmDjlLC,MAAA,QAMI,iBAAA,QnD8kLL,aAAA,QmD3kLK,uDACA,iBAAA,QAGJ,kCAEI,MAAA,QnD4kLL,iBAAA,QmD7lLC,sDnDgmLD,oBAAA,QmD7lLG,eACA,aAAA,QnDimLH,8BmDnmLC,MAAA,QAMI,iBAAA,QnDgmLL,aAAA,QmD7lLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnD8lLL,iBAAA,QmD/mLC,yDnDknLD,oBAAA,QmD/mLG,cACA,aAAA,QnDmnLH,6BmDrnLC,MAAA,QAMI,iBAAA,QnDknLL,aAAA,QmD/mLK,yDACA,iBAAA,QAGJ,oCAEI,MAAA,QnDgnLL,iBAAA,QoD/nLC,wDACA,oBAAA,QAEA,kBACA,SAAA,SpDkoLD,QAAA,MoDvoLC,OAAQ,EnDmqLR,QAAS,EACT,SAAU,OAEZ,yCmDzpLI,wBADA,yBAEA,yBACA,wBACA,SAAA,SACA,IAAA,EACA,OAAA,EpDkoLH,KAAA,EoD7nLC,MAAO,KACP,OAAA,KpD+nLD,OAAA,EoD1nLC,wBpD6nLD,eAAA,OqDvpLC,uBACA,eAAA,IAEA,MACA,WAAA,KACA,QAAA,KjDwDA,cAAA,KACQ,iBAAA,QJmmLT,OAAA,IAAA,MAAA,QqDlqLC,cAAe,IASb,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACA,WAAA,MAAA,EAAA,IAAA,IAAA,gBAKJ,iBACE,aAAA,KACA,aAAA,gBAEF,SACE,QAAA,KACA,cAAA,ICtBF,SACE,QAAA,IACA,cAAA,IAEA,OACA,MAAA,MACA,UAAA,KjCRA,YAAA,IAGA,YAAA,ErBwrLD,MAAA,KsDhrLC,YAAA,EAAA,IAAA,EAAA,KrD4sLA,OAAQ,kBqD1sLN,QAAA,GjCbF,aiCeE,ajCZF,MAAA,KrBgsLD,gBAAA,KsD5qLC,OAAA,QACE,OAAA,kBACA,QAAA,GAEA,aACA,mBAAA,KtD8qLH,QAAA,EuDnsLC,OAAQ,QACR,WAAA,IvDqsLD,OAAA,EuDhsLC,YACA,SAAA,OAEA,OACA,SAAA,MACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EAIA,QAAA,KvDgsLD,QAAA,KuD7rLC,SAAA,OnD+GA,2BAAA,MACI,QAAA,EAEI,0BAkER,mBAAA,kBAAA,IAAA,SAEK,cAAA,aAAA,IAAA,SACG,WAAA,UAAA,IAAA,SJghLT,kBAAA,kBuDnsLC,cAAA,kBnD2GA,aAAA,kBACI,UAAA,kBAEI,wBJ2lLT,kBAAA,euDvsLK,cAAe,eACnB,aAAA,eACA,UAAA,eAIF,mBACE,WAAA,OACA,WAAA,KvDwsLD,cuDnsLC,SAAU,SACV,MAAA,KACA,OAAA,KAEA,eACA,SAAA,SnDaA,iBAAA,KACQ,wBAAA,YmDZR,gBAAA,YtD+tLA,OsD/tLA,IAAA,MAAA,KAEA,OAAA,IAAA,MAAA,evDqsLD,cAAA,IuDjsLC,QAAS,EACT,mBAAA,EAAA,IAAA,IAAA,eACA,WAAA,EAAA,IAAA,IAAA,eAEA,gBACA,SAAA,MACA,IAAA,EACA,MAAA,EvDmsLD,OAAA,EuDjsLC,KAAA,ElCrEA,QAAA,KAGA,iBAAA,KkCmEA,qBlCtEA,OAAA,iBAGA,QAAA,EkCwEF,mBACE,OAAA,kBACA,QAAA,GAIF,cACE,QAAA,KvDmsLD,cAAA,IAAA,MAAA,QuD9rLC,qBACA,WAAA,KAKF,aACE,OAAA,EACA,YAAA,WAIF,YACE,SAAA,SACA,QAAA,KvD6rLD,cuD/rLC,QAAS,KAQP,WAAA,MACA,WAAA,IAAA,MAAA,QATJ,wBAaI,cAAA,EvDyrLH,YAAA,IuDrrLG,mCvDwrLH,YAAA,KuDlrLC,oCACA,YAAA,EAEA,yBACA,SAAA,SvDqrLD,IAAA,QuDnqLC,MAAO,KAZP,OAAA,KACE,SAAA,OvDmrLD,yBuDhrLD,cnDvEA,MAAA,MACQ,OAAA,KAAA,KmD2ER,eAAY,mBAAA,EAAA,IAAA,KAAA,evDkrLX,WAAA,EAAA,IAAA,KAAA,euD5qLD,UAFA,MAAA,OvDorLD,yBwDl0LC,UACA,MAAA,OCNA,SAEA,SAAA,SACA,QAAA,KACA,QAAA,MACA,YAAA,iBAAA,UAAA,MAAA,WACA,UAAA,KACA,WAAA,OACA,YAAA,IACA,YAAA,WACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,ODHA,WAAA,OnCVA,aAAA,OAGA,UAAA,OrBy1LD,YAAA,OwD90LC,OAAA,iBnCdA,QAAA,ErBg2LD,WAAA,KwDj1LY,YAAmB,OAAA,kBxDq1L/B,QAAA,GwDp1LY,aAAmB,QAAA,IAAA,ExDw1L/B,WAAA,KwDv1LY,eAAmB,QAAA,EAAA,IxD21L/B,YAAA,IwD11LY,gBAAmB,QAAA,IAAA,ExD81L/B,WAAA,IwDz1LC,cACA,QAAA,EAAA,IACA,YAAA,KAEA,eACA,UAAA,MxD41LD,QAAA,IAAA,IwDx1LC,MAAO,KACP,WAAA,OACA,iBAAA,KACA,cAAA,IAEA,exD01LD,SAAA,SwDt1LC,MAAA,EACE,OAAA,EACA,aAAA,YACA,aAAA,MAEA,4BxDw1LH,OAAA,EwDt1LC,KAAA,IACE,YAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,iCxDw1LH,MAAA,IwDt1LC,OAAA,EACE,cAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,kCxDw1LH,OAAA,EwDt1LC,KAAA,IACE,cAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,8BxDw1LH,IAAA,IwDt1LC,KAAA,EACE,WAAA,KACA,aAAA,IAAA,IAAA,IAAA,EACA,mBAAA,KAEA,6BxDw1LH,IAAA,IwDt1LC,MAAA,EACE,WAAA,KACA,aAAA,IAAA,EAAA,IAAA,IACA,kBAAA,KAEA,+BxDw1LH,IAAA,EwDt1LC,KAAA,IACE,YAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,oCxDw1LH,IAAA,EwDt1LC,MAAA,IACE,WAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,qCxDw1LH,IAAA,E0Dr7LC,KAAM,IACN,WAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,SACA,SAAA,SACA,IAAA,EDXA,KAAA,EAEA,QAAA,KACA,QAAA,KACA,UAAA,MACA,QAAA,IACA,YAAA,iBAAA,UAAA,MAAA,WACA,UAAA,KACA,WAAA,OACA,YAAA,IACA,YAAA,WACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KCAA,eAAA,OAEA,WAAA,OACA,aAAA,OAAA,UAAA,OACA,YAAA,OACA,iBAAA,KACA,wBAAA,YtD8CA,gBAAA,YACQ,OAAA,IAAA,MAAA,KJq5LT,OAAA,IAAA,MAAA,e0Dh8LC,cAAA,IAAY,mBAAA,EAAA,IAAA,KAAA,e1Dm8Lb,WAAA,EAAA,IAAA,KAAA,e0Dl8La,WAAA,KACZ,aAAY,WAAA,MACZ,eAAY,YAAA,KAGd,gBACE,WAAA,KAEA,cACA,YAAA,MAEA,e1Dw8LD,QAAA,IAAA,K0Dr8LC,OAAQ,EACR,UAAA,K1Du8LD,iBAAA,Q0D/7LC,cAAA,IAAA,MAAA,QzD49LA,cAAe,IAAI,IAAI,EAAE,EyDz9LvB,iBACA,QAAA,IAAA,KAEA,gBACA,sB1Di8LH,SAAA,S0D97LC,QAAS,MACT,MAAA,E1Dg8LD,OAAA,E0D97LC,aAAc,YACd,aAAA,M1Di8LD,gB0D57LC,aAAA,KAEE,sBACA,QAAA,GACA,aAAA,KAEA,oB1D87LH,OAAA,M0D77LG,KAAA,IACE,YAAA,MACA,iBAAA,KACA,iBAAA,gBACA,oBAAA,E1Dg8LL,0B0D57LC,OAAA,IACE,YAAA,MACA,QAAA,IACA,iBAAA,KACA,oBAAA,EAEA,sB1D87LH,IAAA,I0D77LG,KAAA,MACE,WAAA,MACA,mBAAA,KACA,mBAAA,gBACA,kBAAA,E1Dg8LL,4B0D57LC,OAAA,MACE,KAAA,IACA,QAAA,IACA,mBAAA,KACA,kBAAA,EAEA,uB1D87LH,IAAA,M0D77LG,KAAA,IACE,YAAA,MACA,iBAAA,EACA,oBAAA,KACA,oBAAA,gB1Dg8LL,6B0D37LC,IAAA,IACE,YAAA,MACA,QAAA,IACA,iBAAA,EACA,oBAAA,KAEA,qB1D67LH,IAAA,I0D57LG,MAAA,MACE,WAAA,MACA,mBAAA,EACA,kBAAA,KACA,kBAAA,gB1D+7LL,2B2DvjMC,MAAO,IACP,OAAA,M3DyjMD,QAAA,I2DtjMC,mBAAoB,EACpB,kBAAA,KAEA,U3DwjMD,SAAA,S2DrjMG,gBACA,SAAA,SvD6KF,MAAA,KACK,SAAA,OJ64LN,sB2DlkMC,SAAU,S1D+lMV,QAAS,K0DjlML,mBAAA,IAAA,YAAA,K3DwjML,cAAA,IAAA,YAAA,K2D9hMC,WAAA,IAAA,YAAA,KvDmKK,4BAFL,0BAGQ,YAAA,EA3JA,qDA+GR,sBAEQ,mBAAA,kBAAA,IAAA,YJi7LP,cAAA,aAAA,IAAA,Y2D5jMG,WAAA,UAAA,IAAA,YvDmHJ,4BAAA,OACQ,oBAAA,OuDjHF,oBAAA,O3D+jML,YAAA,OI/8LD,mCHy+LA,2BGx+LQ,KAAA,EuD5GF,kBAAA,sB3DgkML,UAAA,sBC2BD,kCADA,2BG/+LA,KAAA,EACQ,kBAAA,uBuDtGF,UAAA,uBArCN,6B3DumMD,gC2DvmMC,iC1DkoME,KAAM,E0DrlMN,kBAAA,mB3D+jMH,UAAA,oBAGA,wB2D/mMD,sBAAA,sBAsDI,QAAA,MAEA,wB3D6jMH,KAAA,E2DzjMG,sB3D4jMH,sB2DxnMC,SAAU,SA+DR,IAAA,E3D4jMH,MAAA,KC0BD,sB0DllMI,KAAA,KAnEJ,sBAuEI,KAAA,MAvEJ,2BA0EI,4B3D2jMH,KAAA,E2DljMC,6BACA,KAAA,MAEA,8BACA,KAAA,KtC3FA,kBsC6FA,SAAA,SACA,IAAA,EACA,OAAA,EACA,KAAA,EACA,MAAA,I3DsjMD,UAAA,K2DjjMC,MAAA,KdnGE,WAAA,OACA,YAAA,EAAA,IAAA,IAAA,eACA,iBAAA,cAAA,OAAA,kBACA,QAAA,G7CwpMH,uB2DrjMC,iBAAA,sEACE,iBAAA,iEACA,iBAAA,uFdxGA,iBAAA,kEACA,OAAA,+GACA,kBAAA,SACA,wBACA,MAAA,E7CgqMH,KAAA,K2DvjMC,iBAAA,sE1DmlMA,iBAAiB,iE0DjlMf,iBAAA,uFACA,iBAAA,kEACA,OAAA,+GtCvHF,kBAAA,SsCyFF,wB3DylMC,wBC4BC,MAAO,KACP,gBAAiB,KACjB,OAAQ,kB0DhlMN,QAAA,EACA,QAAA,G3D2jMH,0C2DnmMD,2CA2CI,6BADA,6B1DqlMF,SAAU,S0DhlMR,IAAA,IACA,QAAA,E3DwjMH,QAAA,a2DxmMC,WAAY,MAqDV,0CADA,6B3DyjMH,KAAA,I2D7mMC,YAAa,MA0DX,2CADA,6BAEA,MAAA,IACA,aAAA,MAME,6BADF,6B3DsjMH,MAAA,K2DjjMG,OAAA,KACE,YAAA,M3DmjML,YAAA,E2DxiMC,oCACA,QAAA,QAEA,oCACA,QAAA,QAEA,qBACA,SAAA,SACA,OAAA,K3D2iMD,KAAA,I2DpjMC,QAAS,GAYP,MAAA,IACA,aAAA,EACA,YAAA,KACA,WAAA,OACA,WAAA,KAEA,wBACA,QAAA,aAWA,MAAA,KACA,OAAA,K3DiiMH,OAAA,I2DhkMC,YAAa,OAkCX,OAAA,QACA,iBAAA,OACA,iBAAA,cACA,OAAA,IAAA,MAAA,K3DiiMH,cAAA,K2DzhMC,6BACA,MAAA,KACA,OAAA,KACA,OAAA,EACA,iBAAA,KAEA,kBACA,SAAA,SACA,MAAA,IACA,OAAA,K3D4hMD,KAAA,I2D3hMC,QAAA,GACE,YAAA,K3D6hMH,eAAA,K2Dp/LC,MAAO,KAhCP,WAAA,O1DijMA,YAAa,EAAE,IAAI,IAAI,eAEzB,uB0D9iMM,YAAA,KAEA,oCACA,0C3DshMH,2C2D9hMD,6BAAA,6BAYI,MAAA,K3DshMH,OAAA,K2DliMD,WAAA,M1D8jME,UAAW,KDxBZ,0C2DjhMD,6BACE,YAAA,MAEA,2C3DmhMD,6B2D/gMD,aAAA,M3DkhMC,kBACF,MAAA,I4DhxMC,KAAA,I3D4yME,eAAgB,KAElB,qBACE,OAAQ,MAkBZ,qCADA,sCADA,mBADA,oBAXA,gBADA,iBAOA,uBADA,wBADA,iBADA,kBADA,wBADA,yBASA,mCADA,oC2DvzME,oBAAA,qBAAA,oBAAA,qB3D8zMF,WADA,YAOA,uBADA,wBADA,qBADA,sBADA,cADA,e2Dl0MI,a3Dw0MJ,cDvBC,kB4DhzMG,mB3DwzMJ,WADA,YAwBE,QAAS,MACT,QAAS,IASX,qCADA,mBANA,gBAGA,uBADA,iBADA,wBAIA,mCDhBC,oB6Dl1MC,oB5Dq2MF,W+B/1MA,uBhCu0MC,qB4D/zMG,cChBF,aACA,kB5Dk2MF,W+Bx1ME,MAAO,KhC40MR,cgCz0MC,QAAS,MACT,aAAA,KhC20MD,YAAA,KgCl0MC,YhCq0MD,MAAA,gBgCl0MC,WhCq0MD,MAAA,egCl0MC,MhCq0MD,QAAA,e8D51MC,MACA,QAAA,gBAEA,WACA,WAAA,O9B8BF,WACE,KAAA,EAAA,EAAA,EhCm0MD,MAAA,YgC5zMC,YAAa,KACb,iBAAA,YhC8zMD,OAAA,E+D91MC,Q/Di2MD,QAAA,eC4BD,OACE,SAAU,M+Dt4MV,chE+2MD,MAAA,aC+BD,YADA,YADA,YADA,YAIE,QAAS,e+Dv5MT,kBhEy4MC,mBgEx4MD,yBhEo4MD,kB+Dr1MD,mBA6IA,yB9D+tMA,kBACA,mB8Dp3ME,yB9Dg3MF,kBACA,mBACA,yB+D15MY,QAAA,eACV,yBAAU,YhE64MT,QAAA,gBC4BD,iB+Dv6MU,QAAA,gBhEg5MX,c+D/1MG,QAAS,oB/Dm2MV,c+Dr2MC,c/Ds2MH,QAAA,sB+Dj2MG,yB/Dq2MD,kBACF,QAAA,iB+Dj2MG,yB/Dq2MD,mBACF,QAAA,kBgEn6MC,yBhEu6MC,yBgEt6MD,QAAA,wBACA,+CAAU,YhE26MT,QAAA,gBC4BD,iB+Dr8MU,QAAA,gBhE86MX,c+Dx2MG,QAAS,oB/D42MV,c+D92MC,c/D+2MH,QAAA,sB+D12MG,+C/D82MD,kBACF,QAAA,iB+D12MG,+C/D82MD,mBACF,QAAA,kBgEj8MC,+ChEq8MC,yBgEp8MD,QAAA,wBACA,gDAAU,YhEy8MT,QAAA,gBC4BD,iB+Dn+MU,QAAA,gBhE48MX,c+Dj3MG,QAAS,oB/Dq3MV,c+Dv3MC,c/Dw3MH,QAAA,sB+Dn3MG,gD/Du3MD,kBACF,QAAA,iB+Dn3MG,gD/Du3MD,mBACF,QAAA,kBgE/9MC,gDhEm+MC,yBgEl+MD,QAAA,wBACA,0BAAU,YhEu+MT,QAAA,gBC4BD,iB+DjgNU,QAAA,gBhE0+MX,c+D13MG,QAAS,oB/D83MV,c+Dh4MC,c/Di4MH,QAAA,sB+D53MG,0B/Dg4MD,kBACF,QAAA,iB+D53MG,0B/Dg4MD,mBACF,QAAA,kBgEr/MC,0BhEy/MC,yBACF,QAAA,wBgE1/MC,yBhE8/MC,WACF,QAAA,gBgE//MC,+ChEmgNC,WACF,QAAA,gBgEpgNC,gDhEwgNC,WACF,QAAA,gBAGA,0B+Dn3MC,WA4BE,QAAS,gBC5LX,eAAU,QAAA,eACV,aAAU,ehE4hNT,QAAA,gBC4BD,oB+DtjNU,QAAA,gBhE+hNX,iB+Dj4MG,QAAS,oBAMX,iB/D83MD,iB+Dz2MG,QAAS,sB/D82MZ,qB+Dl4MC,QAAS,e/Dq4MV,a+D/3MC,qBAcE,QAAS,iB/Ds3MZ,sB+Dn4MC,QAAS,e/Ds4MV,a+Dh4MC,sBAOE,QAAS,kB/D83MZ,4B+D/3MC,QAAS,eCpLT,ahEujNC,4BACF,QAAA,wBC6BD,aACE,cACE,QAAS"}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/fonts/glyphicons-halflings-regular.svg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/fonts/glyphicons-halflings-regular.svg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/fonts/glyphicons-halflings-regular.svg	(revision 5095)
@@ -0,0 +1,288 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="400" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
+<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xa0;" />
+<glyph unicode="&#xa5;" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
+<glyph unicode="&#x2000;" horiz-adv-x="650" />
+<glyph unicode="&#x2001;" horiz-adv-x="1300" />
+<glyph unicode="&#x2002;" horiz-adv-x="650" />
+<glyph unicode="&#x2003;" horiz-adv-x="1300" />
+<glyph unicode="&#x2004;" horiz-adv-x="433" />
+<glyph unicode="&#x2005;" horiz-adv-x="325" />
+<glyph unicode="&#x2006;" horiz-adv-x="216" />
+<glyph unicode="&#x2007;" horiz-adv-x="216" />
+<glyph unicode="&#x2008;" horiz-adv-x="162" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="325" />
+<glyph unicode="&#x20ac;" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
+<glyph unicode="&#x20bd;" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
+<glyph unicode="&#x2212;" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#x231b;" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#x2601;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
+<glyph unicode="&#x26fa;" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
+<glyph unicode="&#x2709;" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
+<glyph unicode="&#x270f;" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
+<glyph unicode="&#xe001;" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
+<glyph unicode="&#xe002;" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
+<glyph unicode="&#xe003;" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
+<glyph unicode="&#xe005;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
+<glyph unicode="&#xe006;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
+<glyph unicode="&#xe007;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
+<glyph unicode="&#xe008;" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
+<glyph unicode="&#xe009;" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
+<glyph unicode="&#xe010;" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe011;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
+<glyph unicode="&#xe012;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe013;" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
+<glyph unicode="&#xe014;" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
+<glyph unicode="&#xe015;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe016;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe017;" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
+<glyph unicode="&#xe018;" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe019;" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
+<glyph unicode="&#xe020;" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
+<glyph unicode="&#xe021;" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe022;" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
+<glyph unicode="&#xe023;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe024;" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
+<glyph unicode="&#xe025;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe026;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
+<glyph unicode="&#xe027;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
+<glyph unicode="&#xe028;" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
+<glyph unicode="&#xe029;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe030;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
+<glyph unicode="&#xe031;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
+<glyph unicode="&#xe032;" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe033;" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
+<glyph unicode="&#xe034;" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
+<glyph unicode="&#xe035;" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
+<glyph unicode="&#xe036;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
+<glyph unicode="&#xe037;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
+<glyph unicode="&#xe038;" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
+<glyph unicode="&#xe039;" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
+<glyph unicode="&#xe040;" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
+<glyph unicode="&#xe041;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe042;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe043;" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
+<glyph unicode="&#xe044;" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe045;" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
+<glyph unicode="&#xe046;" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
+<glyph unicode="&#xe047;" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
+<glyph unicode="&#xe048;" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
+<glyph unicode="&#xe049;" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
+<glyph unicode="&#xe050;" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
+<glyph unicode="&#xe051;" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
+<glyph unicode="&#xe052;" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe053;" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe054;" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe055;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe056;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe057;" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe058;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe059;" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="&#xe060;" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
+<glyph unicode="&#xe062;" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
+<glyph unicode="&#xe063;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
+<glyph unicode="&#xe064;" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
+<glyph unicode="&#xe065;" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
+<glyph unicode="&#xe066;" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
+<glyph unicode="&#xe067;" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
+<glyph unicode="&#xe068;" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe069;" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe070;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe071;" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
+<glyph unicode="&#xe072;" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
+<glyph unicode="&#xe073;" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe074;" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe075;" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
+<glyph unicode="&#xe076;" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe077;" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe078;" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe079;" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
+<glyph unicode="&#xe080;" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
+<glyph unicode="&#xe081;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe082;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe083;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
+<glyph unicode="&#xe084;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
+<glyph unicode="&#xe085;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe086;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe087;" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
+<glyph unicode="&#xe088;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe089;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe090;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
+<glyph unicode="&#xe091;" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
+<glyph unicode="&#xe092;" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe093;" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
+<glyph unicode="&#xe094;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe095;" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe096;" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
+<glyph unicode="&#xe097;" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
+<glyph unicode="&#xe101;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe102;" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
+<glyph unicode="&#xe103;" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
+<glyph unicode="&#xe104;" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
+<glyph unicode="&#xe105;" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe106;" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe107;" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
+<glyph unicode="&#xe108;" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
+<glyph unicode="&#xe109;" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
+<glyph unicode="&#xe110;" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
+<glyph unicode="&#xe111;" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="&#xe112;" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
+<glyph unicode="&#xe113;" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
+<glyph unicode="&#xe114;" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
+<glyph unicode="&#xe115;" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe116;" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
+<glyph unicode="&#xe117;" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="&#xe118;" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="&#xe119;" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe120;" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
+<glyph unicode="&#xe121;" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
+<glyph unicode="&#xe122;" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
+<glyph unicode="&#xe123;" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
+<glyph unicode="&#xe124;" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
+<glyph unicode="&#xe125;" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe126;" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
+<glyph unicode="&#xe127;" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe128;" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe129;" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe130;" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
+<glyph unicode="&#xe131;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
+<glyph unicode="&#xe132;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
+<glyph unicode="&#xe133;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
+<glyph unicode="&#xe134;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe135;" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
+<glyph unicode="&#xe136;" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
+<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
+<glyph unicode="&#xe138;" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
+<glyph unicode="&#xe139;" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="&#xe140;" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
+<glyph unicode="&#xe141;" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
+<glyph unicode="&#xe142;" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
+<glyph unicode="&#xe143;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
+<glyph unicode="&#xe144;" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
+<glyph unicode="&#xe145;" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
+<glyph unicode="&#xe146;" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
+<glyph unicode="&#xe148;" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
+<glyph unicode="&#xe149;" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
+<glyph unicode="&#xe150;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe151;" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
+<glyph unicode="&#xe152;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
+<glyph unicode="&#xe153;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
+<glyph unicode="&#xe154;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
+<glyph unicode="&#xe155;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="&#xe156;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="&#xe157;" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="&#xe158;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe159;" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
+<glyph unicode="&#xe160;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
+<glyph unicode="&#xe161;" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe162;" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
+<glyph unicode="&#xe163;" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe164;" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
+<glyph unicode="&#xe165;" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="&#xe166;" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe167;" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe168;" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
+<glyph unicode="&#xe169;" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe170;" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe171;" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
+<glyph unicode="&#xe172;" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
+<glyph unicode="&#xe173;" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe174;" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
+<glyph unicode="&#xe175;" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe176;" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe177;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
+<glyph unicode="&#xe178;" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
+<glyph unicode="&#xe179;" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
+<glyph unicode="&#xe180;" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
+<glyph unicode="&#xe181;" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="&#xe182;" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
+<glyph unicode="&#xe183;" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
+<glyph unicode="&#xe184;" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe185;" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
+<glyph unicode="&#xe186;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe187;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe188;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
+<glyph unicode="&#xe189;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
+<glyph unicode="&#xe190;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
+<glyph unicode="&#xe191;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe192;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe193;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
+<glyph unicode="&#xe194;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="&#xe195;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
+<glyph unicode="&#xe197;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe198;" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
+<glyph unicode="&#xe199;" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
+<glyph unicode="&#xe200;" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
+<glyph unicode="&#xe201;" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
+<glyph unicode="&#xe202;" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
+<glyph unicode="&#xe203;" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
+<glyph unicode="&#xe204;" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
+<glyph unicode="&#xe205;" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe206;" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe209;" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
+<glyph unicode="&#xe210;" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe211;" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe212;" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe213;" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe214;" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe215;" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe216;" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
+<glyph unicode="&#xe218;" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
+<glyph unicode="&#xe219;" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
+<glyph unicode="&#xe221;" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe223;" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
+<glyph unicode="&#xe224;" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
+<glyph unicode="&#xe225;" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe226;" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
+<glyph unicode="&#xe227;" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
+<glyph unicode="&#xe230;" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
+<glyph unicode="&#xe231;" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe232;" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe233;" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
+<glyph unicode="&#xe234;" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe235;" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe236;" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe237;" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
+<glyph unicode="&#xe238;" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe239;" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
+<glyph unicode="&#xe240;" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
+<glyph unicode="&#xe241;" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
+<glyph unicode="&#xe242;" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe243;" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
+<glyph unicode="&#xe244;" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
+<glyph unicode="&#xe245;" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
+<glyph unicode="&#xe246;" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
+<glyph unicode="&#xe247;" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe248;" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
+<glyph unicode="&#xe249;" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe250;" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
+<glyph unicode="&#xe251;" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
+<glyph unicode="&#xe252;" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
+<glyph unicode="&#xe253;" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
+<glyph unicode="&#xe254;" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
+<glyph unicode="&#xe255;" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
+<glyph unicode="&#xe256;" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
+<glyph unicode="&#xe257;" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
+<glyph unicode="&#xe258;" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
+<glyph unicode="&#xe259;" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
+<glyph unicode="&#xe260;" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
+<glyph unicode="&#xf8ff;" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
+<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
+<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
+</font>
+</defs></svg> 
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/bootstrap.js
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/bootstrap.js	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/bootstrap.js	(revision 5095)
@@ -0,0 +1,2363 @@
+/*!
+ * Bootstrap v3.3.6 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+
+if (typeof jQuery === 'undefined') {
+  throw new Error('Bootstrap\'s JavaScript requires jQuery')
+}
+
++function ($) {
+  'use strict';
+  var version = $.fn.jquery.split(' ')[0].split('.')
+  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 2)) {
+    throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3')
+  }
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: transition.js v3.3.6
+ * http://getbootstrap.com/javascript/#transitions
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+  // ============================================================
+
+  function transitionEnd() {
+    var el = document.createElement('bootstrap')
+
+    var transEndEventNames = {
+      WebkitTransition : 'webkitTransitionEnd',
+      MozTransition    : 'transitionend',
+      OTransition      : 'oTransitionEnd otransitionend',
+      transition       : 'transitionend'
+    }
+
+    for (var name in transEndEventNames) {
+      if (el.style[name] !== undefined) {
+        return { end: transEndEventNames[name] }
+      }
+    }
+
+    return false // explicit for ie8 (  ._.)
+  }
+
+  // http://blog.alexmaccaw.com/css-transitions
+  $.fn.emulateTransitionEnd = function (duration) {
+    var called = false
+    var $el = this
+    $(this).one('bsTransitionEnd', function () { called = true })
+    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+    setTimeout(callback, duration)
+    return this
+  }
+
+  $(function () {
+    $.support.transition = transitionEnd()
+
+    if (!$.support.transition) return
+
+    $.event.special.bsTransitionEnd = {
+      bindType: $.support.transition.end,
+      delegateType: $.support.transition.end,
+      handle: function (e) {
+        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
+      }
+    }
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: alert.js v3.3.6
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // ALERT CLASS DEFINITION
+  // ======================
+
+  var dismiss = '[data-dismiss="alert"]'
+  var Alert   = function (el) {
+    $(el).on('click', dismiss, this.close)
+  }
+
+  Alert.VERSION = '3.3.6'
+
+  Alert.TRANSITION_DURATION = 150
+
+  Alert.prototype.close = function (e) {
+    var $this    = $(this)
+    var selector = $this.attr('data-target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    var $parent = $(selector)
+
+    if (e) e.preventDefault()
+
+    if (!$parent.length) {
+      $parent = $this.closest('.alert')
+    }
+
+    $parent.trigger(e = $.Event('close.bs.alert'))
+
+    if (e.isDefaultPrevented()) return
+
+    $parent.removeClass('in')
+
+    function removeElement() {
+      // detach from parent, fire event then clean up data
+      $parent.detach().trigger('closed.bs.alert').remove()
+    }
+
+    $.support.transition && $parent.hasClass('fade') ?
+      $parent
+        .one('bsTransitionEnd', removeElement)
+        .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
+      removeElement()
+  }
+
+
+  // ALERT PLUGIN DEFINITION
+  // =======================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.alert')
+
+      if (!data) $this.data('bs.alert', (data = new Alert(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  var old = $.fn.alert
+
+  $.fn.alert             = Plugin
+  $.fn.alert.Constructor = Alert
+
+
+  // ALERT NO CONFLICT
+  // =================
+
+  $.fn.alert.noConflict = function () {
+    $.fn.alert = old
+    return this
+  }
+
+
+  // ALERT DATA-API
+  // ==============
+
+  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: button.js v3.3.6
+ * http://getbootstrap.com/javascript/#buttons
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // BUTTON PUBLIC CLASS DEFINITION
+  // ==============================
+
+  var Button = function (element, options) {
+    this.$element  = $(element)
+    this.options   = $.extend({}, Button.DEFAULTS, options)
+    this.isLoading = false
+  }
+
+  Button.VERSION  = '3.3.6'
+
+  Button.DEFAULTS = {
+    loadingText: 'loading...'
+  }
+
+  Button.prototype.setState = function (state) {
+    var d    = 'disabled'
+    var $el  = this.$element
+    var val  = $el.is('input') ? 'val' : 'html'
+    var data = $el.data()
+
+    state += 'Text'
+
+    if (data.resetText == null) $el.data('resetText', $el[val]())
+
+    // push to event loop to allow forms to submit
+    setTimeout($.proxy(function () {
+      $el[val](data[state] == null ? this.options[state] : data[state])
+
+      if (state == 'loadingText') {
+        this.isLoading = true
+        $el.addClass(d).attr(d, d)
+      } else if (this.isLoading) {
+        this.isLoading = false
+        $el.removeClass(d).removeAttr(d)
+      }
+    }, this), 0)
+  }
+
+  Button.prototype.toggle = function () {
+    var changed = true
+    var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+    if ($parent.length) {
+      var $input = this.$element.find('input')
+      if ($input.prop('type') == 'radio') {
+        if ($input.prop('checked')) changed = false
+        $parent.find('.active').removeClass('active')
+        this.$element.addClass('active')
+      } else if ($input.prop('type') == 'checkbox') {
+        if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
+        this.$element.toggleClass('active')
+      }
+      $input.prop('checked', this.$element.hasClass('active'))
+      if (changed) $input.trigger('change')
+    } else {
+      this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
+      this.$element.toggleClass('active')
+    }
+  }
+
+
+  // BUTTON PLUGIN DEFINITION
+  // ========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.button')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+      if (option == 'toggle') data.toggle()
+      else if (option) data.setState(option)
+    })
+  }
+
+  var old = $.fn.button
+
+  $.fn.button             = Plugin
+  $.fn.button.Constructor = Button
+
+
+  // BUTTON NO CONFLICT
+  // ==================
+
+  $.fn.button.noConflict = function () {
+    $.fn.button = old
+    return this
+  }
+
+
+  // BUTTON DATA-API
+  // ===============
+
+  $(document)
+    .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
+      var $btn = $(e.target)
+      if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+      Plugin.call($btn, 'toggle')
+      if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault()
+    })
+    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
+      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
+    })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: carousel.js v3.3.6
+ * http://getbootstrap.com/javascript/#carousel
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // CAROUSEL CLASS DEFINITION
+  // =========================
+
+  var Carousel = function (element, options) {
+    this.$element    = $(element)
+    this.$indicators = this.$element.find('.carousel-indicators')
+    this.options     = options
+    this.paused      = null
+    this.sliding     = null
+    this.interval    = null
+    this.$active     = null
+    this.$items      = null
+
+    this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
+
+    this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
+      .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
+      .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
+  }
+
+  Carousel.VERSION  = '3.3.6'
+
+  Carousel.TRANSITION_DURATION = 600
+
+  Carousel.DEFAULTS = {
+    interval: 5000,
+    pause: 'hover',
+    wrap: true,
+    keyboard: true
+  }
+
+  Carousel.prototype.keydown = function (e) {
+    if (/input|textarea/i.test(e.target.tagName)) return
+    switch (e.which) {
+      case 37: this.prev(); break
+      case 39: this.next(); break
+      default: return
+    }
+
+    e.preventDefault()
+  }
+
+  Carousel.prototype.cycle = function (e) {
+    e || (this.paused = false)
+
+    this.interval && clearInterval(this.interval)
+
+    this.options.interval
+      && !this.paused
+      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+
+    return this
+  }
+
+  Carousel.prototype.getItemIndex = function (item) {
+    this.$items = item.parent().children('.item')
+    return this.$items.index(item || this.$active)
+  }
+
+  Carousel.prototype.getItemForDirection = function (direction, active) {
+    var activeIndex = this.getItemIndex(active)
+    var willWrap = (direction == 'prev' && activeIndex === 0)
+                || (direction == 'next' && activeIndex == (this.$items.length - 1))
+    if (willWrap && !this.options.wrap) return active
+    var delta = direction == 'prev' ? -1 : 1
+    var itemIndex = (activeIndex + delta) % this.$items.length
+    return this.$items.eq(itemIndex)
+  }
+
+  Carousel.prototype.to = function (pos) {
+    var that        = this
+    var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
+
+    if (pos > (this.$items.length - 1) || pos < 0) return
+
+    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
+    if (activeIndex == pos) return this.pause().cycle()
+
+    return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
+  }
+
+  Carousel.prototype.pause = function (e) {
+    e || (this.paused = true)
+
+    if (this.$element.find('.next, .prev').length && $.support.transition) {
+      this.$element.trigger($.support.transition.end)
+      this.cycle(true)
+    }
+
+    this.interval = clearInterval(this.interval)
+
+    return this
+  }
+
+  Carousel.prototype.next = function () {
+    if (this.sliding) return
+    return this.slide('next')
+  }
+
+  Carousel.prototype.prev = function () {
+    if (this.sliding) return
+    return this.slide('prev')
+  }
+
+  Carousel.prototype.slide = function (type, next) {
+    var $active   = this.$element.find('.item.active')
+    var $next     = next || this.getItemForDirection(type, $active)
+    var isCycling = this.interval
+    var direction = type == 'next' ? 'left' : 'right'
+    var that      = this
+
+    if ($next.hasClass('active')) return (this.sliding = false)
+
+    var relatedTarget = $next[0]
+    var slideEvent = $.Event('slide.bs.carousel', {
+      relatedTarget: relatedTarget,
+      direction: direction
+    })
+    this.$element.trigger(slideEvent)
+    if (slideEvent.isDefaultPrevented()) return
+
+    this.sliding = true
+
+    isCycling && this.pause()
+
+    if (this.$indicators.length) {
+      this.$indicators.find('.active').removeClass('active')
+      var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
+      $nextIndicator && $nextIndicator.addClass('active')
+    }
+
+    var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
+    if ($.support.transition && this.$element.hasClass('slide')) {
+      $next.addClass(type)
+      $next[0].offsetWidth // force reflow
+      $active.addClass(direction)
+      $next.addClass(direction)
+      $active
+        .one('bsTransitionEnd', function () {
+          $next.removeClass([type, direction].join(' ')).addClass('active')
+          $active.removeClass(['active', direction].join(' '))
+          that.sliding = false
+          setTimeout(function () {
+            that.$element.trigger(slidEvent)
+          }, 0)
+        })
+        .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
+    } else {
+      $active.removeClass('active')
+      $next.addClass('active')
+      this.sliding = false
+      this.$element.trigger(slidEvent)
+    }
+
+    isCycling && this.cycle()
+
+    return this
+  }
+
+
+  // CAROUSEL PLUGIN DEFINITION
+  // ==========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.carousel')
+      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
+      var action  = typeof option == 'string' ? option : options.slide
+
+      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
+      if (typeof option == 'number') data.to(option)
+      else if (action) data[action]()
+      else if (options.interval) data.pause().cycle()
+    })
+  }
+
+  var old = $.fn.carousel
+
+  $.fn.carousel             = Plugin
+  $.fn.carousel.Constructor = Carousel
+
+
+  // CAROUSEL NO CONFLICT
+  // ====================
+
+  $.fn.carousel.noConflict = function () {
+    $.fn.carousel = old
+    return this
+  }
+
+
+  // CAROUSEL DATA-API
+  // =================
+
+  var clickHandler = function (e) {
+    var href
+    var $this   = $(this)
+    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
+    if (!$target.hasClass('carousel')) return
+    var options = $.extend({}, $target.data(), $this.data())
+    var slideIndex = $this.attr('data-slide-to')
+    if (slideIndex) options.interval = false
+
+    Plugin.call($target, options)
+
+    if (slideIndex) {
+      $target.data('bs.carousel').to(slideIndex)
+    }
+
+    e.preventDefault()
+  }
+
+  $(document)
+    .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
+    .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
+
+  $(window).on('load', function () {
+    $('[data-ride="carousel"]').each(function () {
+      var $carousel = $(this)
+      Plugin.call($carousel, $carousel.data())
+    })
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: collapse.js v3.3.6
+ * http://getbootstrap.com/javascript/#collapse
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // COLLAPSE PUBLIC CLASS DEFINITION
+  // ================================
+
+  var Collapse = function (element, options) {
+    this.$element      = $(element)
+    this.options       = $.extend({}, Collapse.DEFAULTS, options)
+    this.$trigger      = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
+                           '[data-toggle="collapse"][data-target="#' + element.id + '"]')
+    this.transitioning = null
+
+    if (this.options.parent) {
+      this.$parent = this.getParent()
+    } else {
+      this.addAriaAndCollapsedClass(this.$element, this.$trigger)
+    }
+
+    if (this.options.toggle) this.toggle()
+  }
+
+  Collapse.VERSION  = '3.3.6'
+
+  Collapse.TRANSITION_DURATION = 350
+
+  Collapse.DEFAULTS = {
+    toggle: true
+  }
+
+  Collapse.prototype.dimension = function () {
+    var hasWidth = this.$element.hasClass('width')
+    return hasWidth ? 'width' : 'height'
+  }
+
+  Collapse.prototype.show = function () {
+    if (this.transitioning || this.$element.hasClass('in')) return
+
+    var activesData
+    var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
+
+    if (actives && actives.length) {
+      activesData = actives.data('bs.collapse')
+      if (activesData && activesData.transitioning) return
+    }
+
+    var startEvent = $.Event('show.bs.collapse')
+    this.$element.trigger(startEvent)
+    if (startEvent.isDefaultPrevented()) return
+
+    if (actives && actives.length) {
+      Plugin.call(actives, 'hide')
+      activesData || actives.data('bs.collapse', null)
+    }
+
+    var dimension = this.dimension()
+
+    this.$element
+      .removeClass('collapse')
+      .addClass('collapsing')[dimension](0)
+      .attr('aria-expanded', true)
+
+    this.$trigger
+      .removeClass('collapsed')
+      .attr('aria-expanded', true)
+
+    this.transitioning = 1
+
+    var complete = function () {
+      this.$element
+        .removeClass('collapsing')
+        .addClass('collapse in')[dimension]('')
+      this.transitioning = 0
+      this.$element
+        .trigger('shown.bs.collapse')
+    }
+
+    if (!$.support.transition) return complete.call(this)
+
+    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+
+    this.$element
+      .one('bsTransitionEnd', $.proxy(complete, this))
+      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
+  }
+
+  Collapse.prototype.hide = function () {
+    if (this.transitioning || !this.$element.hasClass('in')) return
+
+    var startEvent = $.Event('hide.bs.collapse')
+    this.$element.trigger(startEvent)
+    if (startEvent.isDefaultPrevented()) return
+
+    var dimension = this.dimension()
+
+    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+
+    this.$element
+      .addClass('collapsing')
+      .removeClass('collapse in')
+      .attr('aria-expanded', false)
+
+    this.$trigger
+      .addClass('collapsed')
+      .attr('aria-expanded', false)
+
+    this.transitioning = 1
+
+    var complete = function () {
+      this.transitioning = 0
+      this.$element
+        .removeClass('collapsing')
+        .addClass('collapse')
+        .trigger('hidden.bs.collapse')
+    }
+
+    if (!$.support.transition) return complete.call(this)
+
+    this.$element
+      [dimension](0)
+      .one('bsTransitionEnd', $.proxy(complete, this))
+      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
+  }
+
+  Collapse.prototype.toggle = function () {
+    this[this.$element.hasClass('in') ? 'hide' : 'show']()
+  }
+
+  Collapse.prototype.getParent = function () {
+    return $(this.options.parent)
+      .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
+      .each($.proxy(function (i, element) {
+        var $element = $(element)
+        this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
+      }, this))
+      .end()
+  }
+
+  Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
+    var isOpen = $element.hasClass('in')
+
+    $element.attr('aria-expanded', isOpen)
+    $trigger
+      .toggleClass('collapsed', !isOpen)
+      .attr('aria-expanded', isOpen)
+  }
+
+  function getTargetFromTrigger($trigger) {
+    var href
+    var target = $trigger.attr('data-target')
+      || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
+
+    return $(target)
+  }
+
+
+  // COLLAPSE PLUGIN DEFINITION
+  // ==========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.collapse')
+      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+      if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
+      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.collapse
+
+  $.fn.collapse             = Plugin
+  $.fn.collapse.Constructor = Collapse
+
+
+  // COLLAPSE NO CONFLICT
+  // ====================
+
+  $.fn.collapse.noConflict = function () {
+    $.fn.collapse = old
+    return this
+  }
+
+
+  // COLLAPSE DATA-API
+  // =================
+
+  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
+    var $this   = $(this)
+
+    if (!$this.attr('data-target')) e.preventDefault()
+
+    var $target = getTargetFromTrigger($this)
+    var data    = $target.data('bs.collapse')
+    var option  = data ? 'toggle' : $this.data()
+
+    Plugin.call($target, option)
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.3.6
+ * http://getbootstrap.com/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // DROPDOWN CLASS DEFINITION
+  // =========================
+
+  var backdrop = '.dropdown-backdrop'
+  var toggle   = '[data-toggle="dropdown"]'
+  var Dropdown = function (element) {
+    $(element).on('click.bs.dropdown', this.toggle)
+  }
+
+  Dropdown.VERSION = '3.3.6'
+
+  function getParent($this) {
+    var selector = $this.attr('data-target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    var $parent = selector && $(selector)
+
+    return $parent && $parent.length ? $parent : $this.parent()
+  }
+
+  function clearMenus(e) {
+    if (e && e.which === 3) return
+    $(backdrop).remove()
+    $(toggle).each(function () {
+      var $this         = $(this)
+      var $parent       = getParent($this)
+      var relatedTarget = { relatedTarget: this }
+
+      if (!$parent.hasClass('open')) return
+
+      if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
+
+      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+
+      if (e.isDefaultPrevented()) return
+
+      $this.attr('aria-expanded', 'false')
+      $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
+    })
+  }
+
+  Dropdown.prototype.toggle = function (e) {
+    var $this = $(this)
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    clearMenus()
+
+    if (!isActive) {
+      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+        // if mobile we use a backdrop because click events don't delegate
+        $(document.createElement('div'))
+          .addClass('dropdown-backdrop')
+          .insertAfter($(this))
+          .on('click', clearMenus)
+      }
+
+      var relatedTarget = { relatedTarget: this }
+      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+      if (e.isDefaultPrevented()) return
+
+      $this
+        .trigger('focus')
+        .attr('aria-expanded', 'true')
+
+      $parent
+        .toggleClass('open')
+        .trigger($.Event('shown.bs.dropdown', relatedTarget))
+    }
+
+    return false
+  }
+
+  Dropdown.prototype.keydown = function (e) {
+    if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+
+    var $this = $(this)
+
+    e.preventDefault()
+    e.stopPropagation()
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    if (!isActive && e.which != 27 || isActive && e.which == 27) {
+      if (e.which == 27) $parent.find(toggle).trigger('focus')
+      return $this.trigger('click')
+    }
+
+    var desc = ' li:not(.disabled):visible a'
+    var $items = $parent.find('.dropdown-menu' + desc)
+
+    if (!$items.length) return
+
+    var index = $items.index(e.target)
+
+    if (e.which == 38 && index > 0)                 index--         // up
+    if (e.which == 40 && index < $items.length - 1) index++         // down
+    if (!~index)                                    index = 0
+
+    $items.eq(index).trigger('focus')
+  }
+
+
+  // DROPDOWN PLUGIN DEFINITION
+  // ==========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.dropdown')
+
+      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  var old = $.fn.dropdown
+
+  $.fn.dropdown             = Plugin
+  $.fn.dropdown.Constructor = Dropdown
+
+
+  // DROPDOWN NO CONFLICT
+  // ====================
+
+  $.fn.dropdown.noConflict = function () {
+    $.fn.dropdown = old
+    return this
+  }
+
+
+  // APPLY TO STANDARD DROPDOWN ELEMENTS
+  // ===================================
+
+  $(document)
+    .on('click.bs.dropdown.data-api', clearMenus)
+    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+    .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
+    .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: modal.js v3.3.6
+ * http://getbootstrap.com/javascript/#modals
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // MODAL CLASS DEFINITION
+  // ======================
+
+  var Modal = function (element, options) {
+    this.options             = options
+    this.$body               = $(document.body)
+    this.$element            = $(element)
+    this.$dialog             = this.$element.find('.modal-dialog')
+    this.$backdrop           = null
+    this.isShown             = null
+    this.originalBodyPad     = null
+    this.scrollbarWidth      = 0
+    this.ignoreBackdropClick = false
+
+    if (this.options.remote) {
+      this.$element
+        .find('.modal-content')
+        .load(this.options.remote, $.proxy(function () {
+          this.$element.trigger('loaded.bs.modal')
+        }, this))
+    }
+  }
+
+  Modal.VERSION  = '3.3.6'
+
+  Modal.TRANSITION_DURATION = 300
+  Modal.BACKDROP_TRANSITION_DURATION = 150
+
+  Modal.DEFAULTS = {
+    backdrop: true,
+    keyboard: true,
+    show: true
+  }
+
+  Modal.prototype.toggle = function (_relatedTarget) {
+    return this.isShown ? this.hide() : this.show(_relatedTarget)
+  }
+
+  Modal.prototype.show = function (_relatedTarget) {
+    var that = this
+    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+    this.$element.trigger(e)
+
+    if (this.isShown || e.isDefaultPrevented()) return
+
+    this.isShown = true
+
+    this.checkScrollbar()
+    this.setScrollbar()
+    this.$body.addClass('modal-open')
+
+    this.escape()
+    this.resize()
+
+    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+    this.$dialog.on('mousedown.dismiss.bs.modal', function () {
+      that.$element.one('mouseup.dismiss.bs.modal', function (e) {
+        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
+      })
+    })
+
+    this.backdrop(function () {
+      var transition = $.support.transition && that.$element.hasClass('fade')
+
+      if (!that.$element.parent().length) {
+        that.$element.appendTo(that.$body) // don't move modals dom position
+      }
+
+      that.$element
+        .show()
+        .scrollTop(0)
+
+      that.adjustDialog()
+
+      if (transition) {
+        that.$element[0].offsetWidth // force reflow
+      }
+
+      that.$element.addClass('in')
+
+      that.enforceFocus()
+
+      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+      transition ?
+        that.$dialog // wait for modal to slide in
+          .one('bsTransitionEnd', function () {
+            that.$element.trigger('focus').trigger(e)
+          })
+          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+        that.$element.trigger('focus').trigger(e)
+    })
+  }
+
+  Modal.prototype.hide = function (e) {
+    if (e) e.preventDefault()
+
+    e = $.Event('hide.bs.modal')
+
+    this.$element.trigger(e)
+
+    if (!this.isShown || e.isDefaultPrevented()) return
+
+    this.isShown = false
+
+    this.escape()
+    this.resize()
+
+    $(document).off('focusin.bs.modal')
+
+    this.$element
+      .removeClass('in')
+      .off('click.dismiss.bs.modal')
+      .off('mouseup.dismiss.bs.modal')
+
+    this.$dialog.off('mousedown.dismiss.bs.modal')
+
+    $.support.transition && this.$element.hasClass('fade') ?
+      this.$element
+        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
+        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+      this.hideModal()
+  }
+
+  Modal.prototype.enforceFocus = function () {
+    $(document)
+      .off('focusin.bs.modal') // guard against infinite focus loop
+      .on('focusin.bs.modal', $.proxy(function (e) {
+        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+          this.$element.trigger('focus')
+        }
+      }, this))
+  }
+
+  Modal.prototype.escape = function () {
+    if (this.isShown && this.options.keyboard) {
+      this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
+        e.which == 27 && this.hide()
+      }, this))
+    } else if (!this.isShown) {
+      this.$element.off('keydown.dismiss.bs.modal')
+    }
+  }
+
+  Modal.prototype.resize = function () {
+    if (this.isShown) {
+      $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
+    } else {
+      $(window).off('resize.bs.modal')
+    }
+  }
+
+  Modal.prototype.hideModal = function () {
+    var that = this
+    this.$element.hide()
+    this.backdrop(function () {
+      that.$body.removeClass('modal-open')
+      that.resetAdjustments()
+      that.resetScrollbar()
+      that.$element.trigger('hidden.bs.modal')
+    })
+  }
+
+  Modal.prototype.removeBackdrop = function () {
+    this.$backdrop && this.$backdrop.remove()
+    this.$backdrop = null
+  }
+
+  Modal.prototype.backdrop = function (callback) {
+    var that = this
+    var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+    if (this.isShown && this.options.backdrop) {
+      var doAnimate = $.support.transition && animate
+
+      this.$backdrop = $(document.createElement('div'))
+        .addClass('modal-backdrop ' + animate)
+        .appendTo(this.$body)
+
+      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+        if (this.ignoreBackdropClick) {
+          this.ignoreBackdropClick = false
+          return
+        }
+        if (e.target !== e.currentTarget) return
+        this.options.backdrop == 'static'
+          ? this.$element[0].focus()
+          : this.hide()
+      }, this))
+
+      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+      this.$backdrop.addClass('in')
+
+      if (!callback) return
+
+      doAnimate ?
+        this.$backdrop
+          .one('bsTransitionEnd', callback)
+          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+        callback()
+
+    } else if (!this.isShown && this.$backdrop) {
+      this.$backdrop.removeClass('in')
+
+      var callbackRemove = function () {
+        that.removeBackdrop()
+        callback && callback()
+      }
+      $.support.transition && this.$element.hasClass('fade') ?
+        this.$backdrop
+          .one('bsTransitionEnd', callbackRemove)
+          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+        callbackRemove()
+
+    } else if (callback) {
+      callback()
+    }
+  }
+
+  // these following methods are used to handle overflowing modals
+
+  Modal.prototype.handleUpdate = function () {
+    this.adjustDialog()
+  }
+
+  Modal.prototype.adjustDialog = function () {
+    var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
+
+    this.$element.css({
+      paddingLeft:  !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
+      paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
+    })
+  }
+
+  Modal.prototype.resetAdjustments = function () {
+    this.$element.css({
+      paddingLeft: '',
+      paddingRight: ''
+    })
+  }
+
+  Modal.prototype.checkScrollbar = function () {
+    var fullWindowWidth = window.innerWidth
+    if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
+      var documentElementRect = document.documentElement.getBoundingClientRect()
+      fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
+    }
+    this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
+    this.scrollbarWidth = this.measureScrollbar()
+  }
+
+  Modal.prototype.setScrollbar = function () {
+    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
+    this.originalBodyPad = document.body.style.paddingRight || ''
+    if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
+  }
+
+  Modal.prototype.resetScrollbar = function () {
+    this.$body.css('padding-right', this.originalBodyPad)
+  }
+
+  Modal.prototype.measureScrollbar = function () { // thx walsh
+    var scrollDiv = document.createElement('div')
+    scrollDiv.className = 'modal-scrollbar-measure'
+    this.$body.append(scrollDiv)
+    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+    this.$body[0].removeChild(scrollDiv)
+    return scrollbarWidth
+  }
+
+
+  // MODAL PLUGIN DEFINITION
+  // =======================
+
+  function Plugin(option, _relatedTarget) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.modal')
+      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+      if (typeof option == 'string') data[option](_relatedTarget)
+      else if (options.show) data.show(_relatedTarget)
+    })
+  }
+
+  var old = $.fn.modal
+
+  $.fn.modal             = Plugin
+  $.fn.modal.Constructor = Modal
+
+
+  // MODAL NO CONFLICT
+  // =================
+
+  $.fn.modal.noConflict = function () {
+    $.fn.modal = old
+    return this
+  }
+
+
+  // MODAL DATA-API
+  // ==============
+
+  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+    var $this   = $(this)
+    var href    = $this.attr('href')
+    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
+    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+    if ($this.is('a')) e.preventDefault()
+
+    $target.one('show.bs.modal', function (showEvent) {
+      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
+      $target.one('hidden.bs.modal', function () {
+        $this.is(':visible') && $this.trigger('focus')
+      })
+    })
+    Plugin.call($target, option, this)
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.3.6
+ * http://getbootstrap.com/javascript/#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // TOOLTIP PUBLIC CLASS DEFINITION
+  // ===============================
+
+  var Tooltip = function (element, options) {
+    this.type       = null
+    this.options    = null
+    this.enabled    = null
+    this.timeout    = null
+    this.hoverState = null
+    this.$element   = null
+    this.inState    = null
+
+    this.init('tooltip', element, options)
+  }
+
+  Tooltip.VERSION  = '3.3.6'
+
+  Tooltip.TRANSITION_DURATION = 150
+
+  Tooltip.DEFAULTS = {
+    animation: true,
+    placement: 'top',
+    selector: false,
+    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+    trigger: 'hover focus',
+    title: '',
+    delay: 0,
+    html: false,
+    container: false,
+    viewport: {
+      selector: 'body',
+      padding: 0
+    }
+  }
+
+  Tooltip.prototype.init = function (type, element, options) {
+    this.enabled   = true
+    this.type      = type
+    this.$element  = $(element)
+    this.options   = this.getOptions(options)
+    this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
+    this.inState   = { click: false, hover: false, focus: false }
+
+    if (this.$element[0] instanceof document.constructor && !this.options.selector) {
+      throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
+    }
+
+    var triggers = this.options.trigger.split(' ')
+
+    for (var i = triggers.length; i--;) {
+      var trigger = triggers[i]
+
+      if (trigger == 'click') {
+        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+      } else if (trigger != 'manual') {
+        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
+        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+      }
+    }
+
+    this.options.selector ?
+      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+      this.fixTitle()
+  }
+
+  Tooltip.prototype.getDefaults = function () {
+    return Tooltip.DEFAULTS
+  }
+
+  Tooltip.prototype.getOptions = function (options) {
+    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+    if (options.delay && typeof options.delay == 'number') {
+      options.delay = {
+        show: options.delay,
+        hide: options.delay
+      }
+    }
+
+    return options
+  }
+
+  Tooltip.prototype.getDelegateOptions = function () {
+    var options  = {}
+    var defaults = this.getDefaults()
+
+    this._options && $.each(this._options, function (key, value) {
+      if (defaults[key] != value) options[key] = value
+    })
+
+    return options
+  }
+
+  Tooltip.prototype.enter = function (obj) {
+    var self = obj instanceof this.constructor ?
+      obj : $(obj.currentTarget).data('bs.' + this.type)
+
+    if (!self) {
+      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+      $(obj.currentTarget).data('bs.' + this.type, self)
+    }
+
+    if (obj instanceof $.Event) {
+      self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
+    }
+
+    if (self.tip().hasClass('in') || self.hoverState == 'in') {
+      self.hoverState = 'in'
+      return
+    }
+
+    clearTimeout(self.timeout)
+
+    self.hoverState = 'in'
+
+    if (!self.options.delay || !self.options.delay.show) return self.show()
+
+    self.timeout = setTimeout(function () {
+      if (self.hoverState == 'in') self.show()
+    }, self.options.delay.show)
+  }
+
+  Tooltip.prototype.isInStateTrue = function () {
+    for (var key in this.inState) {
+      if (this.inState[key]) return true
+    }
+
+    return false
+  }
+
+  Tooltip.prototype.leave = function (obj) {
+    var self = obj instanceof this.constructor ?
+      obj : $(obj.currentTarget).data('bs.' + this.type)
+
+    if (!self) {
+      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+      $(obj.currentTarget).data('bs.' + this.type, self)
+    }
+
+    if (obj instanceof $.Event) {
+      self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
+    }
+
+    if (self.isInStateTrue()) return
+
+    clearTimeout(self.timeout)
+
+    self.hoverState = 'out'
+
+    if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+    self.timeout = setTimeout(function () {
+      if (self.hoverState == 'out') self.hide()
+    }, self.options.delay.hide)
+  }
+
+  Tooltip.prototype.show = function () {
+    var e = $.Event('show.bs.' + this.type)
+
+    if (this.hasContent() && this.enabled) {
+      this.$element.trigger(e)
+
+      var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
+      if (e.isDefaultPrevented() || !inDom) return
+      var that = this
+
+      var $tip = this.tip()
+
+      var tipId = this.getUID(this.type)
+
+      this.setContent()
+      $tip.attr('id', tipId)
+      this.$element.attr('aria-describedby', tipId)
+
+      if (this.options.animation) $tip.addClass('fade')
+
+      var placement = typeof this.options.placement == 'function' ?
+        this.options.placement.call(this, $tip[0], this.$element[0]) :
+        this.options.placement
+
+      var autoToken = /\s?auto?\s?/i
+      var autoPlace = autoToken.test(placement)
+      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+      $tip
+        .detach()
+        .css({ top: 0, left: 0, display: 'block' })
+        .addClass(placement)
+        .data('bs.' + this.type, this)
+
+      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+      this.$element.trigger('inserted.bs.' + this.type)
+
+      var pos          = this.getPosition()
+      var actualWidth  = $tip[0].offsetWidth
+      var actualHeight = $tip[0].offsetHeight
+
+      if (autoPlace) {
+        var orgPlacement = placement
+        var viewportDim = this.getPosition(this.$viewport)
+
+        placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top'    :
+                    placement == 'top'    && pos.top    - actualHeight < viewportDim.top    ? 'bottom' :
+                    placement == 'right'  && pos.right  + actualWidth  > viewportDim.width  ? 'left'   :
+                    placement == 'left'   && pos.left   - actualWidth  < viewportDim.left   ? 'right'  :
+                    placement
+
+        $tip
+          .removeClass(orgPlacement)
+          .addClass(placement)
+      }
+
+      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+      this.applyPlacement(calculatedOffset, placement)
+
+      var complete = function () {
+        var prevHoverState = that.hoverState
+        that.$element.trigger('shown.bs.' + that.type)
+        that.hoverState = null
+
+        if (prevHoverState == 'out') that.leave(that)
+      }
+
+      $.support.transition && this.$tip.hasClass('fade') ?
+        $tip
+          .one('bsTransitionEnd', complete)
+          .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+        complete()
+    }
+  }
+
+  Tooltip.prototype.applyPlacement = function (offset, placement) {
+    var $tip   = this.tip()
+    var width  = $tip[0].offsetWidth
+    var height = $tip[0].offsetHeight
+
+    // manually read margins because getBoundingClientRect includes difference
+    var marginTop = parseInt($tip.css('margin-top'), 10)
+    var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+    // we must check for NaN for ie 8/9
+    if (isNaN(marginTop))  marginTop  = 0
+    if (isNaN(marginLeft)) marginLeft = 0
+
+    offset.top  += marginTop
+    offset.left += marginLeft
+
+    // $.fn.offset doesn't round pixel values
+    // so we use setOffset directly with our own function B-0
+    $.offset.setOffset($tip[0], $.extend({
+      using: function (props) {
+        $tip.css({
+          top: Math.round(props.top),
+          left: Math.round(props.left)
+        })
+      }
+    }, offset), 0)
+
+    $tip.addClass('in')
+
+    // check to see if placing tip in new offset caused the tip to resize itself
+    var actualWidth  = $tip[0].offsetWidth
+    var actualHeight = $tip[0].offsetHeight
+
+    if (placement == 'top' && actualHeight != height) {
+      offset.top = offset.top + height - actualHeight
+    }
+
+    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+
+    if (delta.left) offset.left += delta.left
+    else offset.top += delta.top
+
+    var isVertical          = /top|bottom/.test(placement)
+    var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+    var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
+
+    $tip.offset(offset)
+    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
+  }
+
+  Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
+    this.arrow()
+      .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
+      .css(isVertical ? 'top' : 'left', '')
+  }
+
+  Tooltip.prototype.setContent = function () {
+    var $tip  = this.tip()
+    var title = this.getTitle()
+
+    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+    $tip.removeClass('fade in top bottom left right')
+  }
+
+  Tooltip.prototype.hide = function (callback) {
+    var that = this
+    var $tip = $(this.$tip)
+    var e    = $.Event('hide.bs.' + this.type)
+
+    function complete() {
+      if (that.hoverState != 'in') $tip.detach()
+      that.$element
+        .removeAttr('aria-describedby')
+        .trigger('hidden.bs.' + that.type)
+      callback && callback()
+    }
+
+    this.$element.trigger(e)
+
+    if (e.isDefaultPrevented()) return
+
+    $tip.removeClass('in')
+
+    $.support.transition && $tip.hasClass('fade') ?
+      $tip
+        .one('bsTransitionEnd', complete)
+        .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+      complete()
+
+    this.hoverState = null
+
+    return this
+  }
+
+  Tooltip.prototype.fixTitle = function () {
+    var $e = this.$element
+    if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
+      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+    }
+  }
+
+  Tooltip.prototype.hasContent = function () {
+    return this.getTitle()
+  }
+
+  Tooltip.prototype.getPosition = function ($element) {
+    $element   = $element || this.$element
+
+    var el     = $element[0]
+    var isBody = el.tagName == 'BODY'
+
+    var elRect    = el.getBoundingClientRect()
+    if (elRect.width == null) {
+      // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
+      elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
+    }
+    var elOffset  = isBody ? { top: 0, left: 0 } : $element.offset()
+    var scroll    = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
+    var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
+
+    return $.extend({}, elRect, scroll, outerDims, elOffset)
+  }
+
+  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2 } :
+           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+
+  }
+
+  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
+    var delta = { top: 0, left: 0 }
+    if (!this.$viewport) return delta
+
+    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
+    var viewportDimensions = this.getPosition(this.$viewport)
+
+    if (/right|left/.test(placement)) {
+      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
+      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
+      if (topEdgeOffset < viewportDimensions.top) { // top overflow
+        delta.top = viewportDimensions.top - topEdgeOffset
+      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+        delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+      }
+    } else {
+      var leftEdgeOffset  = pos.left - viewportPadding
+      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
+      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+        delta.left = viewportDimensions.left - leftEdgeOffset
+      } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
+        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+      }
+    }
+
+    return delta
+  }
+
+  Tooltip.prototype.getTitle = function () {
+    var title
+    var $e = this.$element
+    var o  = this.options
+
+    title = $e.attr('data-original-title')
+      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
+
+    return title
+  }
+
+  Tooltip.prototype.getUID = function (prefix) {
+    do prefix += ~~(Math.random() * 1000000)
+    while (document.getElementById(prefix))
+    return prefix
+  }
+
+  Tooltip.prototype.tip = function () {
+    if (!this.$tip) {
+      this.$tip = $(this.options.template)
+      if (this.$tip.length != 1) {
+        throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
+      }
+    }
+    return this.$tip
+  }
+
+  Tooltip.prototype.arrow = function () {
+    return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+  }
+
+  Tooltip.prototype.enable = function () {
+    this.enabled = true
+  }
+
+  Tooltip.prototype.disable = function () {
+    this.enabled = false
+  }
+
+  Tooltip.prototype.toggleEnabled = function () {
+    this.enabled = !this.enabled
+  }
+
+  Tooltip.prototype.toggle = function (e) {
+    var self = this
+    if (e) {
+      self = $(e.currentTarget).data('bs.' + this.type)
+      if (!self) {
+        self = new this.constructor(e.currentTarget, this.getDelegateOptions())
+        $(e.currentTarget).data('bs.' + this.type, self)
+      }
+    }
+
+    if (e) {
+      self.inState.click = !self.inState.click
+      if (self.isInStateTrue()) self.enter(self)
+      else self.leave(self)
+    } else {
+      self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+    }
+  }
+
+  Tooltip.prototype.destroy = function () {
+    var that = this
+    clearTimeout(this.timeout)
+    this.hide(function () {
+      that.$element.off('.' + that.type).removeData('bs.' + that.type)
+      if (that.$tip) {
+        that.$tip.detach()
+      }
+      that.$tip = null
+      that.$arrow = null
+      that.$viewport = null
+    })
+  }
+
+
+  // TOOLTIP PLUGIN DEFINITION
+  // =========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.tooltip')
+      var options = typeof option == 'object' && option
+
+      if (!data && /destroy|hide/.test(option)) return
+      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.tooltip
+
+  $.fn.tooltip             = Plugin
+  $.fn.tooltip.Constructor = Tooltip
+
+
+  // TOOLTIP NO CONFLICT
+  // ===================
+
+  $.fn.tooltip.noConflict = function () {
+    $.fn.tooltip = old
+    return this
+  }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: popover.js v3.3.6
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // POPOVER PUBLIC CLASS DEFINITION
+  // ===============================
+
+  var Popover = function (element, options) {
+    this.init('popover', element, options)
+  }
+
+  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+  Popover.VERSION  = '3.3.6'
+
+  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+    placement: 'right',
+    trigger: 'click',
+    content: '',
+    template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+  })
+
+
+  // NOTE: POPOVER EXTENDS tooltip.js
+  // ================================
+
+  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+  Popover.prototype.constructor = Popover
+
+  Popover.prototype.getDefaults = function () {
+    return Popover.DEFAULTS
+  }
+
+  Popover.prototype.setContent = function () {
+    var $tip    = this.tip()
+    var title   = this.getTitle()
+    var content = this.getContent()
+
+    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+    $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
+      this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+    ](content)
+
+    $tip.removeClass('fade top bottom left right in')
+
+    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+    // this manually by checking the contents.
+    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+  }
+
+  Popover.prototype.hasContent = function () {
+    return this.getTitle() || this.getContent()
+  }
+
+  Popover.prototype.getContent = function () {
+    var $e = this.$element
+    var o  = this.options
+
+    return $e.attr('data-content')
+      || (typeof o.content == 'function' ?
+            o.content.call($e[0]) :
+            o.content)
+  }
+
+  Popover.prototype.arrow = function () {
+    return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
+  }
+
+
+  // POPOVER PLUGIN DEFINITION
+  // =========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.popover')
+      var options = typeof option == 'object' && option
+
+      if (!data && /destroy|hide/.test(option)) return
+      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.popover
+
+  $.fn.popover             = Plugin
+  $.fn.popover.Constructor = Popover
+
+
+  // POPOVER NO CONFLICT
+  // ===================
+
+  $.fn.popover.noConflict = function () {
+    $.fn.popover = old
+    return this
+  }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.3.6
+ * http://getbootstrap.com/javascript/#scrollspy
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // SCROLLSPY CLASS DEFINITION
+  // ==========================
+
+  function ScrollSpy(element, options) {
+    this.$body          = $(document.body)
+    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
+    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
+    this.selector       = (this.options.target || '') + ' .nav li > a'
+    this.offsets        = []
+    this.targets        = []
+    this.activeTarget   = null
+    this.scrollHeight   = 0
+
+    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
+    this.refresh()
+    this.process()
+  }
+
+  ScrollSpy.VERSION  = '3.3.6'
+
+  ScrollSpy.DEFAULTS = {
+    offset: 10
+  }
+
+  ScrollSpy.prototype.getScrollHeight = function () {
+    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
+  }
+
+  ScrollSpy.prototype.refresh = function () {
+    var that          = this
+    var offsetMethod  = 'offset'
+    var offsetBase    = 0
+
+    this.offsets      = []
+    this.targets      = []
+    this.scrollHeight = this.getScrollHeight()
+
+    if (!$.isWindow(this.$scrollElement[0])) {
+      offsetMethod = 'position'
+      offsetBase   = this.$scrollElement.scrollTop()
+    }
+
+    this.$body
+      .find(this.selector)
+      .map(function () {
+        var $el   = $(this)
+        var href  = $el.data('target') || $el.attr('href')
+        var $href = /^#./.test(href) && $(href)
+
+        return ($href
+          && $href.length
+          && $href.is(':visible')
+          && [[$href[offsetMethod]().top + offsetBase, href]]) || null
+      })
+      .sort(function (a, b) { return a[0] - b[0] })
+      .each(function () {
+        that.offsets.push(this[0])
+        that.targets.push(this[1])
+      })
+  }
+
+  ScrollSpy.prototype.process = function () {
+    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
+    var scrollHeight = this.getScrollHeight()
+    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()
+    var offsets      = this.offsets
+    var targets      = this.targets
+    var activeTarget = this.activeTarget
+    var i
+
+    if (this.scrollHeight != scrollHeight) {
+      this.refresh()
+    }
+
+    if (scrollTop >= maxScroll) {
+      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
+    }
+
+    if (activeTarget && scrollTop < offsets[0]) {
+      this.activeTarget = null
+      return this.clear()
+    }
+
+    for (i = offsets.length; i--;) {
+      activeTarget != targets[i]
+        && scrollTop >= offsets[i]
+        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
+        && this.activate(targets[i])
+    }
+  }
+
+  ScrollSpy.prototype.activate = function (target) {
+    this.activeTarget = target
+
+    this.clear()
+
+    var selector = this.selector +
+      '[data-target="' + target + '"],' +
+      this.selector + '[href="' + target + '"]'
+
+    var active = $(selector)
+      .parents('li')
+      .addClass('active')
+
+    if (active.parent('.dropdown-menu').length) {
+      active = active
+        .closest('li.dropdown')
+        .addClass('active')
+    }
+
+    active.trigger('activate.bs.scrollspy')
+  }
+
+  ScrollSpy.prototype.clear = function () {
+    $(this.selector)
+      .parentsUntil(this.options.target, '.active')
+      .removeClass('active')
+  }
+
+
+  // SCROLLSPY PLUGIN DEFINITION
+  // ===========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.scrollspy')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.scrollspy
+
+  $.fn.scrollspy             = Plugin
+  $.fn.scrollspy.Constructor = ScrollSpy
+
+
+  // SCROLLSPY NO CONFLICT
+  // =====================
+
+  $.fn.scrollspy.noConflict = function () {
+    $.fn.scrollspy = old
+    return this
+  }
+
+
+  // SCROLLSPY DATA-API
+  // ==================
+
+  $(window).on('load.bs.scrollspy.data-api', function () {
+    $('[data-spy="scroll"]').each(function () {
+      var $spy = $(this)
+      Plugin.call($spy, $spy.data())
+    })
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tab.js v3.3.6
+ * http://getbootstrap.com/javascript/#tabs
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // TAB CLASS DEFINITION
+  // ====================
+
+  var Tab = function (element) {
+    // jscs:disable requireDollarBeforejQueryAssignment
+    this.element = $(element)
+    // jscs:enable requireDollarBeforejQueryAssignment
+  }
+
+  Tab.VERSION = '3.3.6'
+
+  Tab.TRANSITION_DURATION = 150
+
+  Tab.prototype.show = function () {
+    var $this    = this.element
+    var $ul      = $this.closest('ul:not(.dropdown-menu)')
+    var selector = $this.data('target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    if ($this.parent('li').hasClass('active')) return
+
+    var $previous = $ul.find('.active:last a')
+    var hideEvent = $.Event('hide.bs.tab', {
+      relatedTarget: $this[0]
+    })
+    var showEvent = $.Event('show.bs.tab', {
+      relatedTarget: $previous[0]
+    })
+
+    $previous.trigger(hideEvent)
+    $this.trigger(showEvent)
+
+    if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+
+    var $target = $(selector)
+
+    this.activate($this.closest('li'), $ul)
+    this.activate($target, $target.parent(), function () {
+      $previous.trigger({
+        type: 'hidden.bs.tab',
+        relatedTarget: $this[0]
+      })
+      $this.trigger({
+        type: 'shown.bs.tab',
+        relatedTarget: $previous[0]
+      })
+    })
+  }
+
+  Tab.prototype.activate = function (element, container, callback) {
+    var $active    = container.find('> .active')
+    var transition = callback
+      && $.support.transition
+      && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
+
+    function next() {
+      $active
+        .removeClass('active')
+        .find('> .dropdown-menu > .active')
+          .removeClass('active')
+        .end()
+        .find('[data-toggle="tab"]')
+          .attr('aria-expanded', false)
+
+      element
+        .addClass('active')
+        .find('[data-toggle="tab"]')
+          .attr('aria-expanded', true)
+
+      if (transition) {
+        element[0].offsetWidth // reflow for transition
+        element.addClass('in')
+      } else {
+        element.removeClass('fade')
+      }
+
+      if (element.parent('.dropdown-menu').length) {
+        element
+          .closest('li.dropdown')
+            .addClass('active')
+          .end()
+          .find('[data-toggle="tab"]')
+            .attr('aria-expanded', true)
+      }
+
+      callback && callback()
+    }
+
+    $active.length && transition ?
+      $active
+        .one('bsTransitionEnd', next)
+        .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
+      next()
+
+    $active.removeClass('in')
+  }
+
+
+  // TAB PLUGIN DEFINITION
+  // =====================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.tab')
+
+      if (!data) $this.data('bs.tab', (data = new Tab(this)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.tab
+
+  $.fn.tab             = Plugin
+  $.fn.tab.Constructor = Tab
+
+
+  // TAB NO CONFLICT
+  // ===============
+
+  $.fn.tab.noConflict = function () {
+    $.fn.tab = old
+    return this
+  }
+
+
+  // TAB DATA-API
+  // ============
+
+  var clickHandler = function (e) {
+    e.preventDefault()
+    Plugin.call($(this), 'show')
+  }
+
+  $(document)
+    .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
+    .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: affix.js v3.3.6
+ * http://getbootstrap.com/javascript/#affix
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // AFFIX CLASS DEFINITION
+  // ======================
+
+  var Affix = function (element, options) {
+    this.options = $.extend({}, Affix.DEFAULTS, options)
+
+    this.$target = $(this.options.target)
+      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))
+
+    this.$element     = $(element)
+    this.affixed      = null
+    this.unpin        = null
+    this.pinnedOffset = null
+
+    this.checkPosition()
+  }
+
+  Affix.VERSION  = '3.3.6'
+
+  Affix.RESET    = 'affix affix-top affix-bottom'
+
+  Affix.DEFAULTS = {
+    offset: 0,
+    target: window
+  }
+
+  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
+    var scrollTop    = this.$target.scrollTop()
+    var position     = this.$element.offset()
+    var targetHeight = this.$target.height()
+
+    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
+
+    if (this.affixed == 'bottom') {
+      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
+      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
+    }
+
+    var initializing   = this.affixed == null
+    var colliderTop    = initializing ? scrollTop : position.top
+    var colliderHeight = initializing ? targetHeight : height
+
+    if (offsetTop != null && scrollTop <= offsetTop) return 'top'
+    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
+
+    return false
+  }
+
+  Affix.prototype.getPinnedOffset = function () {
+    if (this.pinnedOffset) return this.pinnedOffset
+    this.$element.removeClass(Affix.RESET).addClass('affix')
+    var scrollTop = this.$target.scrollTop()
+    var position  = this.$element.offset()
+    return (this.pinnedOffset = position.top - scrollTop)
+  }
+
+  Affix.prototype.checkPositionWithEventLoop = function () {
+    setTimeout($.proxy(this.checkPosition, this), 1)
+  }
+
+  Affix.prototype.checkPosition = function () {
+    if (!this.$element.is(':visible')) return
+
+    var height       = this.$element.height()
+    var offset       = this.options.offset
+    var offsetTop    = offset.top
+    var offsetBottom = offset.bottom
+    var scrollHeight = Math.max($(document).height(), $(document.body).height())
+
+    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
+    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
+    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+
+    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
+
+    if (this.affixed != affix) {
+      if (this.unpin != null) this.$element.css('top', '')
+
+      var affixType = 'affix' + (affix ? '-' + affix : '')
+      var e         = $.Event(affixType + '.bs.affix')
+
+      this.$element.trigger(e)
+
+      if (e.isDefaultPrevented()) return
+
+      this.affixed = affix
+      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+
+      this.$element
+        .removeClass(Affix.RESET)
+        .addClass(affixType)
+        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
+    }
+
+    if (affix == 'bottom') {
+      this.$element.offset({
+        top: scrollHeight - height - offsetBottom
+      })
+    }
+  }
+
+
+  // AFFIX PLUGIN DEFINITION
+  // =======================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.affix')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.affix
+
+  $.fn.affix             = Plugin
+  $.fn.affix.Constructor = Affix
+
+
+  // AFFIX NO CONFLICT
+  // =================
+
+  $.fn.affix.noConflict = function () {
+    $.fn.affix = old
+    return this
+  }
+
+
+  // AFFIX DATA-API
+  // ==============
+
+  $(window).on('load', function () {
+    $('[data-spy="affix"]').each(function () {
+      var $spy = $(this)
+      var data = $spy.data()
+
+      data.offset = data.offset || {}
+
+      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
+      if (data.offsetTop    != null) data.offset.top    = data.offsetTop
+
+      Plugin.call($spy, data)
+    })
+  })
+
+}(jQuery);
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/bootstrap.min.js
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/bootstrap.min.js	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/bootstrap.min.js	(revision 5095)
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.3.6 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.6",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.6",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.6",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.6",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.6",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.6",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");
+d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.6",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.6",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/npm.js
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/npm.js	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/bootstrap/js/npm.js	(revision 5095)
@@ -0,0 +1,13 @@
+// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
+require('../../js/transition.js')
+require('../../js/alert.js')
+require('../../js/button.js')
+require('../../js/carousel.js')
+require('../../js/collapse.js')
+require('../../js/dropdown.js')
+require('../../js/modal.js')
+require('../../js/tooltip.js')
+require('../../js/popover.js')
+require('../../js/scrollspy.js')
+require('../../js/tab.js')
+require('../../js/affix.js')
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm-terms-of-use.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm-terms-of-use.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm-terms-of-use.html	(revision 5095)
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM: Terms of Use</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet"
+  media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM: Terms of Use</h1>
+  </div>
+
+  <div class="container-fluid">
+  <div class="row">
+  <div class="col-md-12">
+
+  <h2 id="software.licence">FCM Software: Licence</h2>
+
+  <p>FCM is free software: you can redistribute it and/or modify it under the
+  terms of the <a href="http://www.gnu.org/licenses/gpl.html" rel="license">GNU
+  General Public License</a> as published by the Free Software Foundation,
+  either version 3 of the License, or (at your option) any later version.</p>
+
+  <p>FCM is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+  details.</p>
+
+  <p>You should have received a copy of the GNU General Public License along
+  with FCM. If not, see
+  <a href="http://www.gnu.org/licenses/"
+  rel="license">http://www.gnu.org/licenses/</a>.</p>
+
+  <h2 id="doc.licence">FCM Documentation: Licence</h2>
+
+  <p>You may use and re-use Crown copyright information from any part of the
+  <a href="..">FCM Documentation</a> (not including logos) free of charge in
+  any format or medium, under the terms and conditions of the <a href=
+  "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+  "license">Open Government Licence</a>, provided it is reproduced accurately
+  and not used in a misleading context. Where any of the Crown copyright
+  items on this website are being republished or copied to others, the source
+  of the material must be identified and the copyright status
+  acknowledged.</p>
+
+  <p>See the <a href=
+  "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+  "license">Open Government Licence</a> for the full conditions of this
+  licence and for information on how to attribute the source of material.</p>
+
+  <h2 id="team">FCM: Maintenance</h2>
+
+  <p>The FCM software and <a href="..">FCM Documentation</a> are maintained
+  by the <a href="mailto:metomi@metoffice.gov.uk">Modelling Infrastructure
+  Support Systems Team</a>, Met Office, FitzRoy Road, Exeter, EX1 3PB, UK.</p>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm-version.js
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm-version.js	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm-version.js	(revision 5095)
@@ -0,0 +1,1 @@
+FCM.VERSION="2021.05.0";
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm.css
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm.css	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm.css	(revision 5095)
@@ -0,0 +1,28 @@
+/**********************************************************************
+ * Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+ *
+ * This file is part of FCM.
+ *
+ * FCM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FCM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FCM. If not, see <http://www.gnu.org/licenses/>.
+ *
+ **********************************************************************/
+
+:hover > .sectionlink,
+.sectionlink:active,
+.sectionlink:hover {
+    display: inline;
+}
+.sectionlink {
+    display: none;
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm.js
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm.js	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/fcm.js	(revision 5095)
@@ -0,0 +1,169 @@
+/**********************************************************************
+ * Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+ *
+ * This file is part of FCM.
+ *
+ * FCM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FCM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FCM. If not, see <http://www.gnu.org/licenses/>.
+ *
+ **********************************************************************/
+
+FCM = {};
+$(function() {
+    var TITLE_COLLAPSE = "collapse";
+    var TITLE_EXPAND = "expand";
+    var IS_MAIN = true;
+
+    // Toggle a collapse/expand image.
+    function collapse_expand_icon_toggle(anchor) {
+        var icon = $("span", anchor);
+        if (icon.attr("title") == TITLE_EXPAND) {
+            icon.attr("title", TITLE_COLLAPSE);
+            icon.removeClass("glyphicon-triangle-right");
+            icon.addClass("glyphicon-triangle-bottom");
+            anchor.siblings().filter("ul").show();
+        }
+        else { // if (icon.attr("title") == "collapse")
+            icon.attr("title", TITLE_EXPAND);
+            icon.removeClass("glyphicon-triangle-bottom");
+            icon.addClass("glyphicon-triangle-right");
+            anchor.siblings().filter("ul").hide();
+        }
+    }
+
+    // Add collapse/expand anchor to a ul tree.
+    function ul_collapse_expand(ul, is_main) {
+        var nodes = $("li", ul);
+        nodes.each(function(i) {
+            var li = $(this);
+            var li_anchor = li.children().first();
+            if (!li_anchor.is("a")) {
+                return;
+            }
+            var li_ul = $("> ul", li);
+            li_ul.hide();
+            var icon = $("<span/>", {"title": TITLE_EXPAND, "aria-hidden": "true"});
+            icon.addClass("glyphicon");
+            icon.addClass("glyphicon-triangle-right");
+            icon.css("cursor", "pointer");
+            icon.css("opacity", 0.5);
+            var anchor = $("<a/>").append(icon);
+            li.prepend(" ");
+            li.prepend(anchor);
+            if (is_main) {
+                anchor.click(function() {
+                    var href = li_anchor.attr("href");
+                    $.get(
+                        href,
+                        function(data) {
+                            collapse_expand_icon_toggle(anchor);
+                            anchor.unbind("click");
+                            if (content_gen(li, data, href)) {
+                                ul_collapse_expand(li.children().filter("ul"));
+                                anchor.click(function() {
+                                    collapse_expand_icon_toggle(anchor);
+                                });
+                            }
+                            else {
+                                icon.css("opacity", 0.1);
+                            }
+                        },
+                        "html"
+                    )
+                    .error(function(b) {
+                        alert(b);
+                        anchor.unbind("click");
+                        icon.css("opacity", 0.1);
+                    });
+                });
+            }
+            else if (li_ul.length) {
+                anchor.click(function() {
+                    collapse_expand_icon_toggle(anchor);
+                });
+            }
+            else {
+                icon.css("opacity", 0.1);
+            }
+        });
+    }
+
+    // Generate table of content of a document.
+    function content_gen(root, d, d_href) {
+        if (d == null) {
+            d = document;
+        }
+        var CONTENT_INDEX_OF = {"h2": 1, "h3": 2, "h4": 3, "h5": 4, "h6": 5};
+        var stack = [];
+        var done_something = false;
+        var headings = $("h2, h3, h4, h5, h6", $(d));
+        headings.each(function(i) {
+            if (this.id == null || this.id == "") {
+                return;
+            }
+            var tag_name = this.tagName.toLowerCase();
+            // Add to table of content
+            while (CONTENT_INDEX_OF[tag_name] < stack.length) {
+                stack.shift();
+            }
+            while (stack.length < CONTENT_INDEX_OF[tag_name]) {
+                var node = stack.length == 0 ? root : $("> :last-child", stack[0]);
+                stack.unshift($("<ul/>").appendTo(node).addClass("list-unstyled"));
+            }
+            var href = "#" + this.id;
+            if (d_href) {
+                href = d_href + href;
+            }
+            var padding = "";
+            for (var i = 0; i < stack.length; i++) {
+                padding += "&nbsp;&nbsp;&nbsp;&nbsp;";
+            }
+            stack[0].append($("<li/>").html(padding).append(
+                $("<a/>", {"href": href}).html($(this).text())
+            ));
+
+            // Add a section link as well
+            if (d == document) {
+                var section_link_anchor = $("<a/>", {"href": "#" + this.id});
+                section_link_anchor.addClass("sectionlink");
+                section_link_anchor.append("\xb6");
+                $(this).append(section_link_anchor);
+            }
+
+            done_something = true;
+        });
+        return done_something;
+    }
+
+    var NODE;
+
+    // Top page table of content
+    NODE = $(".fcm-top-content");
+    if (NODE) {
+        ul_collapse_expand(NODE, IS_MAIN);
+    }
+
+    // Table of content
+    NODE = $(".fcm-page-content");
+    if (NODE) {
+        if (content_gen(NODE)) {
+            ul_collapse_expand(NODE);
+        }
+    }
+
+    // Display version information
+    NODE = $(".fcm-version");
+    if (NODE) {
+        NODE.text("FCM " + FCM.VERSION);
+    }
+});
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/jquery.min.js
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/jquery.min.js	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/jquery.min.js	(revision 5095)
@@ -0,0 +1,5 @@
+/*! jQuery v1.12.0 | (c) jQuery Foundation | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.0",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=la(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=ma(b);function pa(){}pa.prototype=d.filters=d.pseudos,d.setFilters=new pa,g=fa.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=R.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=S.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(Q," ")}),h=h.slice(c.length));for(g in d.filter)!(e=W[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fa.error(a):z(a,i).slice(0)};function qa(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){n.each(b,function(b,c){n.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==n.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return n.each(arguments,function(a,b){var c;while((c=n.inArray(b,f,c))>-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;
+return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=n._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}}),function(){var a;l.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,e;return c=d.getElementsByTagName("body")[0],c&&c.style?(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(d.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(e),a):void 0}}();var T=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,U=new RegExp("^(?:([+-])=|)("+T+")([a-z%]*)$","i"),V=["Top","Right","Bottom","Left"],W=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)};function X(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return n.css(a,b,"")},i=h(),j=c&&c[3]||(n.cssNumber[b]?"":"px"),k=(n.cssNumber[b]||"px"!==j&&+i)&&U.exec(n.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,n.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var Y=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)Y(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav></:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="<textarea>x</textarea>",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:l.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/<tbody/i;function ia(a){Z.test(a.type)&&(a.defaultChecked=a.checked)}function ja(a,b,c,d,e){for(var f,g,h,i,j,k,m,o=a.length,p=ca(b),q=[],r=0;o>r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?"<table>"!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.rnamespace||a.rnamespace.test(g.namespace))&&(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[n.expando])return a;var b,c,e,f=a.type,g=a,h=this.fixHooks[f];h||(this.fixHooks[f]=h=ma.test(f)?this.mouseHooks:la.test(f)?this.keyHooks:{}),e=h.props?this.props.concat(h.props):this.props,a=new n.Event(g),b=e.length;while(b--)c=e[b],a[c]=g[c];return a.target||(a.target=g.srcElement||d),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,h.filter?h.filter(a,g):a},props:"altKey bubbles cancelable ctrlKey currentTarget detail eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,e,f,g=b.button,h=b.fromElement;return null==a.pageX&&null!=b.clientX&&(e=a.target.ownerDocument||d,f=e.documentElement,c=e.body,a.pageX=b.clientX+(f&&f.scrollLeft||c&&c.scrollLeft||0)-(f&&f.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(f&&f.scrollTop||c&&c.scrollTop||0)-(f&&f.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&h&&(a.relatedTarget=h===a.target?b.toElement:h),a.which||void 0===g||(a.which=1&g?1:2&g?3:4&g?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==ra()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===ra()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return n.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c){var d=n.extend(new n.Event,c,{type:a,isSimulated:!0});n.event.trigger(d,null,b),d.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=d.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)}:function(a,b,c){var d="on"+b;a.detachEvent&&("undefined"==typeof a[d]&&(a[d]=null),a.detachEvent(d,c))},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?pa:qa):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={constructor:n.Event,isDefaultPrevented:qa,isPropagationStopped:qa,isImmediatePropagationStopped:qa,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=pa,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=pa,a&&!this.isSimulated&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=pa,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),l.submit||(n.event.special.submit={setup:function(){return n.nodeName(this,"form")?!1:void n.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=n.nodeName(b,"input")||n.nodeName(b,"button")?n.prop(b,"form"):void 0;c&&!n._data(c,"submit")&&(n.event.add(c,"submit._submit",function(a){a._submitBubble=!0}),n._data(c,"submit",!0))})},postDispatch:function(a){a._submitBubble&&(delete a._submitBubble,this.parentNode&&!a.isTrigger&&n.event.simulate("submit",this.parentNode,a))},teardown:function(){return n.nodeName(this,"form")?!1:void n.event.remove(this,"._submit")}}),l.change||(n.event.special.change={setup:function(){return ka.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(n.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._justChanged=!0)}),n.event.add(this,"click._change",function(a){this._justChanged&&!a.isTrigger&&(this._justChanged=!1),n.event.simulate("change",this,a)})),!1):void n.event.add(this,"beforeactivate._change",function(a){var b=a.target;ka.test(b.nodeName)&&!n._data(b,"change")&&(n.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||n.event.simulate("change",this.parentNode,a)}),n._data(b,"change",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return n.event.remove(this,"._change"),!ka.test(this.nodeName)}}),l.focusin||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a))};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=n._data(d,b);e||d.addEventListener(a,c,!0),n._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=n._data(d,b)-1;e?n._data(d,b,e):(d.removeEventListener(a,c,!0),n._removeData(d,b))}}}),n.fn.extend({on:function(a,b,c,d){return sa(this,a,b,c,d)},one:function(a,b,c,d){return sa(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=qa),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ta=/ jQuery\d+="(?:null|\d+)"/g,ua=new RegExp("<(?:"+ba+")[\\s/>]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/<script|<style|<link/i,xa=/checked\s*(?:[^=]|=\s*.checked.)/i,ya=/^true\/(.*)/,za=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Ja[0].contentWindow||Ja[0].contentDocument).document,b.write(),b.close(),c=La(a,b),Ja.detach()),Ka[a]=c),c}var Na=/^margin/,Oa=new RegExp("^("+T+")(?!px)[a-z%]+$","i"),Pa=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e},Qa=d.documentElement;!function(){var b,c,e,f,g,h,i=d.createElement("div"),j=d.createElement("div");if(j.style){j.style.cssText="float:left;opacity:.5",l.opacity="0.5"===j.style.opacity,l.cssFloat=!!j.style.cssFloat,j.style.backgroundClip="content-box",j.cloneNode(!0).style.backgroundClip="",l.clearCloneStyle="content-box"===j.style.backgroundClip,i=d.createElement("div"),i.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",j.innerHTML="",i.appendChild(j),l.boxSizing=""===j.style.boxSizing||""===j.style.MozBoxSizing||""===j.style.WebkitBoxSizing,n.extend(l,{reliableHiddenOffsets:function(){return null==b&&k(),f},boxSizingReliable:function(){return null==b&&k(),e},pixelMarginRight:function(){return null==b&&k(),c},pixelPosition:function(){return null==b&&k(),b},reliableMarginRight:function(){return null==b&&k(),g},reliableMarginLeft:function(){return null==b&&k(),h}});function k(){var k,l,m=d.documentElement;m.appendChild(i),j.style.cssText="-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",b=e=h=!1,c=g=!0,a.getComputedStyle&&(l=a.getComputedStyle(j),b="1%"!==(l||{}).top,h="2px"===(l||{}).marginLeft,e="4px"===(l||{width:"4px"}).width,j.style.marginRight="50%",c="4px"===(l||{marginRight:"4px"}).marginRight,k=j.appendChild(d.createElement("div")),k.style.cssText=j.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",k.style.marginRight=k.style.width="0",j.style.width="1px",g=!parseFloat((a.getComputedStyle(k)||{}).marginRight),j.removeChild(k)),j.style.display="none",f=0===j.getClientRects().length,f&&(j.style.display="",j.innerHTML="<table><tr><td></td><td>t</td></tr></table>",k=j.getElementsByTagName("td"),k[0].style.cssText="margin:0;border:0;padding:0;display:none",f=0===k[0].offsetHeight,f&&(k[0].style.display="",k[1].style.display="none",f=0===k[0].offsetHeight)),m.removeChild(i)}}}();var Ra,Sa,Ta=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ra=function(b){var c=b.ownerDocument.defaultView;return c.opener||(c=a),c.getComputedStyle(b)},Sa=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ra(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),!l.pixelMarginRight()&&Oa.test(g)&&Na.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):Qa.currentStyle&&(Ra=function(a){return a.currentStyle},Sa=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ra(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Oa.test(g)&&!Ta.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Ua(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Va=/alpha\([^)]*\)/i,Wa=/opacity\s*=\s*([^)]*)/i,Xa=/^(none|table(?!-c[ea]).+)/,Ya=new RegExp("^("+T+")(.*)$","i"),Za={position:"absolute",visibility:"hidden",display:"block"},$a={letterSpacing:"0",fontWeight:"400"},_a=["Webkit","O","Moz","ms"],ab=d.createElement("div").style;function bb(a){if(a in ab)return a;var b=a.charAt(0).toUpperCase()+a.slice(1),c=_a.length;while(c--)if(a=_a[c]+b,a in ab)return a}function cb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=n._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&W(d)&&(f[g]=n._data(d,"olddisplay",Ma(d.nodeName)))):(e=W(d),(c&&"none"!==c||!e)&&n._data(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function db(a,b,c){var d=Ya.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function eb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+V[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+V[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+V[f]+"Width",!0,e))):(g+=n.css(a,"padding"+V[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+V[f]+"Width",!0,e)));return g}function fb(b,c,e){var f=!0,g="width"===c?b.offsetWidth:b.offsetHeight,h=Ra(b),i=l.boxSizing&&"border-box"===n.css(b,"boxSizing",!1,h);if(d.msFullscreenElement&&a.top!==a&&b.getClientRects().length&&(g=Math.round(100*b.getBoundingClientRect()[c])),0>=g||null==g){if(g=Sa(b,c,h),(0>g||null==g)&&(g=b.style[c]),Oa.test(g))return g;f=i&&(l.boxSizingReliable()||g===b.style[c]),g=parseFloat(g)||0}return g+eb(b,c,e||(i?"border":"content"),f,h)+"px"}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Sa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":l.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;if(b=n.cssProps[h]||(n.cssProps[h]=bb(h)||h),g=n.cssHooks[b]||n.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=U.exec(c))&&e[1]&&(c=X(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(n.cssNumber[h]?"":"px")),l.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=bb(h)||h),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Sa(a,b,d)),"normal"===f&&b in $a&&(f=$a[b]),""===c||c?(e=parseFloat(f),c===!0||isFinite(e)?e||0:f):f}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?Xa.test(n.css(a,"display"))&&0===a.offsetWidth?Pa(a,Za,function(){return fb(a,b,d)}):fb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ra(a);return db(a,c,d?eb(a,b,d,l.boxSizing&&"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),l.opacity||(n.cssHooks.opacity={get:function(a,b){return Wa.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=n.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===n.trim(f.replace(Va,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Va.test(f)?f.replace(Va,e):f+" "+e)}}),n.cssHooks.marginRight=Ua(l.reliableMarginRight,function(a,b){return b?Pa(a,{display:"inline-block"},Sa,[a,"marginRight"]):void 0}),n.cssHooks.marginLeft=Ua(l.reliableMarginLeft,function(a,b){return b?(parseFloat(Sa(a,"marginLeft"))||(n.contains(a.ownerDocument,a)?a.getBoundingClientRect().left-Pa(a,{
+marginLeft:0},function(){return a.getBoundingClientRect().left}):0))+"px":void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+V[d]+b]=f[d]||f[d-2]||f[0];return e}},Na.test(a)||(n.cssHooks[a+b].set=db)}),n.fn.extend({css:function(a,b){return Y(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=Ra(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return cb(this,!0)},hide:function(){return cb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){W(this)?n(this).show():n(this).hide()})}});function gb(a,b,c,d,e){return new gb.prototype.init(a,b,c,d,e)}n.Tween=gb,gb.prototype={constructor:gb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||n.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=gb.propHooks[this.prop];return a&&a.get?a.get(this):gb.propHooks._default.get(this)},run:function(a){var b,c=gb.propHooks[this.prop];return this.options.duration?this.pos=b=n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):gb.propHooks._default.set(this),this}},gb.prototype.init.prototype=gb.prototype,gb.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[n.cssProps[a.prop]]&&!n.cssHooks[a.prop]?a.elem[a.prop]=a.now:n.style(a.elem,a.prop,a.now+a.unit)}}},gb.propHooks.scrollTop=gb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},n.fx=gb.prototype.init,n.fx.step={};var hb,ib,jb=/^(?:toggle|show|hide)$/,kb=/queueHooks$/;function lb(){return a.setTimeout(function(){hb=void 0}),hb=n.now()}function mb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=V[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function nb(a,b,c){for(var d,e=(qb.tweeners[b]||[]).concat(qb.tweeners["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ob(a,b,c){var d,e,f,g,h,i,j,k,m=this,o={},p=a.style,q=a.nodeType&&W(a),r=n._data(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,m.always(function(){m.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=n.css(a,"display"),k="none"===j?n._data(a,"olddisplay")||Ma(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(l.inlineBlockNeedsLayout&&"inline"!==Ma(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",l.shrinkWrapBlocks()||m.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],jb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(o))"inline"===("none"===j?Ma(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=n._data(a,"fxshow",{}),f&&(r.hidden=!q),q?n(a).show():m.done(function(){n(a).hide()}),m.done(function(){var b;n._removeData(a,"fxshow");for(b in o)n.style(a,b,o[b])});for(d in o)g=nb(q?r[d]:0,d,m),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function pb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function qb(a,b,c){var d,e,f=0,g=qb.prefilters.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=hb||lb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{},easing:n.easing._default},c),originalProperties:b,originalOptions:c,startTime:hb||lb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(pb(k,j.opts.specialEasing);g>f;f++)if(d=qb.prefilters[f].call(j,a,k,j.opts))return n.isFunction(d.stop)&&(n._queueHooks(j.elem,j.opts.queue).stop=n.proxy(d.stop,d)),d;return n.map(k,nb,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(qb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return X(c.elem,a,U.exec(b),c),c}]},tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.match(G);for(var c,d=0,e=a.length;e>d;d++)c=a[d],qb.tweeners[c]=qb.tweeners[c]||[],qb.tweeners[c].unshift(b)},prefilters:[ob],prefilter:function(a,b){b?qb.prefilters.unshift(a):qb.prefilters.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(W).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=qb(this,n.extend({},a),f);(e||n._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=n._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&kb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=n._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(mb(b,!0),a,d,e)}}),n.each({slideDown:mb("show"),slideUp:mb("hide"),slideToggle:mb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=n.timers,c=0;for(hb=n.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||n.fx.stop(),hb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){ib||(ib=a.setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){a.clearInterval(ib),ib=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(b,c){return b=n.fx?n.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a,b=d.createElement("input"),c=d.createElement("div"),e=d.createElement("select"),f=e.appendChild(d.createElement("option"));c=d.createElement("div"),c.setAttribute("className","t"),c.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",a=c.getElementsByTagName("a")[0],b.setAttribute("type","checkbox"),c.appendChild(b),a=c.getElementsByTagName("a")[0],a.style.cssText="top:1px",l.getSetAttribute="t"!==c.className,l.style=/top/.test(a.getAttribute("style")),l.hrefNormalized="/a"===a.getAttribute("href"),l.checkOn=!!b.value,l.optSelected=f.selected,l.enctype=!!d.createElement("form").enctype,e.disabled=!0,l.optDisabled=!f.disabled,b=d.createElement("input"),b.setAttribute("value",""),l.input=""===b.getAttribute("value"),b.value="t",b.setAttribute("type","radio"),l.radioValue="t"===b.value}();var rb=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],(c.selected||i===e)&&(l.optDisabled?!c.disabled:null===c.getAttribute("disabled"))&&(!c.parentNode.disabled||!n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)if(d=e[g],n.inArray(n.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>-1:void 0}},l.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb,tb,ub=n.expr.attrHandle,vb=/^(?:checked|selected)$/i,wb=l.getSetAttribute,xb=l.input;n.fn.extend({attr:function(a,b){return Y(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),e=n.attrHooks[b]||(n.expr.match.bool.test(b)?tb:sb)),void 0!==c?null===c?void n.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=n.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!l.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(G);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)?xb&&wb||!vb.test(c)?a[d]=!1:a[n.camelCase("default-"+c)]=a[d]=!1:n.attr(a,c,""),a.removeAttribute(wb?c:d)}}),tb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):xb&&wb||!vb.test(c)?a.setAttribute(!wb&&n.propFix[c]||c,c):a[n.camelCase("default-"+c)]=a[c]=!0,c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ub[b]||n.find.attr;xb&&wb||!vb.test(b)?ub[b]=function(a,b,d){var e,f;return d||(f=ub[b],ub[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,ub[b]=f),e}:ub[b]=function(a,b,c){return c?void 0:a[n.camelCase("default-"+b)]?b.toLowerCase():null}}),xb&&wb||(n.attrHooks.value={set:function(a,b,c){return n.nodeName(a,"input")?void(a.defaultValue=b):sb&&sb.set(a,b,c)}}),wb||(sb={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},ub.id=ub.name=ub.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},n.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:sb.set},n.attrHooks.contenteditable={set:function(a,b,c){sb.set(a,""===b?!1:b,c)}},n.each(["width","height"],function(a,b){n.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),l.style||(n.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var yb=/^(?:input|select|textarea|button|object)$/i,zb=/^(?:a|area)$/i;n.fn.extend({prop:function(a,b){return Y(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return a=n.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),n.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&n.isXMLDoc(a)||(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=n.find.attr(a,"tabindex");return b?parseInt(b,10):yb.test(a.nodeName)||zb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),l.hrefNormalized||n.each(["href","src"],function(a,b){n.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),l.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this}),l.enctype||(n.propFix.enctype="encoding");var Ab=/[\t\r\n\f]/g;function Bb(a){return n.attr(a,"class")||""}n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,Bb(this)))});if("string"==typeof a&&a){b=a.match(G)||[];while(c=this[i++])if(e=Bb(c),d=1===c.nodeType&&(" "+e+" ").replace(Ab," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=n.trim(d),e!==h&&n.attr(c,"class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,Bb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(G)||[];while(c=this[i++])if(e=Bb(c),d=1===c.nodeType&&(" "+e+" ").replace(Ab," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=n.trim(d),e!==h&&n.attr(c,"class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):n.isFunction(a)?this.each(function(c){n(this).toggleClass(a.call(this,c,Bb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=n(this),f=a.match(G)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(void 0===a||"boolean"===c)&&(b=Bb(this),b&&n._data(this,"__className__",b),n.attr(this,"class",b||a===!1?"":n._data(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+Bb(c)+" ").replace(Ab," ").indexOf(b)>-1)return!0;return!1}}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Cb=a.location,Db=n.now(),Eb=/\?/,Fb=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;n.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=n.trim(b+"");return e&&!n.trim(e.replace(Fb,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():n.error("Invalid JSON: "+b)},n.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new a.DOMParser,c=d.parseFromString(b,"text/xml")):(c=new a.ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||n.error("Invalid XML: "+b),c};var Gb=/#.*$/,Hb=/([?&])_=[^&]*/,Ib=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Jb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Kb=/^(?:GET|HEAD)$/,Lb=/^\/\//,Mb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Nb={},Ob={},Pb="*/".concat("*"),Qb=Cb.href,Rb=Mb.exec(Qb.toLowerCase())||[];function Sb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(G)||[];if(n.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Tb(a,b,c,d){var e={},f=a===Ob;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ub(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&n.extend(!0,a,c),a}function Vb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Wb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Qb,type:"GET",isLocal:Jb.test(Rb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Pb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ub(Ub(a,n.ajaxSettings),b):Ub(n.ajaxSettings,a)},ajaxPrefilter:Sb(Nb),ajaxTransport:Sb(Ob),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var d,e,f,g,h,i,j,k,l=n.ajaxSetup({},c),m=l.context||l,o=l.context&&(m.nodeType||m.jquery)?n(m):n.event,p=n.Deferred(),q=n.Callbacks("once memory"),r=l.statusCode||{},s={},t={},u=0,v="canceled",w={readyState:0,getResponseHeader:function(a){var b;if(2===u){if(!k){k={};while(b=Ib.exec(g))k[b[1].toLowerCase()]=b[2]}b=k[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===u?g:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return u||(a=t[c]=t[c]||a,s[a]=b),this},overrideMimeType:function(a){return u||(l.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>u)for(b in a)r[b]=[r[b],a[b]];else w.always(a[w.status]);return this},abort:function(a){var b=a||v;return j&&j.abort(b),y(0,b),this}};if(p.promise(w).complete=q.add,w.success=w.done,w.error=w.fail,l.url=((b||l.url||Qb)+"").replace(Gb,"").replace(Lb,Rb[1]+"//"),l.type=c.method||c.type||l.method||l.type,l.dataTypes=n.trim(l.dataType||"*").toLowerCase().match(G)||[""],null==l.crossDomain&&(d=Mb.exec(l.url.toLowerCase()),l.crossDomain=!(!d||d[1]===Rb[1]&&d[2]===Rb[2]&&(d[3]||("http:"===d[1]?"80":"443"))===(Rb[3]||("http:"===Rb[1]?"80":"443")))),l.data&&l.processData&&"string"!=typeof l.data&&(l.data=n.param(l.data,l.traditional)),Tb(Nb,l,c,w),2===u)return w;i=n.event&&l.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),l.type=l.type.toUpperCase(),l.hasContent=!Kb.test(l.type),f=l.url,l.hasContent||(l.data&&(f=l.url+=(Eb.test(f)?"&":"?")+l.data,delete l.data),l.cache===!1&&(l.url=Hb.test(f)?f.replace(Hb,"$1_="+Db++):f+(Eb.test(f)?"&":"?")+"_="+Db++)),l.ifModified&&(n.lastModified[f]&&w.setRequestHeader("If-Modified-Since",n.lastModified[f]),n.etag[f]&&w.setRequestHeader("If-None-Match",n.etag[f])),(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&w.setRequestHeader("Content-Type",l.contentType),w.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+("*"!==l.dataTypes[0]?", "+Pb+"; q=0.01":""):l.accepts["*"]);for(e in l.headers)w.setRequestHeader(e,l.headers[e]);if(l.beforeSend&&(l.beforeSend.call(m,w,l)===!1||2===u))return w.abort();v="abort";for(e in{success:1,error:1,complete:1})w[e](l[e]);if(j=Tb(Ob,l,c,w)){if(w.readyState=1,i&&o.trigger("ajaxSend",[w,l]),2===u)return w;l.async&&l.timeout>0&&(h=a.setTimeout(function(){w.abort("timeout")},l.timeout));try{u=1,j.send(s,y)}catch(x){if(!(2>u))throw x;y(-1,x)}}else y(-1,"No Transport");function y(b,c,d,e){var k,s,t,v,x,y=c;2!==u&&(u=2,h&&a.clearTimeout(h),j=void 0,g=e||"",w.readyState=b>0?4:0,k=b>=200&&300>b||304===b,d&&(v=Vb(l,w,d)),v=Wb(l,v,w,k),k?(l.ifModified&&(x=w.getResponseHeader("Last-Modified"),x&&(n.lastModified[f]=x),x=w.getResponseHeader("etag"),x&&(n.etag[f]=x)),204===b||"HEAD"===l.type?y="nocontent":304===b?y="notmodified":(y=v.state,s=v.data,t=v.error,k=!t)):(t=y,(b||!y)&&(y="error",0>b&&(b=0))),w.status=b,w.statusText=(c||y)+"",k?p.resolveWith(m,[s,y,w]):p.rejectWith(m,[w,y,t]),w.statusCode(r),r=void 0,i&&o.trigger(k?"ajaxSuccess":"ajaxError",[w,l,k?s:t]),q.fireWith(m,[w,y]),i&&(o.trigger("ajaxComplete",[w,l]),--n.active||n.event.trigger("ajaxStop")))}return w},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax(n.extend({url:a,type:b,dataType:e,data:c,success:d},n.isPlainObject(a)&&a))}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){if(n.isFunction(a))return this.each(function(b){n(this).wrapAll(a.call(this,b))});if(this[0]){var b=n(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return n.isFunction(a)?this.each(function(b){n(this).wrapInner(a.call(this,b))}):this.each(function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}});function Xb(a){return a.style&&a.style.display||n.css(a,"display")}function Yb(a){while(a&&1===a.nodeType){if("none"===Xb(a)||"hidden"===a.type)return!0;a=a.parentNode}return!1}n.expr.filters.hidden=function(a){return l.reliableHiddenOffsets()?a.offsetWidth<=0&&a.offsetHeight<=0&&!a.getClientRects().length:Yb(a)},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var Zb=/%20/g,$b=/\[\]$/,_b=/\r?\n/g,ac=/^(?:submit|button|image|reset|file)$/i,bc=/^(?:input|select|textarea|keygen)/i;function cc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||$b.test(a)?d(a,e):cc(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)cc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)cc(c,a[c],b,e);return d.join("&").replace(Zb,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&bc.test(this.nodeName)&&!ac.test(a)&&(this.checked||!Z.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(_b,"\r\n")}}):{name:b.name,value:c.replace(_b,"\r\n")}}).get()}}),n.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return this.isLocal?hc():d.documentMode>8?gc():/^(get|post|head|put|delete|options)$/i.test(this.type)&&gc()||hc()}:gc;var dc=0,ec={},fc=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in ec)ec[a](void 0,!0)}),l.cors=!!fc&&"withCredentials"in fc,fc=l.ajax=!!fc,fc&&n.ajaxTransport(function(b){if(!b.crossDomain||l.cors){var c;return{send:function(d,e){var f,g=b.xhr(),h=++dc;if(g.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(f in b.xhrFields)g[f]=b.xhrFields[f];b.mimeType&&g.overrideMimeType&&g.overrideMimeType(b.mimeType),b.crossDomain||d["X-Requested-With"]||(d["X-Requested-With"]="XMLHttpRequest");for(f in d)void 0!==d[f]&&g.setRequestHeader(f,d[f]+"");g.send(b.hasContent&&b.data||null),c=function(a,d){var f,i,j;if(c&&(d||4===g.readyState))if(delete ec[h],c=void 0,g.onreadystatechange=n.noop,d)4!==g.readyState&&g.abort();else{j={},f=g.status,"string"==typeof g.responseText&&(j.text=g.responseText);try{i=g.statusText}catch(k){i=""}f||!b.isLocal||b.crossDomain?1223===f&&(f=204):f=j.text?200:404}j&&e(f,i,j,g.getAllResponseHeaders())},b.async?4===g.readyState?a.setTimeout(c):g.onreadystatechange=ec[h]=c:c()},abort:function(){c&&c(void 0,!0)}}}});function gc(){try{return new a.XMLHttpRequest}catch(b){}}function hc(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}n.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=d.head||n("head")[0]||d.documentElement;return{send:function(e,f){b=d.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||f(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var ic=[],jc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=ic.pop()||n.expando+"_"+Db++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(jc.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&jc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(jc,"$1"+e):b.jsonp!==!1&&(b.url+=(Eb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?n(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,ic.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),l.createHTMLDocument=function(){if(!d.implementation.createHTMLDocument)return!1;var a=d.implementation.createHTMLDocument("");return a.body.innerHTML="<form></form><form></form>",2===a.body.childNodes.length}(),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||(l.createHTMLDocument?d.implementation.createHTMLDocument(""):d);var e=x.exec(a),f=!c&&[];return e?[b.createElement(e[1])]:(e=ja([a],b,f),f&&f.length&&n(f).remove(),n.merge([],e.childNodes))};var kc=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&kc)return kc.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=n.trim(a.slice(h,a.length)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(g,f||[a.responseText,b,a])})}),this},n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};function lc(a){return n.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&n.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,n.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,n.contains(b,e)?("undefined"!=typeof e.getBoundingClientRect&&(d=e.getBoundingClientRect()),c=lc(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===n.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(c=a.offset()),c.top+=n.css(a[0],"borderTopWidth",!0)-a.scrollTop(),c.left+=n.css(a[0],"borderLeftWidth",!0)-a.scrollLeft()),{top:b.top-c.top-n.css(d,"marginTop",!0),left:b.left-c.left-n.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Qa})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);n.fn[a]=function(d){return Y(this,function(a,d,e){var f=lc(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?n(f).scrollLeft():e,c?e:n(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),n.each(["top","left"],function(a,b){
+n.cssHooks[b]=Ua(l.pixelPosition,function(a,c){return c?(c=Sa(a,b),Oa.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return Y(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var mc=a.jQuery,nc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=nc),b&&a.jQuery===n&&(a.jQuery=mc),n},b||(a.jQuery=a.$=n),n});
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/moment.min.js
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/moment.min.js	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/etc/moment.min.js	(revision 5095)
@@ -0,0 +1,6 @@
+// moment.js
+// version : 2.1.0
+// author : Tim Wood
+// license : MIT
+// momentjs.com
+!function(t){function e(t,e){return function(n){return u(t.call(this,n),e)}}function n(t,e){return function(n){return this.lang().ordinal(t.call(this,n),e)}}function s(){}function i(t){a(this,t)}function r(t){var e=t.years||t.year||t.y||0,n=t.months||t.month||t.M||0,s=t.weeks||t.week||t.w||0,i=t.days||t.day||t.d||0,r=t.hours||t.hour||t.h||0,a=t.minutes||t.minute||t.m||0,o=t.seconds||t.second||t.s||0,u=t.milliseconds||t.millisecond||t.ms||0;this._input=t,this._milliseconds=u+1e3*o+6e4*a+36e5*r,this._days=i+7*s,this._months=n+12*e,this._data={},this._bubble()}function a(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}function o(t){return 0>t?Math.ceil(t):Math.floor(t)}function u(t,e){for(var n=t+"";n.length<e;)n="0"+n;return n}function h(t,e,n,s){var i,r,a=e._milliseconds,o=e._days,u=e._months;a&&t._d.setTime(+t._d+a*n),(o||u)&&(i=t.minute(),r=t.hour()),o&&t.date(t.date()+o*n),u&&t.month(t.month()+u*n),a&&!s&&H.updateOffset(t),(o||u)&&(t.minute(i),t.hour(r))}function d(t){return"[object Array]"===Object.prototype.toString.call(t)}function c(t,e){var n,s=Math.min(t.length,e.length),i=Math.abs(t.length-e.length),r=0;for(n=0;s>n;n++)~~t[n]!==~~e[n]&&r++;return r+i}function f(t){return t?ie[t]||t.toLowerCase().replace(/(.)s$/,"$1"):t}function l(t,e){return e.abbr=t,x[t]||(x[t]=new s),x[t].set(e),x[t]}function _(t){if(!t)return H.fn._lang;if(!x[t]&&A)try{require("./lang/"+t)}catch(e){return H.fn._lang}return x[t]}function m(t){return t.match(/\[.*\]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function y(t){var e,n,s=t.match(E);for(e=0,n=s.length;n>e;e++)s[e]=ue[s[e]]?ue[s[e]]:m(s[e]);return function(i){var r="";for(e=0;n>e;e++)r+=s[e]instanceof Function?s[e].call(i,t):s[e];return r}}function M(t,e){function n(e){return t.lang().longDateFormat(e)||e}for(var s=5;s--&&N.test(e);)e=e.replace(N,n);return re[e]||(re[e]=y(e)),re[e](t)}function g(t,e){switch(t){case"DDDD":return V;case"YYYY":return X;case"YYYYY":return $;case"S":case"SS":case"SSS":case"DDD":return I;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return R;case"a":case"A":return _(e._l)._meridiemParse;case"X":return B;case"Z":case"ZZ":return j;case"T":return q;case"MM":case"DD":case"YY":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":return J;default:return new RegExp(t.replace("\\",""))}}function p(t){var e=(j.exec(t)||[])[0],n=(e+"").match(ee)||["-",0,0],s=+(60*n[1])+~~n[2];return"+"===n[0]?-s:s}function D(t,e,n){var s,i=n._a;switch(t){case"M":case"MM":i[1]=null==e?0:~~e-1;break;case"MMM":case"MMMM":s=_(n._l).monthsParse(e),null!=s?i[1]=s:n._isValid=!1;break;case"D":case"DD":case"DDD":case"DDDD":null!=e&&(i[2]=~~e);break;case"YY":i[0]=~~e+(~~e>68?1900:2e3);break;case"YYYY":case"YYYYY":i[0]=~~e;break;case"a":case"A":n._isPm=_(n._l).isPM(e);break;case"H":case"HH":case"h":case"hh":i[3]=~~e;break;case"m":case"mm":i[4]=~~e;break;case"s":case"ss":i[5]=~~e;break;case"S":case"SS":case"SSS":i[6]=~~(1e3*("0."+e));break;case"X":n._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":n._useUTC=!0,n._tzm=p(e)}null==e&&(n._isValid=!1)}function Y(t){var e,n,s=[];if(!t._d){for(e=0;7>e;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];s[3]+=~~((t._tzm||0)/60),s[4]+=~~((t._tzm||0)%60),n=new Date(0),t._useUTC?(n.setUTCFullYear(s[0],s[1],s[2]),n.setUTCHours(s[3],s[4],s[5],s[6])):(n.setFullYear(s[0],s[1],s[2]),n.setHours(s[3],s[4],s[5],s[6])),t._d=n}}function w(t){var e,n,s=t._f.match(E),i=t._i;for(t._a=[],e=0;e<s.length;e++)n=(g(s[e],t).exec(i)||[])[0],n&&(i=i.slice(i.indexOf(n)+n.length)),ue[s[e]]&&D(s[e],n,t);i&&(t._il=i),t._isPm&&t._a[3]<12&&(t._a[3]+=12),t._isPm===!1&&12===t._a[3]&&(t._a[3]=0),Y(t)}function k(t){var e,n,s,r,o,u=99;for(r=0;r<t._f.length;r++)e=a({},t),e._f=t._f[r],w(e),n=new i(e),o=c(e._a,n.toArray()),n._il&&(o+=n._il.length),u>o&&(u=o,s=n);a(t,s)}function v(t){var e,n=t._i,s=K.exec(n);if(s){for(t._f="YYYY-MM-DD"+(s[2]||" "),e=0;4>e;e++)if(te[e][1].exec(n)){t._f+=te[e][0];break}j.exec(n)&&(t._f+=" Z"),w(t)}else t._d=new Date(n)}function T(e){var n=e._i,s=G.exec(n);n===t?e._d=new Date:s?e._d=new Date(+s[1]):"string"==typeof n?v(e):d(n)?(e._a=n.slice(0),Y(e)):e._d=n instanceof Date?new Date(+n):new Date(n)}function b(t,e,n,s,i){return i.relativeTime(e||1,!!n,t,s)}function S(t,e,n){var s=W(Math.abs(t)/1e3),i=W(s/60),r=W(i/60),a=W(r/24),o=W(a/365),u=45>s&&["s",s]||1===i&&["m"]||45>i&&["mm",i]||1===r&&["h"]||22>r&&["hh",r]||1===a&&["d"]||25>=a&&["dd",a]||45>=a&&["M"]||345>a&&["MM",W(a/30)]||1===o&&["y"]||["yy",o];return u[2]=e,u[3]=t>0,u[4]=n,b.apply({},u)}function F(t,e,n){var s,i=n-e,r=n-t.day();return r>i&&(r-=7),i-7>r&&(r+=7),s=H(t).add("d",r),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function O(t){var e=t._i,n=t._f;return null===e||""===e?null:("string"==typeof e&&(t._i=e=_().preparse(e)),H.isMoment(e)?(t=a({},e),t._d=new Date(+e._d)):n?d(n)?k(t):w(t):T(t),new i(t))}function z(t,e){H.fn[t]=H.fn[t+"s"]=function(t){var n=this._isUTC?"UTC":"";return null!=t?(this._d["set"+n+e](t),H.updateOffset(this),this):this._d["get"+n+e]()}}function C(t){H.duration.fn[t]=function(){return this._data[t]}}function L(t,e){H.duration.fn["as"+t]=function(){return+this/e}}for(var H,P,U="2.1.0",W=Math.round,x={},A="undefined"!=typeof module&&module.exports,G=/^\/?Date\((\-?\d+)/i,Z=/(\-)?(\d*)?\.?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,E=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,N=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,J=/\d\d?/,I=/\d{1,3}/,V=/\d{3}/,X=/\d{1,4}/,$=/[+\-]?\d{1,6}/,R=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,j=/Z|[\+\-]\d\d:?\d\d/i,q=/T/i,B=/[\+\-]?\d+(\.\d{1,3})?/,K=/^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,Q="YYYY-MM-DDTHH:mm:ssZ",te=[["HH:mm:ss.S",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],ee=/([\+\-]|\d\d)/gi,ne="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),se={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},ie={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",w:"week",M:"month",y:"year"},re={},ae="DDD w W M D d".split(" "),oe="M D H h m s w W".split(" "),ue={M:function(){return this.month()+1},MMM:function(t){return this.lang().monthsShort(this,t)},MMMM:function(t){return this.lang().months(this,t)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(t){return this.lang().weekdaysMin(this,t)},ddd:function(t){return this.lang().weekdaysShort(this,t)},dddd:function(t){return this.lang().weekdays(this,t)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return u(this.year()%100,2)},YYYY:function(){return u(this.year(),4)},YYYYY:function(){return u(this.year(),5)},gg:function(){return u(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return u(this.weekYear(),5)},GG:function(){return u(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return u(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return~~(this.milliseconds()/100)},SS:function(){return u(~~(this.milliseconds()/10),2)},SSS:function(){return u(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+u(~~(t/60),2)+":"+u(~~t%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+u(~~(10*t/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}};ae.length;)P=ae.pop(),ue[P+"o"]=n(ue[P],P);for(;oe.length;)P=oe.pop(),ue[P+P]=e(ue[P],2);for(ue.DDDD=e(ue.DDD,3),s.prototype={set:function(t){var e,n;for(n in t)e=t[n],"function"==typeof e?this[n]=e:this["_"+n]=e},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t){var e,n,s;for(this._monthsParse||(this._monthsParse=[]),e=0;12>e;e++)if(this._monthsParse[e]||(n=H([2e3,e]),s="^"+this.months(n,"")+"|^"+this.monthsShort(n,""),this._monthsParse[e]=new RegExp(s.replace(".",""),"i")),this._monthsParse[e].test(t))return e},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,n,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(n=H([2e3,1]).day(e),s="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase()[0]},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e){var n=this._calendar[t];return"function"==typeof n?n.apply(e):n},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,n,s){var i=this._relativeTime[n];return"function"==typeof i?i(t,e,n,s):i.replace(/%d/i,t)},pastFuture:function(t,e){var n=this._relativeTime[t>0?"future":"past"];return"function"==typeof n?n(e):n.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",preparse:function(t){return t},postformat:function(t){return t},week:function(t){return F(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6}},H=function(t,e,n){return O({_i:t,_f:e,_l:n,_isUTC:!1})},H.utc=function(t,e,n){return O({_useUTC:!0,_isUTC:!0,_l:n,_i:t,_f:e})},H.unix=function(t){return H(1e3*t)},H.duration=function(t,e){var n,s,i=H.isDuration(t),a="number"==typeof t,o=i?t._input:a?{}:t,u=Z.exec(t);return a?e?o[e]=t:o.milliseconds=t:u&&(n="-"===u[1]?-1:1,o={y:0,d:~~u[2]*n,h:~~u[3]*n,m:~~u[4]*n,s:~~u[5]*n,ms:~~u[6]*n}),s=new r(o),i&&t.hasOwnProperty("_lang")&&(s._lang=t._lang),s},H.version=U,H.defaultFormat=Q,H.updateOffset=function(){},H.lang=function(t,e){return t?(e?l(t,e):x[t]||_(t),H.duration.fn._lang=H.fn._lang=_(t),void 0):H.fn._lang._abbr},H.langData=function(t){return t&&t._lang&&t._lang._abbr&&(t=t._lang._abbr),_(t)},H.isMoment=function(t){return t instanceof i},H.isDuration=function(t){return t instanceof r},H.fn=i.prototype={clone:function(){return H(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return M(H(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var t=this;return[t.year(),t.month(),t.date(),t.hours(),t.minutes(),t.seconds(),t.milliseconds()]},isValid:function(){return null==this._isValid&&(this._isValid=this._a?!c(this._a,(this._isUTC?H.utc(this._a):H(this._a)).toArray()):!isNaN(this._d.getTime())),!!this._isValid},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(t){var e=M(this,t||H.defaultFormat);return this.lang().postformat(e)},add:function(t,e){var n;return n="string"==typeof t?H.duration(+e,t):H.duration(t,e),h(this,n,1),this},subtract:function(t,e){var n;return n="string"==typeof t?H.duration(+e,t):H.duration(t,e),h(this,n,-1),this},diff:function(t,e,n){var s,i,r=this._isUTC?H(t).zone(this._offset||0):H(t).local(),a=6e4*(this.zone()-r.zone());return e=f(e),"year"===e||"month"===e?(s=432e5*(this.daysInMonth()+r.daysInMonth()),i=12*(this.year()-r.year())+(this.month()-r.month()),i+=(this-H(this).startOf("month")-(r-H(r).startOf("month")))/s,i-=6e4*(this.zone()-H(this).startOf("month").zone()-(r.zone()-H(r).startOf("month").zone()))/s,"year"===e&&(i/=12)):(s=this-r,i="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-a)/864e5:"week"===e?(s-a)/6048e5:s),n?i:o(i)},from:function(t,e){return H.duration(this.diff(t)).lang(this.lang()._abbr).humanize(!e)},fromNow:function(t){return this.from(H(),t)},calendar:function(){var t=this.diff(H().startOf("day"),"days",!0),e=-6>t?"sameElse":-1>t?"lastWeek":0>t?"lastDay":1>t?"sameDay":2>t?"nextDay":7>t?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){var t=this.year();return 0===t%4&&0!==t%100||0===t%400},isDST:function(){return this.zone()<this.clone().month(0).zone()||this.zone()<this.clone().month(5).zone()},day:function(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?"string"==typeof t&&(t=this.lang().weekdaysParse(t),"number"!=typeof t)?this:this.add({d:t-e}):e},month:function(t){var e,n=this._isUTC?"UTC":"";return null!=t?"string"==typeof t&&(t=this.lang().monthsParse(t),"number"!=typeof t)?this:(e=this.date(),this.date(1),this._d["set"+n+"Month"](t),this.date(Math.min(e,this.daysInMonth())),H.updateOffset(this),this):this._d["get"+n+"Month"]()},startOf:function(t){switch(t=f(t)){case"year":this.month(0);case"month":this.date(1);case"week":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===t&&this.weekday(0),this},endOf:function(t){return this.startOf(t).add(t,1).subtract("ms",1)},isAfter:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)>+H(t).startOf(e)},isBefore:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)<+H(t).startOf(e)},isSame:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)===+H(t).startOf(e)},min:function(t){return t=H.apply(null,arguments),this>t?this:t},max:function(t){return t=H.apply(null,arguments),t>this?this:t},zone:function(t){var e=this._offset||0;return null==t?this._isUTC?e:this._d.getTimezoneOffset():("string"==typeof t&&(t=p(t)),Math.abs(t)<16&&(t=60*t),this._offset=t,this._isUTC=!0,e!==t&&h(this,H.duration(e-t,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},daysInMonth:function(){return H.utc([this.year(),this.month()+1,0]).date()},dayOfYear:function(t){var e=W((H(this).startOf("day")-H(this).startOf("year"))/864e5)+1;return null==t?e:this.add("d",t-e)},weekYear:function(t){var e=F(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==t?e:this.add("y",t-e)},isoWeekYear:function(t){var e=F(this,1,4).year;return null==t?e:this.add("y",t-e)},week:function(t){var e=this.lang().week(this);return null==t?e:this.add("d",7*(t-e))},isoWeek:function(t){var e=F(this,1,4).week;return null==t?e:this.add("d",7*(t-e))},weekday:function(t){var e=(this._d.getDay()+7-this.lang()._week.dow)%7;return null==t?e:this.add("d",t-e)},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},lang:function(e){return e===t?this._lang:(this._lang=_(e),this)}},P=0;P<ne.length;P++)z(ne[P].toLowerCase().replace(/s$/,""),ne[P]);z("year","FullYear"),H.fn.days=H.fn.day,H.fn.months=H.fn.month,H.fn.weeks=H.fn.week,H.fn.isoWeeks=H.fn.isoWeek,H.fn.toJSON=H.fn.toISOString,H.duration.fn=r.prototype={_bubble:function(){var t,e,n,s,i=this._milliseconds,r=this._days,a=this._months,u=this._data;u.milliseconds=i%1e3,t=o(i/1e3),u.seconds=t%60,e=o(t/60),u.minutes=e%60,n=o(e/60),u.hours=n%24,r+=o(n/24),u.days=r%30,a+=o(r/30),u.months=a%12,s=o(a/12),u.years=s},weeks:function(){return o(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+2592e6*(this._months%12)+31536e6*~~(this._months/12)},humanize:function(t){var e=+this,n=S(e,!t,this.lang());return t&&(n=this.lang().pastFuture(e,n)),this.lang().postformat(n)},add:function(t,e){var n=H.duration(t,e);return this._milliseconds+=n._milliseconds,this._days+=n._days,this._months+=n._months,this._bubble(),this},subtract:function(t,e){var n=H.duration(t,e);return this._milliseconds-=n._milliseconds,this._days-=n._days,this._months-=n._months,this._bubble(),this},get:function(t){return t=f(t),this[t.toLowerCase()+"s"]()},as:function(t){return t=f(t),this["as"+t.charAt(0).toUpperCase()+t.slice(1)+"s"]()},lang:H.fn.lang};for(P in se)se.hasOwnProperty(P)&&(L(P,se[P]),C(P.toLowerCase()));L("Weeks",6048e5),H.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},H.lang("en",{ordinal:function(t){var e=t%10,n=1===~~(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+n}}),A&&(module.exports=H),"undefined"==typeof ender&&(this.moment=H),"function"==typeof define&&define.amd&&define("moment",[],function(){return H})}.call(this);
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/index.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/index.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/index.html	(revision 5095)
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM Documentation</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="etc/fcm-icon.png" type="image/png" />
+  <link href="etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="active navbar-brand" href="#">
+          <span class="fcm-version">FCM</span>
+        </a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="installation/">Installation</a></li>
+
+          <li><a href="user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+  <div class="jumbotron">
+    <div class="container-fluid">
+      <div class="row">
+        <div class="col-md-12">
+          <h1>FCM</h1>
+        </div>
+      </div>
+    
+      <div class="row">
+        <div class="col-md-6 well">
+          <h2>Build</h2>
+
+          <p>A powerful build system for modern Fortran software
+          applications.</p>
+        </div>
+
+        <div class="col-md-6 well">
+          <h2>Version Control</h2>
+
+          <p>Wrappers to the Subversion version control system,
+          usage conventions and processes for scientific software
+          development.</p>
+        </div>
+      </div>
+
+      <div class="row text-center">
+        <div class="col-md-4">
+          <p><a class="btn btn-primary btn-block" href=
+          "https://github.com/metomi/fcm/releases/">
+          <span class="glyphicon glyphicon-download"
+          aria-hidden="true"></span>
+          Get FCM from Github</a></p>
+        </div>
+        <div class="col-md-4">
+          <p><a class="btn btn-primary btn-block" href="installation/">
+          <span class="glyphicon glyphicon-wrench"
+          aria-hidden="true"></span>
+          Read the FCM Installation Guide</a></p>
+        </div>
+        <div class="col-md-4">
+          <p><a class="btn btn-primary btn-block" href="user_guide/">
+          <span class="glyphicon glyphicon-user"
+          aria-hidden="true"></span>
+          Read the FCM User Guide</a></p>
+        </div>
+      </div>
+    </div>
+  </div>
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="etc/jquery.min.js"></script>
+  <script type="text/javascript" src="etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="etc/fcm.js"></script>
+  <script type="text/javascript" src="etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/installation/index.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/installation/index.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/installation/index.html	(revision 5095)
@@ -0,0 +1,360 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM: Installation</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" type="text/css"
+  rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" type="text/css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li class="active"><a href="#">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM: Installation</h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+  <h2 id="requirements">System Requirements</h2>
+
+  <p>FCM is intended to run on a Unix/Linux system. It is known to work on
+  recent popular Linux distributions such as RHEL-6. See
+  <a href="https://github.com/metomi/metomi-vms">metomi-vms @ Github</a> for
+  information.</p>
+
+  <p>FCM releases can be downloaded from <a href=
+  "https://github.com/metomi/fcm/releases">Github</a>. Download the tar.gz file
+  and un-pack it into an appropriate location on your system. Add the
+  <samp>bin/</samp> directory into your <var>PATH</var> environment variable.
+  Enable the configuration files in <samp>etc/fcm/</samp> directory and edit
+  them to meet the your requirements. Once you have done this you should now
+  have access to the FCM user utilities, assuming that you have met the
+  requirements described below:</p>
+
+  <dl class="well">
+    <dt><a href="http://www.perl.org/">Perl</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> <code>fcm</code>.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 5.10.1</p>
+
+      <p><dfn>remark:</dfn> We assume that all <em>core</em> Perl modules (as
+      documented by <a href="http://perldoc.perl.org/">perldoc.perl.org</a>) of
+      the <em>known to work versions</em> are installed on your system. (N.B. On
+      platforms based on RHEL, you may need the <em>perl-core</em> RPM instead
+      of just <em>perl</em>, see <a href=
+      "http://www.nntp.perl.org/group/perl.perl5.porters/2009/08/msg149891.html">this
+      discussion</a>.)</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/dist/Config-IniFiles/">Config::IniFiles</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the admin commands.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 2.72.</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/dist/DBD-SQLite/">DBD::SQLite</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the admin commands.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 1.29.</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/dist/Alien-SVN/">SVN::Client</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the admin commands.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 1.8.5 (RPM from <a href=
+      "http://opensource.wandisco.com/rhel/6/svn-1.8/RPMS/x86_64/">http://opensource.wandisco.com/</a>).</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/dist/XML-Parser/">XML::Parser</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the code management commands.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 2.36.</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/~srezic/Tk-804.028/">Tk</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> <code>fcm gui</code>.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 804.028.</p>
+    </dd>
+
+    <dt><a href="http://subversion.apache.org/">Subversion</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the code management commands, the extract system
+      of <code>fcm make</code>, the deprecated <code>fcm extract</code>.</p>
+
+      <p><dfn>versions known to work:</dfn>RHEL-6: 1.8.10.</p>
+
+      <p><dfn>remark:</dfn> you can use the extract system to mirror code to a
+      remote platform for building. Therefore it is only necessary to have
+      Subversion installed on the platform where you do your code development.
+      If you use other platforms purely for building and running then you do
+      not need to have Subversion installed on these platforms.</p>
+    </dd>
+
+    <dt><a href="http://trac.edgewall.org/">Trac</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> (optional, but highly recommended as a companion
+      to Subversion)</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 1.0.5.</p>
+    </dd>
+
+    <dt><a href="http://furius.ca/xxdiff/">xxdiff</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> <code>fcm branch-diff --graphical</code>,
+      <code>fcm conflicts</code>, <code>fcm diff --graphical</code>.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 3.2.</p>
+
+      <p><dfn>remark:</dfn> The <code>fcm branch-diff --graphical</code> and
+      <code>fcm diff --graphical</code> commands use xxdiff by default but can
+      also use other graphical diff tools.</p>
+    </dd>
+
+    <dt><a href="http://www.gnu.org/software/diffutils/">GNU diffutils</a>:
+    diff3</dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the extract system of <code>fcm make</code>, the
+      deprecated <code>fcm extract</code>.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 2.8.1.</p>
+
+      <p><dfn>remark:</dfn>: used to merge changes to source files modified by
+      2+ diff source trees (compared with the base).</p>
+    </dd>
+
+    <dt><a href="http://rsync.samba.org/">rsync</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the mirror system of <code>fcm make</code>, the
+      deprecated <code>fcm extract</code>.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 3.0.6.</p>
+
+      <p><dfn>remark:</dfn> used to mirror source file to another
+      <var>USER@HOST</var>.</p>
+    </dd>
+
+    <dt><a href="http://www.gnu.org/software/make/make.html">GNU make</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the deprecated <code>fcm build</code>.</p>
+
+      <p><dfn>versions known to work:</dfn> RHEL-6: 3.81.</p>
+    </dd>
+  </dl>
+  </div>
+  </div>
+
+  <div class="row">
+  <div class="col-md-12">
+  <h2 id="distro">Content in the Distribution</h2>
+
+  <dl class="well">
+    <dt>ACKNOWLEDGEMENT.md<br />
+    CONTRIBUTING.md<br />
+    README.md<br />
+    COPYING</dt>
+
+    <dd>Terms of use and project information.</dd>
+
+    <dt>CHANGES.md</dt>
+
+    <dd>Contains highlight and noteworthy changes since release 2-3-1.</dd>
+
+    <dt>bin/</dt>
+
+    <dd>Contains the <code>fcm</code> user utilities.</dd>
+
+    <dt>doc/</dt>
+
+    <dd>FCM documentation.</dd>
+
+    <dt>doc/installation/</dt>
+
+    <dd>Contains this document.</dd>
+
+    <dt>doc/release_notes/</dt>
+
+    <dd>Contains release notes prior to and including release 2-3-1.</dd>
+
+    <dt>doc/user_guide/</dt>
+
+    <dd>Contains the <a href="../user_guide/">FCM User Guide</a>.</dd>
+
+    <dt>etc/</dt>
+
+    <dd>Miscellaneous items and example site configurations, including the
+    <samp>fcm/keyword.cfg.example</samp> file. If you wish to define keywords
+    for your site you will need to create the <samp>etc/fcm/keyword.cfg</samp>
+    file. An example file, <samp>fcm/keyword.cfg.example</samp>, is provided
+    which is a copy of the file currently used at the Met Office. For further
+    details please refer to the section <a
+    href="../user_guide/system_admin.html#fcm-keywords">FCM keywords</a> in the
+    System Admin chapter of the User Guide.</dd>
+
+    <dt>lib/</dt>
+
+    <dd>Contains the Perl library of FCM.</dd>
+
+    <dt>man/</dt>
+
+    <dd>Contains a basic manual page for <code>fcm</code>.</dd>
+
+    <dt>sbin/</dt>
+
+    <dd>Contains a selection of useful admin utility commands.</dd>
+
+    <dt>sbin/my-regular-update.example</dt>
+
+    <dd>An example of how you might set up a cron job to make use of the
+    <samp>&lt;repos&gt;.latest</samp> file (see
+    <code>svn-hooks/post-commit-background</code>).</dd>
+
+    <dt>svn-hooks/</dt>
+
+    <dd>Contains a selection of useful hook scripts for Subversion.</dd>
+
+    <dt>svn-hooks/pre-commit</dt>
+
+    <dd>
+      This script restricts write-access to the repository by checking the
+      following:
+
+      <ul>
+        <li>It executes the Subversion utility <code>svnperms.py</code> if it,
+        and the associated <samp>svnperms.conf</samp> file, exist. This utility
+        checks whether the author of the current transaction has enough
+        permission to write to particular paths in the repository.</li>
+
+        <li>It checks the disk space required by the current transaction. It
+        fails the commit if it requires more than 10MB of disk space (or
+        whatever is specified in the
+        <code>pre-commit-size-threshold.conf</code> file.</li>
+      </ul>
+    </dd>
+
+    <dt>svn-hooks/post-commit</dt>
+
+    <dd>This script runs <code>post-commit-background</code> in the
+    background.</dd>
+
+    <dt>svn-hooks/post-commit-background</dt>
+
+    <dd>
+      This script runs in the background after each commit.
+
+      <ul>
+        <li>It updates a <samp>&lt;repos&gt;.latest</samp> file with the latest
+        revision number.</li>
+
+        <li>It creates a dump of the new revision.</li>
+
+        <li>It calls <code>post-commit-background-custom</code> if it
+        exists.</li>
+      </ul>
+    </dd>
+
+    <dt>svn-hooks/pre-revprop-change</dt>
+
+    <dd>This script only allows the modification of <var>svn:log</var>.</dd>
+
+    <dt>svn-hooks/post-revprop-change</dt>
+
+    <dd>This script runs <code>post-revprop-change-background</code> in the
+    background.</dd>
+
+    <dt>svn-hooks/post-revprop-change-background</dt>
+
+    <dd>This script invokes the <code>trac-admin</code> command to
+    <code>resync</code> the revision property cache stored in the corresponding
+    Trac environment. If a user modifies the log message of a changeset and
+    he/she is not the original author of the changeset, this script will e-mail
+    the original author. If the file
+    <code>post-revprop-change-background-cc.list</code> exits, the script will
+    also e-mail those in the list.</dd>
+
+    <dt>t/</dt>
+
+    <dd>Contains functional test for FCM.</dd>
+
+    <dt>test/</dt>
+
+    <dd>Contains regression tests for FCM.</dd>
+
+    <dt>test/test_include/</dt>
+
+    <dd>Contains simple test code to check how your chosen compilers handle
+    include files.</dd>
+
+    <dt>tutorial/</dt>
+
+    <dd>Contains the files necessary to set up a Subversion repository for the
+    FCM tutorial. This will allow you to follow the <a href=
+    "../user_guide/getting_started.html#tutorial">tutorial section</a> in the
+    User Guide. See <samp>tutorial/README</samp> on how to set it up.</dd>
+  </dl>
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-3.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-3.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-3.html	(revision 5095)
@@ -0,0 +1,589 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 1.3 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 1.3 Release Notes <small>30 January 2008</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM release 1.3. You can use this release
+  of FCM freely under the terms of the FCM LICENSE, which you should receive
+  with the distribution of this release.</p>
+
+  <p>Note that FCM now requires Subversion 1.4.0 or later (previous releases
+  only required Subversion 1.2.0).</p>
+
+  <p>FCM is maintained by the FCM team at the Met Office. Please feedback any
+  bug reports or feature requests to us by <a href=
+  "mailto:fcm-team@metoffice.gov.uk">e-mail</a>.</p>
+
+  <h2 id="new">What's New?</h2>
+
+  <p>Build and extract:</p>
+
+  <ul>
+    <li>The syntax for declaring the extract/build destinations are unified.
+    The <code>DEST</code> (or <code>DEST::ROOTDIR</code>) declaration should be
+    used to declare the root of the extract/build destination. The
+    <code>DIR::ROOT</code> declaration is deprecated.</li>
+
+    <li>Users can no longer declare sub-directories in a destination.
+    Declarations such as <code>DEST::CFGDIR</code>, <code>DEST::SRCDIR</code>,
+    etc are no longer supported.</li>
+
+    <li>In configuration files, words or fields in a <em>label</em> can now be
+    delimited by a slash (<code>/</code>) as well as a double colon
+    (<code>::</code>). To improve readability, the convention is to only use
+    slash as the delimiter when referring to package names.</li>
+
+    <li>In a <code>true</code> or <code>false</code> declaration in a
+    configuration file, the system will now accept the following values as
+    false: <samp>0</samp>, <samp>&lt;empty string&gt;</samp>,
+    <samp>false</samp>, <samp>no</samp> and <samp>off</samp>. (It used to
+    accept <samp>&lt;any non empty string&gt;</samp> for <code>true</code> and
+    <samp>0</samp>, or <samp>&lt;empty string&gt;</samp> for
+    <code>false</code>.)</li>
+
+    <li>The build and extract caches are now located separately in
+    <samp>.cache/.bld/</samp> and <samp>.cache/.ext/</samp> respectively. In
+    most cases they will be upgraded automatically when you perform the next
+    incremental build/extract. However, if you use inherited build/extract, you
+    must upgrade the inherited build/extract before they can be used. To
+    upgrade an inherited extract, issue the following commands:
+      <pre>
+cd /path/to/inherited/extract/
+cd .cache/
+mkdir .ext/
+cp -r -p .config * .ext/
+</pre>
+
+      <p>To upgrade an inherited build, issue the following commands:</p>
+      <pre>
+cd /path/to/inherited/build/
+fcm bld
+</pre>
+    </li>
+
+    <li>Extra/common stages in the build/extract processes: <em>parse
+    configuration</em> and <em>set up destination</em>.</li>
+
+    <li>The build/extract destination (and the remote destination for extract)
+    will now be printed in verbose mode 1 or above.</li>
+
+    <li>A new command line option <code>--clean</code> for removing files
+    generated by previous extracts/builds.</li>
+
+    <li>An as-parsed configuration file will be generated for each run (if the
+    file differs from the previous run).</li>
+  </ul>
+
+  <p>Build:</p>
+
+  <ul>
+    <li>It is now more efficient to make <code>SRC</code> declarations for
+    files instead of the container directories. Container directories can still
+    be declared, but they will be expanded by the configuration file parser
+    into a list of source files within it. In addition, <code>SRC</code>
+    declarations no longer require the specification of package names - if it
+    is a relative path of the src/ sub-directory. If a relative path is
+    specified, it will be assumed a relative path of the src/
+    sub-directory.</li>
+
+    <li>For declarations such as <code>EXCL_DEP, PP, TOOL</code>, if a package
+    name is associated with the declaration, the system will fail if the
+    package is not declared or defined.</li>
+
+    <li>The system will now detect changes in <code>EXCL_DEP, INFILE_EXT and
+    OUTFILE_EXT</code> declarations in an incremental build.</li>
+
+    <li>It is now possible to make PP declarations down to the file level.
+    (Previously, it could only be done down to the level of the container
+    directories.)</li>
+
+    <li>The package name for a file used to be its root name (i.e. its basename
+    without the extension). It is now its basename, (although the system will
+    continue to support declarations down to the file's root name).</li>
+
+    <li>The declarations <code>TYPE</code> and <code>DEP</code> can now be used
+    to define the type and dependencies of a source file. (This replaces most
+    functionalities of the package configuration file.)</li>
+
+    <li>The build package configuration file is no longer supported.</li>
+
+    <li>The system now generates a single <samp>Makefile</samp> in the root
+    location of the destination. Hard coded directories should appear only once
+    at the top of the Makefile, provided that source files are only located in
+    the <samp>src/</samp> sub-directory of the build destination. (A build used
+    to have a top level <samp>Makefile</samp>, which included a
+    <samp>*.mk</samp> file for each source directory in the <samp>bld/</samp>
+    sub-directory of the destination.)</li>
+
+    <li>The <samp>fcm_env.ksh</samp> file is renamed <samp>fcm_env.sh</samp>,
+    which should work with the Bourne shell. (<samp>fcm_env.ksh</samp> is still
+    available via a symbolic link but is deprecated.)</li>
+
+    <li>The <code>--archive</code> or <code>-a</code> option will now archive
+    your build directories using the command <code>tar -czf FILE DIR</code>.
+    Consequently, the system will dearchive them using <code>tar -xzf
+    FILE</code>. If you have been using this option in your previous builds,
+    you should extract the archives manually using <code>tar -xf FILE</code>
+    before running <code>fcm build</code> in incremental mode. Remove the old
+    TAR files on success.</li>
+
+    <li>The linker command now defaults to the compiler of the language of the
+    program source file. (The default used to be <code>ld</code>.)</li>
+  </ul>
+
+  <p>Code management commands:</p>
+
+  <ul>
+    <li>Allow other graphical merge tools to be used in place of
+    <em>xxdiff</em> by modifying <code>bin/fcm_graphic_merge</code>.</li>
+
+    <li><code>fcm commit</code> will issue extra warning when a user attempts
+    to commit to or remove a branch belonging to another user.</li>
+
+    <li>The system has been modified to account for the improved support for
+    peg revisions using the <code>svn log</code> command in Subversion 1.4.
+    Unfortunately this means that Subversion 1.4.x clients are now
+    required.</li>
+
+    <li><code>fcm diff</code> now supports the <code>--summarize</code> option
+    which was introduced in Subversion 1.4.</li>
+
+    <li>Added alternate branching strategy in the <a href=
+    "../collaboration/">External Distribution &amp; Collaboration for FCM
+    Projects</a> document.</li>
+
+    <li>A number of limitations with the <code>fcm mkpatch</code> command have
+    been fixed. It will also use unified diffs where possible in order to
+    reduce the size of the patch and to make it more readable.</li>
+  </ul>
+
+  <p>Extract:</p>
+
+  <ul>
+    <li>The <code>ROOTDIR</code> part of the <code>RDEST::ROOTDIR</code>
+    declaration is now optional.</li>
+
+    <li>The <code>MIRROR</code> declaration is deprecated, and replaced by the
+    <code>RDEST::MIRROR_CMD</code> declaration.</li>
+
+    <li>The build configuration file generated by extract should no longer
+    contain hard coded paths - except for <code>USE</code> declarations and
+    those protected by the <code>BLD</code> prefix.</li>
+
+    <li>The <code>VERSION</code> declaration is deprecated, and replaced by the
+    <code>REVISION</code> declaration.</li>
+
+    <li>Added a new <code>REVMATCH</code> declaration for the extract
+    configuration file. If you specify a revision (other than HEAD) for a
+    branch, and this revision is not associated with a changeset for this
+    branch, the system will normally inform you of this discrepancy. By setting
+    <code>REVMATCH</code> to "true", however, the discrepancy will cause
+    extract to fail.</li>
+
+    <li>Extract used to fail if the same file is modified by two different
+    branches (compared with the base branch). It now attempts to merge the
+    changes using <code>diff3 -E -m</code>. It only fails if there are
+    unresolved conflicts.</li>
+
+    <li>Consequently, the <code>OVERRIDE</code> declaration is deprecated, and
+    replaced by the <code>CONFLICT</code> declaration, which can be set to
+    <samp>fail</samp>, <samp>merge</samp> (default) or
+    <samp>override</samp>.</li>
+  </ul>
+
+  <h2 id="fix">Minor enhancements &amp; Bug Fixes</h2>
+
+  <p>Build:</p>
+
+  <ul>
+    <li>If there is no source file to build, report an error at the beginning
+    of the build process.</li>
+  </ul>
+
+  <p>Code management commands:</p>
+
+  <ul>
+    <li>Fixed: FCM URL keyword not expanded when it is specified with an equal
+    sign in an option.</li>
+
+    <li>Fixed: typo in the output of <code>fcm branch --info</code>.</li>
+  </ul>
+
+  <p>Extract:</p>
+
+  <ul>
+    <li>Improved logic for better performance.</li>
+
+    <li>Allow mirroring to use <code>rsync</code> with an alternate remote
+    shell.</li>
+
+    <li><code>fcm cmp-ext-cfg</code>: Improved support for <a href=
+    "http://trac.edgewall.org/wiki/InterTrac">InterTrac</a> links.</li>
+  </ul>
+
+  <p>General:</p>
+
+  <ul>
+    <li>Fixed: problems parsing Subversion peg revision with the FCM URL
+    keywords.</li>
+
+    <li>The general shell used by FCM is changed from <code>/usr/bin/ksh</code>
+    to <code>/bin/sh</code> to improve portability.</li>
+
+    <li>Various other very minor enhancements and bug fixes.</li>
+  </ul>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <p>The following are known issues with this release of FCM which we plan to
+  address in later releases:</p>
+
+  <ul>
+    <li>FCM build does not handle changes in an include file correctly in an
+    inherited build if the include file resides in the same directory as the
+    source file including it, and the source file remains unchanged. This is
+    due to the fact that most pre-processor/compiler commands search the
+    directory containing the source file for include files first before they
+    search elsewhere. We are hoping to find a solution to this problem before
+    the next release.</li>
+  </ul>
+
+  <h2 id="req">System Requirements</h2>
+
+  <h3 id="req_perl">Perl</h3>
+
+  <p>The core part of FCM is a set of Perl scripts and modules. For the build
+  system to work, you need the following modules installed:</p>
+
+  <ul>
+    <li>Carp</li>
+
+    <li>Cwd</li>
+
+    <li>File::Basename</li>
+
+    <li>File::Compare</li>
+
+    <li>File::Find</li>
+
+    <li>File::Path</li>
+
+    <li>File::Spec::Functions</li>
+
+    <li>File::Spec</li>
+
+    <li>FindBin</li>
+
+    <li>Getopt::Long</li>
+
+    <li>POSIX</li>
+  </ul>
+
+  <p>The code management commands and extract system need the following
+  additional modules installed:</p>
+
+  <ul>
+    <li>File::Temp</li>
+
+    <li>Getopt::Long</li>
+
+    <li>HTTP::Date</li>
+
+    <li>XML::DOM</li>
+  </ul>
+
+  <p>To use the simple GUI for some of the code management commands, you also
+  need the following modules:</p>
+
+  <ul>
+    <li>Tk::ROText</li>
+
+    <li>Tk</li>
+  </ul>
+
+  <p>At the Met Office we are currently using the complete FCM system with Perl
+  5.8.x. In addition the build system is being used with Perl 5.6.x.</p>
+
+  <h3 id="req_svn">Subversion</h3>
+
+  <p>To use the code management commands (and relevant parts of the extract
+  system) you need to have <a href=
+  "http://subversion.tigris.org/">Subversion</a> installed.</p>
+
+  <ul>
+    <li>FCM makes extensive use of peg revisions in both the code management
+    and extract systems. This requires Subversion 1.4.0.</li>
+
+    <li>At the Met Office we are currently using Subversion 1.4.3.</li>
+  </ul>
+
+  <p>Note that the extract system can mirror extracted code to a remote
+  platform for building. Therefore it is only necessary to have Subversion
+  installed on the platform where you do your code development. If you use
+  other platforms purely for building and running then you do not need to have
+  Subversion installed on these platforms.</p>
+
+  <h3 id="req_trac">Trac</h3>
+
+  <p>The use of <a href="http://trac.edgewall.org/">Trac</a> is entirely
+  optional (although highly recommended if you are using Subversion).</p>
+
+  <ul>
+    <li>The <code>--trac</code> and <code>--wiki</code> options to the
+    <code>fcm diff --branch</code> command allow you to view branch differences
+    using Trac. This requires Trac 0.10.</li>
+
+    <li>At the Met Office we are currently using Trac 0.10.3.</li>
+  </ul>
+
+  <h3 id="req_other">Other Requirements</h3>
+
+  <p>The <code>fcm conflicts</code> command requires <a href=
+  "http://furius.ca/xxdiff/">xxdiff</a>. At the Met Office we are currently
+  using version 3.1. The <code>fcm diff --graphical</code> command also uses
+  xxdiff by default although other graphical diff tools can also be used.</p>
+
+  <p>The extract system can use diff3, which is part of <a href=
+  "http://www.gnu.org/software/diffutils/">GNU diffutils</a>, to merge together
+  changes where the same file is modified by two different branches (compared
+  with the base branch). At the Met Office we are currently using version
+  2.8.1.</p>
+
+  <p>The build system requires <a href=
+  "http://www.gnu.org/software/make/make.html">GNU make</a>. At the Met Office
+  we are currently using version 3.79.x and 3.80.</p>
+
+  <p>Optionally, the build system can use <a href=
+  "http://www.ifremer.fr/ditigo/molagnon/fortran90/">f90aib</a> to generate
+  interface files. However, there is also a built in Perl based interface file
+  generator which is quicker and better in most cases so you are unlikely to
+  need f90aib unless you hit a problem with some particular code.</p>
+
+  <p>FCM is intended to run on a Unix/Linux system. It is currently used at the
+  Met Office on Linux (Red Hat Enterprise 2.1 and 4.5) and HP-UX 11.00.</p>
+
+  <h2 id="ins">Installation</h2>
+
+  <p>FCM is distributed in the form of a compressed tar file. Un-pack the tar
+  file into an appropriate location on your system. Then add the
+  <samp>bin/</samp> directory into your <var>PATH</var>. Once you have done
+  this you should now have full access to the FCM system, assuming that you
+  have met the requirements described in the previous section.</p>
+
+  <p>If you wish to define keywords for your systems you will need to create a
+  file <samp>etc/fcm.cfg</samp>. An example file, <samp>fcm.cfg.eg</samp>, is
+  provided which is a copy of the file currently used at the Met Office. For
+  further details please refer to the section <a href=
+  "../user_guide/system_admin.html#fcm-keywords">FCM keywords</a> in the System
+  Admin chapter of the User Guide.</p>
+
+  <p>The <samp>doc/</samp> directory contains all the system documentation.</p>
+
+  <ul>
+    <li><samp>doc/release_notes/</samp> contains these release notes. It also
+    contains the release notes for all previous versions which may be useful if
+    you have skipped any versions.</li>
+
+    <li><samp>doc/user_guide/</samp> contains the <a href="../user_guide/">FCM
+    User Guide</a>.</li>
+
+    <li><samp>doc/design/</samp> contains the <a href="../design/">FCM Detailed
+    Design</a> document (currently in draft form).</li>
+
+    <li><samp>doc/standards/</samp> contains the FCM <a href=
+    "../standards/perl_standard.html">Perl</a> and <a href=
+    "../standards/fortran_standard.html">Fortran</a> coding standards. The Perl
+    standard describes the standards followed by the FCM code. The Fortran
+    standard contains some <a href=
+    "../standards/fortran_standard.html#fcm">specific advice</a> on the best
+    way of writing Fortran code for use with FCM as well as more general advice
+    on good practise.</li>
+
+    <li><samp>doc/collaboration/</samp> contains the <a href=
+    "../collaboration/index.html">External Distribution &amp; Collaboration for
+    FCM Projects</a> document which discusses how projects configured under FCM
+    can be distributed externally.</li>
+  </ul>
+
+  <p>The <samp>tutorial/</samp> directory contains the files necessary to set
+  up a tutorial repository. This will allow you to follow the <a href=
+  "../user_guide/getting_started.html#tutorial">tutorial section</a> in the
+  User Guide.</p>
+
+  <ul>
+    <li>The file <samp>tutorial/repos/tutorial.dump</samp> should be loaded
+    into an empty repository using the <code>svnadmin load</code> command.</li>
+
+    <li>The hook scripts in <samp>tutorial/hook/</samp> should then be
+    installed in this repository in order to prevent any commits to the trunk.
+    Note that the configuration file <samp>svnperms.conf</samp> assumes that
+    the tutorial repository is called <samp>tutorial_svn</samp>. Please edit
+    this file if you use a different name. You also need to install the
+    Subversion utility <code>svnperms.py</code> in order for this to work.</li>
+
+    <li>The repository should be configured to allow users write access. You
+    may find it easiest to simply allow anonymous access.</li>
+
+    <li>A Trac system should be configured associated with the Tutorial
+    repository. You then need to allow users write access. You may find it
+    easiest to set up a number of guest accounts for this purpose.</li>
+  </ul>
+
+  <p>The <samp>templates/</samp> directory contains various example scripts
+  which you may find useful. Note that these scripts are all specific to the
+  Met Office and may contain hard coded paths and email addresses. They are
+  provided in the hope that you may find them useful as templates for setting
+  up similar scripts of your own. However, they should only be used after
+  careful review to adapt them to your environment. The contents are as
+  follows:</p>
+
+  <dl>
+    <dt>templates/hook/pre-commit</dt>
+
+    <dd>
+      This script restricts write-access to the repository by checking the
+      following:
+
+      <ul>
+        <li>It executes the Subversion utility <code>svnperms.py</code> if it,
+        and the associated <code>svnperms.conf</code> file, exist. This utility
+        checks whether the author of the current transaction has enough
+        permission to write to particular paths in the repository.</li>
+
+        <li>It checks the disk space required by the current transaction. It
+        fails the commit if it requires more than 5Mb of disk space.</li>
+      </ul>
+    </dd>
+
+    <dt>templates/hook/post-commit</dt>
+
+    <dd>A simple post-commit hook script which runs the script
+    <code>post-commit-background</code> in the background.</dd>
+
+    <dt>templates/hook/post-commit-background</dt>
+
+    <dd>
+      This script runs in the background after each commit
+
+      <ul>
+        <li>It updates a <code>&lt;repos&gt;.latest</code> file with the latest
+        revision number.</li>
+
+        <li>It creates a dump of the new revision.</li>
+
+        <li>It calls the script <code>background_updates.pl</code> if it
+        exists.</li>
+      </ul>This script is installed as standard in all our repositories.
+    </dd>
+
+    <dt>templates/hook/background_updates.pl</dt>
+
+    <dd>An example of how you may want to set up a
+    <code>background_updates.pl</code> script to perform post-commit tasks for
+    a specific repository. This script uses a lock file to prevent multiple
+    commits in quick succession from causing problems.</dd>
+
+    <dt>templates/hook/pre-revprop-change</dt>
+
+    <dd>A simple pre-revprop-change hook script which runs the script
+    <code>pre-revprop-change.pl</code>.</dd>
+
+    <dt>templates/hook/pre-revprop-change.pl</dt>
+
+    <dd>If a user attempts to modify the log message of a changeset and he/she
+    is not the original author of the changeset, this script will e-mail the
+    original author. You can also set up a watch facility to monitor changes of
+    log messages that affect particular paths in the repository. For further
+    details please refer to the section <a href=
+    "../user_guide/system_admin.html#svn_watch">Watching changes in log
+    messages</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>templates/hook/post-revprop-change</dt>
+
+    <dd>A simple post-revprop-change hook script which runs the script
+    <code>post-revprop-change.py</code>.</dd>
+
+    <dt>templates/hook/post-revprop-change.py</dt>
+
+    <dd>This hook script updates the Trac SQLite database following a
+    successful change in the log message.</dd>
+
+    <dt>templates/utils/cron_template.sh</dt>
+
+    <dd>An example of how you might set up a cron job to make use of the
+    <samp>&lt;repos&gt;.latest</samp> file.</dd>
+
+    <dt>templates/utils/daily_cron</dt>
+
+    <dd>The cron job which we run each night. It verifies and backs up each of
+    our repositories, housekeeps the revision dumps created by
+    <code>post-commit-background</code> and backs up each of our Trac systems.
+    It also handles the distribution of FCM to various platforms at the Met
+    Office.</dd>
+
+    <dt>templates/utils/fcm_add_trac.pl</dt>
+
+    <dd>This script sets up a new Trac system and applies some configuration
+    options which we use by default at the Met Office.</dd>
+
+    <dt>templates/utils/recover_svn.pl</dt>
+
+    <dd>This script allows us to recover all of our Subversion repositories by
+    using the nightly backups and the repository dumps.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-4.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-4.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-4.html	(revision 5095)
@@ -0,0 +1,399 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 1.4 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 1.4 Release Notes <small>12 February 2009</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM release 1.4. You can use this release
+  of FCM freely under the terms of the FCM LICENSE, which you should receive
+  with the distribution of this release.</p>
+
+  <p>Note that FCM 1.4 requires Subversion 1.4.x (but it has not been tested on
+  Subversion 1.5.x or above).</p>
+
+  <p>FCM is maintained by the FCM team at the Met Office. Please feedback any
+  bug reports or feature requests to us by <a href=
+  "mailto:fcm-team@metoffice.gov.uk">e-mail</a>.</p>
+
+  <h2 id="fix">Minor enhancements &amp; Bug Fixes</h2>
+
+  <p>Build:</p>
+
+  <ul>
+    <li>Fixed: ensure consistent behaviour for specifying <code>-D</code>,
+    <code>-I</code>, etc options for a preprocessor/compiler.</li>
+  </ul>
+
+  <p>Code management commands:</p>
+
+  <ul>
+    <li>Fixed: <code>fcm mkpatch</code>: Fix how property changes are
+    handled.</li>
+
+    <li>Fixed: <code>fcm mkpatch</code>: Fix how copied files and directories
+    are handled.</li>
+
+    <li>Fixed: <code>fcm mkpatch</code>: Prevent failures caused by the use of
+    Subversion keywords.</li>
+
+    <li>Fixed: <code>fcm mkpatch</code>: Prevent failures when used with
+    branches which do not follow the FCM naming convention.</li>
+  </ul>
+
+  <p>Extract:</p>
+
+  <ul>
+    <li>Allow extract config to define an alternate remote shell for
+    <code>rsync</code>.</li>
+  </ul>
+
+  <p>General:</p>
+
+  <ul>
+    <li>Some of the example scripts in the <samp>templates/</samp> directory
+    have been rewritten.</li>
+
+    <li>Various other very minor enhancements and bug fixes.</li>
+  </ul>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <p>The following are known issues with this release of FCM which we plan to
+  address in later releases:</p>
+
+  <ul>
+    <li>FCM build does not handle changes in an include file correctly in an
+    inherited build if the include file resides in the same directory as the
+    source file including it, and the source file remains unchanged. This is
+    due to the fact that most pre-processor/compiler commands search the
+    directory containing the source file for include files first before they
+    search elsewhere.</li>
+  </ul>
+
+  <h2 id="req">System Requirements</h2>
+
+  <h3 id="req_perl">Perl</h3>
+
+  <p>The core part of FCM is a set of Perl scripts and modules. For the build
+  system to work, you need the following modules installed:</p>
+
+  <ul>
+    <li>Carp</li>
+
+    <li>Cwd</li>
+
+    <li>File::Basename</li>
+
+    <li>File::Compare</li>
+
+    <li>File::Find</li>
+
+    <li>File::Path</li>
+
+    <li>File::Spec::Functions</li>
+
+    <li>File::Spec</li>
+
+    <li>FindBin</li>
+
+    <li>Getopt::Long</li>
+
+    <li>POSIX</li>
+  </ul>
+
+  <p>The code management commands and extract system need the following
+  additional modules installed:</p>
+
+  <ul>
+    <li>File::Temp</li>
+
+    <li>Getopt::Long</li>
+
+    <li>HTTP::Date</li>
+
+    <li>XML::DOM</li>
+  </ul>
+
+  <p>To use the simple GUI for some of the code management commands, you also
+  need the following modules:</p>
+
+  <ul>
+    <li>Tk::ROText</li>
+
+    <li>Tk</li>
+  </ul>
+
+  <p>At the Met Office we are currently using the complete FCM system with Perl
+  5.8.x. In addition the build system is being used with Perl 5.6.x.</p>
+
+  <h3 id="req_svn">Subversion</h3>
+
+  <p>To use the code management commands (and relevant parts of the extract
+  system) you need to have <a href=
+  "http://subversion.tigris.org/">Subversion</a> installed.</p>
+
+  <ul>
+    <li>FCM makes extensive use of peg revisions in both the code management
+    and extract systems. This requires Subversion 1.4.0.</li>
+
+    <li>At the Met Office we are currently using Subversion 1.4.3.</li>
+  </ul>
+
+  <p>Note that the extract system can mirror extracted code to a remote
+  platform for building. Therefore it is only necessary to have Subversion
+  installed on the platform where you do your code development. If you use
+  other platforms purely for building and running then you do not need to have
+  Subversion installed on these platforms.</p>
+
+  <h3 id="req_trac">Trac</h3>
+
+  <p>The use of <a href="http://trac.edgewall.org/">Trac</a> is entirely
+  optional (although highly recommended if you are using Subversion).</p>
+
+  <ul>
+    <li>The <code>--trac</code> and <code>--wiki</code> options to the
+    <code>fcm diff --branch</code> command allow you to view branch differences
+    using Trac. This requires Trac 0.10.</li>
+
+    <li>Some of the example scripts in the <samp>templates/</samp> directory
+    require Trac 0.11.</li>
+
+    <li>At the Met Office we are currently using Trac 0.11.2.1.</li>
+  </ul>
+
+  <h3 id="req_other">Other Requirements</h3>
+
+  <p>The <code>fcm conflicts</code> command requires <a href=
+  "http://furius.ca/xxdiff/">xxdiff</a>. At the Met Office we are currently
+  using version 3.1. The <code>fcm diff --graphical</code> command also uses
+  xxdiff by default although other graphical diff tools can also be used.</p>
+
+  <p>The extract system can use diff3, which is part of <a href=
+  "http://www.gnu.org/software/diffutils/">GNU diffutils</a>, to merge together
+  changes where the same file is modified by two different branches (compared
+  with the base branch). At the Met Office we are currently using version
+  2.8.1.</p>
+
+  <p>The build system requires <a href=
+  "http://www.gnu.org/software/make/make.html">GNU make</a>. At the Met Office
+  we are currently using version 3.79.x and 3.80.</p>
+
+  <p>Optionally, the build system can use <a href=
+  "http://www.ifremer.fr/ditigo/molagnon/fortran90/">f90aib</a> to generate
+  interface files. However, there is also a built in Perl based interface file
+  generator which is quicker and better in most cases so you are unlikely to
+  need f90aib unless you hit a problem with some particular code.</p>
+
+  <p>FCM is intended to run on a Unix/Linux system. It is currently used at the
+  Met Office on Linux (Red Hat Enterprise 2.1 and 4.6) and HP-UX 11.00.</p>
+
+  <h2 id="ins">Installation</h2>
+
+  <p>FCM is distributed in the form of a compressed tar file. Un-pack the tar
+  file into an appropriate location on your system. Then add the
+  <samp>bin/</samp> directory into your <var>PATH</var>. Once you have done
+  this you should now have full access to the FCM system, assuming that you
+  have met the requirements described in the previous section.</p>
+
+  <p>If you wish to define keywords for your systems you will need to create a
+  file <samp>etc/fcm.cfg</samp>. An example file, <samp>fcm.cfg.eg</samp>, is
+  provided which is a copy of the file currently used at the Met Office. For
+  further details please refer to the section <a href=
+  "../user_guide/system_admin.html#fcm-keywords">FCM keywords</a> in the System
+  Admin chapter of the User Guide.</p>
+
+  <p>The <samp>doc/</samp> directory contains all the system documentation.</p>
+
+  <ul>
+    <li><samp>doc/release_notes/</samp> contains these release notes. It also
+    contains the release notes for all previous versions which may be useful if
+    you have skipped any versions.</li>
+
+    <li><samp>doc/user_guide/</samp> contains the <a href="../user_guide/">FCM
+    User Guide</a>.</li>
+
+    <li><samp>doc/design/</samp> contains the <a href="../design/">FCM Detailed
+    Design</a> document (currently in draft form).</li>
+
+    <li><samp>doc/standards/</samp> contains the FCM <a href=
+    "../standards/perl_standard.html">Perl</a> and <a href=
+    "../standards/fortran_standard.html">Fortran</a> coding standards. The Perl
+    standard describes the standards followed by the FCM code. The Fortran
+    standard contains some <a href=
+    "../standards/fortran_standard.html#fcm">specific advice</a> on the best
+    way of writing Fortran code for use with FCM as well as more general advice
+    on good practise.</li>
+
+    <li><samp>doc/collaboration/</samp> contains the <a href=
+    "../collaboration/index.html">External Distribution &amp; Collaboration for
+    FCM Projects</a> document which discusses how projects configured under FCM
+    can be distributed externally.</li>
+  </ul>
+
+  <p>The <samp>tutorial/</samp> directory contains the files necessary to set
+  up a tutorial repository. This will allow you to follow the <a href=
+  "../user_guide/getting_started.html#tutorial">tutorial section</a> in the
+  User Guide.</p>
+
+  <ul>
+    <li>The file <samp>tutorial/svn.dump</samp> should be loaded into an empty
+    repository using the <code>svnadmin load</code> command.</li>
+
+    <li>The hook scripts in <samp>tutorial/hooks/</samp> should then be
+    installed in this repository in order to prevent any commits to the trunk.
+    Note that the configuration file <code>svnperms.conf</code> assumes that
+    the tutorial repository is called <samp>tutorial_svn</samp>. Please edit
+    this file if you use a different name.</li>
+
+    <li>The repository should be configured to allow users write access. You
+    may find it easiest to simply allow anonymous access.</li>
+
+    <li>A Trac system should be configured associated with the Tutorial
+    repository. You then need to allow users write access. You may find it
+    easiest to set up a number of guest accounts for this purpose.</li>
+  </ul>
+
+  <p>The <samp>templates/</samp> directory contains various example scripts
+  which you may find useful. Note that these scripts are all specific to the
+  Met Office and may contain hard coded paths and email addresses. They are
+  provided in the hope that you may find them useful as templates for setting
+  up similar scripts of your own. However, they should only be used after
+  careful review to adapt them to your environment. The contents are as
+  follows:</p>
+
+  <dl>
+    <dt>templates/hooks/pre-commit</dt>
+
+    <dd>
+      This script restricts write-access to the repository by checking the
+      following:
+
+      <ul>
+        <li>It executes the Subversion utility <code>svnperms.py</code> if it,
+        and the associated <samp>svnperms.conf</samp> file, exist. This utility
+        checks whether the author of the current transaction has enough
+        permission to write to particular paths in the repository.</li>
+
+        <li>It checks the disk space required by the current transaction. It
+        fails the commit if it requires more than 5Mb of disk space.</li>
+      </ul>
+    </dd>
+
+    <dt>templates/hooks/post-commit</dt>
+
+    <dd>A simple post-commit hook script which runs the script
+    <code>post-commit-background</code> in the background.</dd>
+
+    <dt>templates/hooks/post-commit-background</dt>
+
+    <dd>
+      This script runs in the background after each commit
+
+      <ul>
+        <li>It updates a <samp>&lt;repos&gt;.latest</samp> file with the latest
+        revision number.</li>
+
+        <li>It creates a dump of the new revision.</li>
+
+        <li>It calls the script <code>background_updates.pl</code> if it
+        exists.</li>
+      </ul>This script is installed as standard in all our repositories.
+    </dd>
+
+    <dt>templates/hooks/background_updates.pl</dt>
+
+    <dd>An example of how you may want to set up a
+    <code>background_updates.pl</code> script to perform post-commit tasks for
+    a specific repository. This script uses a lock file to prevent multiple
+    commits in quick succession from causing problems.</dd>
+
+    <dt>templates/hooks/pre-revprop-change</dt>
+
+    <dd>A simple pre-revprop-change hook script which runs the script
+    <code>pre-revprop-change.pl</code>.</dd>
+
+    <dt>templates/hooks/pre-revprop-change.pl</dt>
+
+    <dd>If a user attempts to modify the log message of a changeset and he/she
+    is not the original author of the changeset, this script will e-mail the
+    original author. You can also set up a watch facility to monitor changes of
+    log messages that affect particular paths in the repository. For further
+    details please refer to the section <a href=
+    "../user_guide/system_admin.html#svn_watch">Watching changes in log
+    messages</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>templates/hooks/post-revprop-change</dt>
+
+    <dd>A simple post-revprop-change hook script which invokes the
+    <code>trac-admin</code> command to <code>resync</code> the revision
+    property cache stored in the corresponding Trac environment.</dd>
+
+    <dt>templates/utils/cron_template.sh</dt>
+
+    <dd>An example of how you might set up a cron job to make use of the
+    <samp>&lt;repos&gt;.latest</samp> file.</dd>
+
+    <dt>templates/utils/FCM/Admin/</dt>
+
+    <dd>A Perl library in the <code>FCM::Admin::*</code> name space, which
+    implements the functionalities of the FCM admin utility commands.</dd>
+
+    <dt>templates/utils/fcm-*</dt>
+
+    <dd>A selection of useful FCM admin utility commands.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-5.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-5.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/1-5.html	(revision 5095)
@@ -0,0 +1,487 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 1.5 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 1.5 Release Notes <small>22 January 2010</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM release 1.5. You can use this release
+  of FCM freely under the terms of the FCM LICENSE, which you should receive
+  with the distribution of this release.</p>
+
+  <p>FCM is maintained by the FCM team at the Met Office. Please feedback any
+  bug reports or feature requests to us by <a href=
+  "mailto:fcm-team@metoffice.gov.uk">e-mail</a>.</p>
+
+  <h2 id="new">What's New?</h2>
+
+  <dl>
+    <dt><code>fcm branch --list --show-all</code></dt>
+
+    <dd>New option to list all branches in a project.</dd>
+
+    <dt><code>fcm keyword-print</code>: new command</dt>
+
+    <dd>A command to print registered FCM keywords.</dd>
+
+    <dt>New method to manage revision keywords and other keywords related
+    settings.</dt>
+
+    <dd>See <a href="../user_guide/system_admin.html#fcm-keywords">FCM User
+    Guide &gt; System Administration &gt; FCM keywords</a> for detail. Note:
+    the <code>SET::TRAC</code> declaration is no longer supported.</dd>
+
+    <dt><code>fcm update</code>: improvement</dt>
+
+    <dd>The <code>fcm update</code> command applies to a whole working copy. If
+    the working copy contains local changes, the command will prompt the user
+    for confirmation.</dd>
+  </dl>
+
+  <h2 id="fix">Minor Enhancements &amp; Bug Fixes</h2>
+
+  <dl>
+    <dt><code>fcm build</code>: new <code>NO_DEP</code> declaration to switch
+    off dependency checking</dt>
+
+    <dd>A new declaration to switch off dependency checking for a given
+    name-space. See the <a href=
+    "../user_guide/build.html#advanced_dependency">FCM User Guide &gt; The
+    Build System &gt; Further dependency features</a> for details.</dd>
+
+    <dt><code>fcm build</code>: incorrect behaviour when dealing with the
+    removal of TOOL declarations in incremental mode</dt>
+
+    <dd><code>fcm build</code> did not always handle the removal of TOOL
+    declarations from the build configuration correctly in incremental mode.
+    This has been fixed.</dd>
+
+    <dt><code>fcm build</code>: new <code>TOOL::FC_MODSEARCH</code>
+    declaration</dt>
+
+    <dd>While most Fortran compilers search for the compiled module definition
+    files (i.e. <samp>*.mod</samp> files) using the same option as the include
+    search path (i.e. <samp>-I</samp>), some require a special option such as
+    <samp>-M</samp>. The new <code>TOOL::FC_MODSEARCH</code> declaration allows
+    such an option to be specified in the build configuration file.</dd>
+
+    <dt><code>fcm build</code>: incorrect logic for handling
+    <code>INHERIT::SRC</code> declarations</dt>
+
+    <dd>The logic for handling this declaration was incorrect. This led to
+    deleted files being incorrectly inherited. This has been fixed.</dd>
+
+    <dt><code>fcm build</code>: incorrect logic for generating exclude
+    dependency files for directory-based libraries</dt>
+
+    <dd>These files were not being generated corectly. This has been fixed.</dd>
+
+    <dt><code>fcm build</code>: incorrect logic for handling
+    <code>SRC_TYPE</code></dt>
+
+    <dd>The system was unable to search for an include file whose type was
+    declared via a <code>SRC_TYPE</code> declaration. This has been fixed.</dd>
+
+    <dt><code>fcm build</code>: improvement to the Fortran interface file
+    generator</dt>
+
+    <dd>The logic to extract the calling interfaces of top level subroutines
+    and functions from Fortran source files has been rewritten, based on the
+    original logic developed by the <a href="http://www.ecmwf.int">European
+    Centre for Medium-Range Weather Forecasts (ECMWF)</a>. In particular, the
+    new logic will correctly handle 1) pre-processor directives with
+    continuation lines, 2) continuation and comment markers in quotes, 3)
+    BLOCKDATA program units in the source file, 4) TYPE components in variable
+    identifiers, and 5) multiple program units in the source file. There are
+    also improvements in the new logic to reduce the number of useless
+    declarations and module imports in the generated interface block.</dd>
+
+    <dt><code>fcm commit</code>: improvement to the commit message
+    delimiter</dt>
+
+    <dd>Some users found the old delimiter line confusing. This has been
+    improved.</dd>
+
+    <dt><code>fcm commit</code>: <code>svn:special</code> and
+    <code>svn:executable</code></dt>
+
+    <dd>A symbolic link pointing to an executable target can cause a subsequent
+    <code>svn checkout</code> to fail if the target is removed. To avoid the
+    potential problem, <code>fcm commit</code> has been altered to remove the
+    <code>svn:executable</code> property if a path is a symbolic link.</dd>
+
+    <dt><code>fcm extract</code>: handling of file permission changes in
+    incremental mode</dt>
+
+    <dd><code>fcm extract</code> did not handle file permission changes in
+    incremental mode. This has been fixed.</dd>
+
+    <dt><code>fcm extract</code>: handling of symbolic links</dt>
+
+    <dd>Symbolic links cannot be handled safely by <code>fcm extract</code>.
+    They are now removed from the extract.</dd>
+
+    <dt><code>fcm extract</code> and <code>fcm build</code>: machine
+    hostname</dt>
+
+    <dd>The machine hostname will now be printed with the destination in the
+    diagnostic output of these commands.</dd>
+
+    <dt><code>fcm extract</code>: missing <code>RDEST</code> in the on-success
+    configuration file</dt>
+
+    <dd>Some <code>RDEST</code> declarations were missing from the on-success
+    generated configuration file. This has been fixed.</dd>
+
+    <dt><code>fcm extract</code>: improved options for the mirror
+    sub-system</dt>
+
+    <dd>It is now possible to specify the options of the <code>rsync</code>
+    command in the extract configuration file. In addition, <code>ssh</code> is
+    now the default remote shell command.</dd>
+
+    <dt><code>fcm cmp-ext-cfg</code>: changed verbose option</dt>
+
+    <dd>The <code>--verbose</code> option now requires an argument.</dd>
+  </dl>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>See the <a href="../user_guide/build.html#advanced_inherit">FCM User
+    Guide &gt; The Build System &gt; Inherit from a previous build</a> for
+    detail.</dd>
+  </dl>
+
+  <h2 id="req">System Requirements</h2>
+
+  <h3 id="req_perl">Perl</h3>
+
+  <p>The core part of FCM is a set of Perl scripts and modules. The following
+  core/CPAN Perl modules are required to invoke the <code>fcm</code>
+  command:</p>
+  <pre>
+Carp
+Cwd
+File::Basename
+File::Compare
+File::Copy
+File::Find
+File::Path
+File::Spec
+File::Spec::Functions
+File::Temp
+FindBin
+Getopt::Long
+HTTP::Date
+IO::File
+List::Util
+POSIX
+Pod::Usage
+Scalar::Util
+Sys::Hostname
+Text::ParseWords
+URI
+XML::DOM
+</pre>
+
+  <p>The following Perl modules are also required if you want to use the
+  <code>fcm gui</code> command:</p>
+  <pre>
+Tk
+Tk::ROText
+</pre>
+
+  <p>At the Met Office we are currently using FCM with Perl 5.8.2 on AIX 5.3
+  and Perl 5.8.5 on RHEL 4.</p>
+
+  <h3 id="req_svn">Subversion</h3>
+
+  <p>To use the code management commands (and relevant parts of the extract
+  system) you need to have <a href=
+  "http://subversion.tigris.org/">Subversion</a> installed.</p>
+
+  <p>At the Met Office we are currently using Subversion 1.4.3. Note: FCM 1.5
+  requires Subversion 1.4.x (but it has not been tested on Subversion 1.5.x or
+  above).</p>
+
+  <p>Note: you can use the extract system to mirror code to a remote platform
+  for building. Therefore it is only necessary to have Subversion installed on
+  the platform where you do your code development. If you use other platforms
+  purely for building and running then you do not need to have Subversion
+  installed on these platforms.</p>
+
+  <h3 id="req_trac">Trac</h3>
+
+  <p>The use of <a href="http://trac.edgewall.org/">Trac</a> is entirely
+  optional (although highly recommended if you are using Subversion).</p>
+
+  <p>At the Met Office we are currently using Trac 0.11.2.1. Note:</p>
+
+  <ul>
+    <li>The <code>--trac</code> and <code>--wiki</code> options to the
+    <code>fcm diff --branch</code> command allow you to view branch differences
+    using Trac. This requires Trac 0.10 or above.</li>
+
+    <li>Some of the example scripts in the <samp>examples/</samp> directory
+    require Trac 0.11.</li>
+  </ul>
+
+  <h3 id="req_other">Other Requirements</h3>
+
+  <p>The <code>fcm conflicts</code> command requires <a href=
+  "http://furius.ca/xxdiff/">xxdiff</a>. At the Met Office we are currently
+  using version 3.1. The <code>fcm diff --graphical</code> command also uses
+  xxdiff by default although other graphical diff tools can also be used.</p>
+
+  <p>The extract system uses <code>diff3</code>, (which is part of <a href=
+  "http://www.gnu.org/software/diffutils/">GNU diffutils</a>), to merge
+  together changes where the same file is modified by two different branches
+  (compared with the base branch). At the Met Office we are currently using
+  version 2.8.1.</p>
+
+  <p>The extract system uses <a href="http://rsync.samba.org/">rsync</a> to
+  mirror source file to another machine. At the Met Office we are currently
+  using version 2.6.3</p>
+
+  <p>The build system requires <a href=
+  "http://www.gnu.org/software/make/make.html">GNU make</a>. At the Met Office
+  we are currently using version 3.80.</p>
+
+  <p>FCM is intended to run on a Unix/Linux system. It is currently used at the
+  Met Office on Linux (RHEL 4.8) and AIX 5.3.</p>
+
+  <h2 id="ins">Installation</h2>
+
+  <p>FCM is distributed in the form of a compressed tar file. Un-pack the tar
+  file into an appropriate location on your system. Add the <samp>bin/</samp>
+  directory into your <var>PATH</var> environment variable. Once you have done
+  this you should now have full access to the FCM system, assuming that you
+  have met the requirements described in the previous section.</p>
+
+  <p>You should find the following contents in the distribution:</p>
+
+  <dl>
+    <dt>README</dt>
+
+    <dd>The README file contains the internal revision number of the release.</dd>
+
+    <dt>COPYRIGHT.txt<br />
+    LICENSE.html</dt>
+
+    <dd>The FCM license and other copyright information.</dd>
+
+    <dt>bin/</dt>
+
+    <dd>Contains the <code>fcm</code> command and other utilities.</dd>
+
+    <dt>doc/</dt>
+
+    <dd>System documentation.</dd>
+
+    <dt>doc/release_notes/</dt>
+
+    <dd>Contains these release notes. It also contains the release notes for
+    all previous versions which may be useful if you have skipped any
+    versions.</dd>
+
+    <dt>doc/user_guide/</dt>
+
+    <dd>Contains the <a href="../user_guide/">FCM User Guide</a>.</dd>
+
+    <dt>doc/standards/</dt>
+
+    <dd>Contains the FCM <a href="../standards/perl_standard.html">Perl</a> and
+    <a href="../standards/fortran_standard.html">Fortran</a> coding standards.
+    The Perl standard describes the standards followed by the FCM code. The
+    Fortran standard contains some <a href=
+    "../standards/fortran_standard.html#fcm">specific advice</a> on the best
+    way of writing Fortran code for use with FCM as well as more general advice
+    on good practise.</dd>
+
+    <dt>doc/collaboration/</dt>
+
+    <dd>Contains the <a href="../collaboration/index.html">External
+    Distribution &amp; Collaboration for FCM Projects</a> document which
+    discusses how projects configured under FCM can be distributed
+    externally.</dd>
+
+    <dt>etc/</dt>
+
+    <dd>Miscellaneous items, including the <samp>fcm.cfg.eg</samp> file. If you
+    wish to define keywords for your systems you will need to create the
+    <samp>etc/fcm.cfg</samp> file. An example file, <samp>fcm.cfg.eg</samp>, is
+    provided which is a copy of the file currently used at the Met Office. For
+    further details please refer to the section <a href=
+    "../user_guide/system_admin.html#fcm-keywords">FCM keywords</a> in the
+    System Admin chapter of the User Guide.</dd>
+
+    <dt>examples/</dt>
+
+    <dd>Contains various example scripts which you may find useful. Note that
+    these scripts are all specific to the Met Office and may contain hard coded
+    paths and email addresses. They are provided in the hope that you may find
+    them useful as examples for setting up similar scripts of your own.
+    However, they should only be used after careful review to adapt them to
+    your environment.</dd>
+
+    <dt>examples/etc/regular-update.eg</dt>
+
+    <dd>An example of how you might set up a cron job to make use of the
+    <samp>&lt;repos&gt;.latest</samp> file (see
+    <code>examples/svn-hooks/post-commit-background</code>).</dd>
+
+    <dt>examples/lib/</dt>
+
+    <dd>Contains the <code>FCM::Admin::*</code> Perl library, which implements
+    the functionalities of the FCM admin utility commands.</dd>
+
+    <dt>examples/sbin/</dt>
+
+    <dd>Contains a selection of useful admin utility commands.</dd>
+
+    <dt>examples/svn-hooks/pre-commit</dt>
+
+    <dd>
+      This script restricts write-access to the repository by checking the
+      following:
+
+      <ul>
+        <li>It executes the Subversion utility <code>svnperms.py</code> if it,
+        and the associated <samp>svnperms.conf</samp> file, exist. This utility
+        checks whether the author of the current transaction has enough
+        permission to write to particular paths in the repository.</li>
+
+        <li>It checks the disk space required by the current transaction. It
+        fails the commit if it requires more than 5Mb of disk space.</li>
+      </ul>
+    </dd>
+
+    <dt>examples/svn-hooks/post-commit</dt>
+
+    <dd>A simple post-commit hook script which runs the script
+    <code>post-commit-background</code> in the background.</dd>
+
+    <dt>examples/svn-hooks/post-commit-background</dt>
+
+    <dd>
+      This script runs in the background after each commit
+
+      <ul>
+        <li>It updates a <samp>&lt;repos&gt;.latest</samp> file with the latest
+        revision number.</li>
+
+        <li>It creates a dump of the new revision.</li>
+
+        <li>It calls the script <code>background_updates.pl</code> if it
+        exists.</li>
+      </ul>This script is installed as standard in all our repositories.
+    </dd>
+
+    <dt>examples/svn-hooks/background_updates.pl</dt>
+
+    <dd>An example of how you may want to set up a
+    <code>background_updates.pl</code> script to perform post-commit tasks for
+    a specific repository. This script uses a lock file to prevent multiple
+    commits in quick succession from causing problems.</dd>
+
+    <dt>examples/svn-hooks/pre-revprop-change</dt>
+
+    <dd>A simple pre-revprop-change hook script which runs the script
+    <code>pre-revprop-change.pl</code>.</dd>
+
+    <dt>examples/svn-hooks/pre-revprop-change.pl</dt>
+
+    <dd>If a user attempts to modify the log message of a changeset and he/she
+    is not the original author of the changeset, this script will e-mail the
+    original author. You can also set up a watch facility to monitor changes of
+    log messages that affect particular paths in the repository. For further
+    details please refer to the section <a href=
+    "../user_guide/system_admin.html#svn_watch">Watching changes in log
+    messages</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>examples/svn-hooks/post-revprop-change</dt>
+
+    <dd>A simple post-revprop-change hook script which invokes the
+    <code>trac-admin</code> command to <code>resync</code> the revision
+    property cache stored in the corresponding Trac environment.</dd>
+
+    <dt>lib/</dt>
+
+    <dd>Contains the Perl library of FCM.</dd>
+
+    <dt>man/</dt>
+
+    <dd>Contains a basic manual page for <code>fcm</code>.</dd>
+
+    <dt>t/</dt>
+
+    <dd>Contains unit test for FCM.</dd>
+
+    <dt>test/</dt>
+
+    <dd>Contains regression tests for FCM.</dd>
+
+    <dt>tutorial/</dt>
+
+    <dd>Contains the files necessary to set up a Subversion repository for the
+    FCM tutorial. This will allow you to follow the <a href=
+    "../user_guide/getting_started.html#tutorial">tutorial section</a> in the
+    User Guide. See <samp>tutorial/README</samp> on how to set it up.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-0.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-0.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-0.html	(revision 5095)
@@ -0,0 +1,758 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 2-0 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 2-0 Release Notes <small>11 March 2011</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM 2-0. You can use this release of FCM
+  freely under the terms of the FCM LICENSE, which you should receive with the
+  distribution of this release.</p>
+
+  <p>FCM is maintained by the FCM team at the Met Office. Please feedback any
+  bug reports or feature requests to us by <a href=
+  "mailto:fcm-team@metoffice.gov.uk">e-mail</a>.</p>
+
+  <h2 id="new">What's New?</h2>
+
+  <dl>
+    <dt><code><a href="../user_guide/command_ref.html#fcm-make">fcm
+    make</a></code>: new command</dt>
+
+    <dd>New extract/build system. Deprecates <code>fcm extract</code> and
+    <code>fcm build</code>. See <a href="../user_guide/make.html">User Guide
+    &gt; FCM Make</a> for detail on how to use the new system. See also
+    <a href="#new.make">fcm make: key differences compared with fcm
+    extract/build</a>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-branch-create">fcm
+    branch-create</a></code>: new command</dt>
+
+    <dd>Deprecates <code>fcm branch --create</code>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-branch-delete">fcm
+    branch-delete</a></code>: new command</dt>
+
+    <dd>Deprecates <code>fcm branch --delete</code>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-branch-diff">fcm
+    branch-diff</a></code>: new command</dt>
+
+    <dd>Deprecates <code>fcm diff --branch</code>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-branch-info">fcm
+    branch-info</a></code>: new command</dt>
+
+    <dd>Deprecates <code>fcm branch --info</code>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-branch-list">fcm
+    branch-list</a></code>: new command</dt>
+
+    <dd>Deprecates <code>fcm branch --list</code>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-browse">fcm
+    browse</a></code></dt>
+
+    <dd>This is now the preferred name of <code>fcm trac</code> or <code>fcm
+    www</code>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-cfg-print">fcm
+    cfg-print</a></code></dt>
+
+    <dd>This is now the preferred name of <code>fcm cfg</code>.</dd>
+
+    <dt><code><a href="../user_guide/command_ref.html#fcm-export-items">fcm
+    export-items</a></code>: new command</dt>
+
+    <dd>Deprecates <code>fcm_update_version_dir.pl</code>.</dd>
+
+    <dt>Configuration for keywords</dt>
+
+    <dd>New syntax and location. See <a href=
+    "../user_guide/annex_cfg.html#keyword">User Guide &gt; Annex: FCM
+    Configuration File &gt; FCM Keyword Configuration</a> for detail.
+    Equivalent settings in <samp>$FCM/etc/fcm.cfg</samp> will no longer work.
+    Those in <samp>$HOME/.fcm</samp> should continue to work. We would
+    encourage users to migrate to the new syntax and location.</dd>
+
+    <dt>Configuration for external commands</dt>
+
+    <dd>New syntax and location. See <a href=
+    "../user_guide/annex_cfg.html#external">User Guide &gt; Annex: FCM
+    Configuration File &gt; FCM External Configuration</a> for detail.
+    Equivalent settings in <samp>$FCM/etc/fcm.cfg</samp> and
+    <samp>$HOME/.fcm</samp> will no longer work.</dd>
+  </dl>
+
+  <h3 id="new.make">fcm make: key differences compared with fcm
+  extract/build</h3>
+
+  <p>Single command and framework of a configurable chain of <dfn>steps</dfn>,
+  e.g. extract, mirror, preprocess, build, etc.</p>
+
+  <ul>
+    <li>as opposed to 2 separate commands with fixed steps.</li>
+
+    <li>possible to set up multiple builds with different configurations from
+    the same extract.</li>
+  </ul>
+
+  <p>New configuration file format, with more powerful syntax and declarations.
+  E.g.:</p>
+
+  <ul>
+    <li>Improved support for specifying <dfn>name-spaces</dfn> (previously
+    <dfn>package</dfn> or <dfn>sub-package</dfn>) for a declaration.</li>
+
+    <li>Improved support for declaring and referencing variables.</li>
+
+    <li>Improved support for space and other meta-characters.</li>
+  </ul>
+
+  <p>extract: automatically associates location keywords to source tree
+  locations. E.g.:</p>
+
+  <ul>
+    <li>
+      <code>fcm extract</code> configuration requires repeated declarations:
+      <pre>
+cfg::type ext
+repos::foo::base fcm:foo/trunk
+expsrc::foo::base
+repos::bar::base fcm:bar/trunk
+expsrc::bar::base
+</pre>
+    </li>
+
+    <li>
+      <code>fcm make</code> configuration is much simpler:
+      <pre>
+steps = extract
+extract.ns = foo bar
+</pre>
+    </li>
+  </ul>
+
+  <p>extract: clearly distinguishes a base source tree from the diff source
+  trees for each project. E.g.:</p>
+
+  <ul>
+    <li>
+      <code>fcm extract</code> configuration requires arbitrary IDs for each
+      source tree, separate revision declarations, and assumes that the first
+      declared tree for a project is the <dfn>base</dfn>:
+      <pre>
+cfg::type ext
+repos::foo::base fcm:foo/trunk
+revision::foo::base 1234
+expsrc::foo::base
+repos::foo::b1 fcm:foo/branches/dev/fred/r1234_b1
+revision::foo::b1 2345
+repos::foo::b2 fcm:foo/branches/dev/bob/r1234_b2
+repos::foo::b3 fcm:foo/branches/dev/alice/r1234_b3
+</pre>
+    </li>
+
+    <li>
+      <code>fcm make</code> configuration uses different declarations for the
+      location of the <dfn>base</dfn> source tree and the locations of the
+      <dfn>diff</dfn> source trees:
+      <pre>
+steps = extract
+extract.ns = foo
+extract.location[foo] = trunk@1234
+extract.location{diff}[foo] = \
+    branches/dev/fred/r1234_b1@2345 \
+    branches/dev/bob/r1234_b2 \
+    branches/dev/alice/r1234_b3
+</pre>
+    </li>
+  </ul>
+
+  <p>extract: can easily filter parts of a project source tree, and/or change
+  the root of the extract tree. E.g.:</p>
+
+  <ul>
+    <li>
+      <code>fcm extract</code> configuration can cause confusion:
+      <pre>
+cfg::type ext
+repos::um::base fcm:um/trunk/src
+revision::foo::base vn7.7
+expsrc::um::base
+repos::um::b1 fcm:um/branches/dev/fred/vn7.7_b1/src
+repos::um::b2 fcm:um/branches/dev/bob/vn7.7_b2/src
+repos::um::b3 fcm:um/branches/dev/alice/vn7.7_b3/src
+</pre>
+    </li>
+
+    <li>
+      <code>fcm make</code> configuration is clearer and has more features:
+      <pre>
+steps = extract
+extract.ns = um
+extract.path-root[um] = src
+extract.path-excl[um] = configs scm
+extract.location[um] = trunk@vn7.7
+extract.location{diff}[um] = \
+    branches/dev/fred/vn7.7_b1 \
+    branches/dev/bob/vn7.7_b2 \
+    branches/dev/alice/vn7.7_b3
+</pre>
+    </li>
+  </ul>
+
+  <p>extract: works with project source trees as opposed to individual source
+  directories.</p>
+
+  <ul>
+    <li>fewer calls to the version control system servers.</li>
+
+    <li>deleted directories are now handled correctly.</li>
+  </ul>
+
+  <p>extract: can use multiple processes to retrieve source trees information
+  and to export source tree files from the version control system.</p>
+
+  <ul>
+    <li>extract of multiple projects and/or with multiple source trees can be
+    much faster.</li>
+  </ul>
+
+  <p>mirror: is now an independent step.</p>
+
+  <ul>
+    <li>can set up multiple mirror steps to mirror an extract to alternate
+    destinations.</li>
+  </ul>
+
+  <p>build: can use multiple processes to analyse the source files for
+  dependencies and other information.</p>
+
+  <ul>
+    <li>multi-process build is much faster.</li>
+  </ul>
+
+  <p>build: uses an internal task manager and runner - more efficient logic
+  possible:</p>
+
+  <ul>
+    <li>no longer requires GNU make.</li>
+
+    <li>no longer requires dummy files such as <samp>*.done
+    *.flags</samp>.</li>
+
+    <li>uses MD5 checksums to determine whether sources and targets are out of
+    date - as opposed to time stamps.</li>
+
+    <li>fails the build if duplicated targets are detected if those targets are
+    required by the build.</li>
+  </ul>
+
+  <p>build: has improved the logic for building Fortran program units.</p>
+
+  <ul>
+    <li>detects correctly multiple top program units in the same source
+    file.</li>
+
+    <li>sets up a module usage as an include dependency on the
+    <samp>*.mod</samp> file instead of the <samp>*.o</samp> file - reduces the
+    chance of module compile cascades in incremental mode.</li>
+
+    <li>only generates interface files on demand.</li>
+  </ul>
+
+  <p>build: has improved facilities for sources and targets selection.</p>
+
+  <ul>
+    <li>can now select targets by name-space, category and task.</li>
+
+    <li>has better documentation on the relationship between source files and
+    build targets.</li>
+
+    <li>note that target declarations are not cumulative and that targets are
+    inherited by default (unlike with <code>fcm build</code>).</li>
+  </ul>
+
+  <p>build: automatically uses the Fortran compiler to link Fortran executables
+  and the C compiler to link C executables.</p>
+
+  <p>build: has more diagnostics, e.g. on source dependencies, target build
+  tree, etc.</p>
+
+  <p>preprocess: is now an independent step, but shares all the logic of the
+  build system, e.g.:</p>
+
+  <ul>
+    <li>preprocessing dependency analysis and target update can be performed in
+    multiple processes.</li>
+
+    <li>note that file extensions are not modified by the preprocess step
+    unlike with <code>fcm build</code> which changed, for example
+    <samp>.F90</samp> extensions to <samp>.f90</samp>.</li>
+  </ul>
+
+  <p>Other notable changes:</p>
+
+  <ul>
+    <li>By default, <code>fcm make</code> will always rebuild link targets and
+    re-install scripts in inherited builds. Therefore, to use the executables
+    from a build all you need to do is set your <var>PATH</var> environment
+    variable to point to <samp>$DEST/build/bin/</samp> (where <var>$DEST</var>
+    is the destination of the make). Note that there is no
+    <samp>fcm_env.sh</samp> file produced by <code>fcm make</code>.</li>
+
+    <li><code>fcm extract</code> has the ability to fail if the declared
+    revision of a branch does not correspond to a changeset of that branch.
+    Furthermore, it can output the latest revision of a branch if the declared
+    revision is not the latest. <code>fcm make</code> does not support
+    this.</li>
+
+    <li>There is no equivalent of <code><a href=
+    "../user_guide/command_ref.html#fcm-cmp-ext-cfg">fcm cmp-ext-cfg</a></code>
+    for FCM make configurations.</li>
+
+    <li><code>fcm make</code> does not support defining a separate linker - it
+    always uses the compiler of the source file containing the main program
+    (which is the default with <code>fcm build</code>).</li>
+
+    <li><code>fcm make</code> does not recognise existing binaries as install
+    targets (unlike <code>fcm build</code>). This feature is currently used to
+    allow the Met Office's Suite Control System (SCS) to "build" suites but is
+    no longer considered the best method. SCS will continue to use <code>fcm
+    build</code> until such time as a better method is adopted or the system is
+    retired.</li>
+
+    <li><code>fcm make</code> recognises data files as install targets in a
+    similar way to <code>fcm build</code>. However, the default destination of
+    such targets is now the full name-space under the <samp>etc/</samp>
+    sub-directory.</li>
+
+    <li><code>fcm make</code> supports the building of libraries but does not
+    generate the relevant exclude dependency configuration as is done by
+    <code>fcm build</code>.</li>
+
+    <li><code>fcm make</code> has no equivalent of the <code>--archive</code>
+    nor the <code>--targets</code> options provided by <code>fcm
+    build</code>.</li>
+
+    <li><code>fcm build</code> recognises a file name without its extension as
+    a sub-package name. This is not true with <code>fcm make</code> which only
+    recognises the full file name as a namespace.</li>
+  </ul>
+
+  <p>In addition to the differences noted above, <code>fcm make</code> fixes
+  various subtle problems which can occur with <code>fcm extract</code> and
+  <code>fcm build</code> as a result of limitations in the internal design.
+  Overall, <code>fcm make</code> is much better and we recommend that all users
+  migrate to it. <code>fcm extract</code> and <code>fcm build</code> will
+  continue to be maintained for legacy systems but will not be developed
+  further.</p>
+
+  <h2 id="fix">Minor Changes and Bug Fixes</h2>
+
+  <dl>
+    <dt><code>fcm build</code></dt>
+
+    <dd>
+      <p>Handle directory names with a dot extension.</p>
+
+      <p>Correct search path for inherited configuration file.</p>
+
+      <p>Always export <var>OBJECTS</var> in generated
+      <samp>Makefile</samp>.</p>
+    </dd>
+
+    <dt><code>fcm cfg</code></dt>
+
+    <dd>
+      <p>Now an alias of <code>fcm cfg-print</code>.</p>
+
+      <p>The default behaviour is to parse FCM 2 configuration files. To parse
+      FCM 1 configuration files, use the <code>--fcm1</code> option.</p>
+
+      <p>The values in the output will no longer be lined up.</p>
+    </dd>
+
+    <dt><code>fcm extract</code></dt>
+
+    <dd>
+      <p>Fix double slashes in cache of extract with project root level
+      files.</p>
+
+      <p>Correct search path for inherited configuration file.</p>
+
+      <p>Fix incremental mode behaviour of targets with <dfn>deleted,
+      overriding inherited</dfn> status.</p>
+    </dd>
+
+    <dt><code>fcm keyword-print</code></dt>
+
+    <dd>Change in output format to match the new configuration file
+    format.</dd>
+
+    <dt><code>fcm mkpatch</code></dt>
+
+    <dd>
+      <p>Don't use patch file if PDF file detected.</p>
+
+      <p>Handle property changes to directories.</p>
+
+      <p>Handle copies within new directories.</p>
+
+      <p>Handle replaced directories.</p>
+
+      <p>Fix handling of symbolic links.</p>
+
+      <p>Fix pattern match used when checking for excluded or copied paths.</p>
+
+      <p>Use <code>--no-backup-if-mismatch</code> option to patch command to
+      ensure backup files not created if patch does not match exactly.</p>
+    </dd>
+
+    <dt><code>fcm</code> direct wrappers to <code>svn</code> commands</dt>
+
+    <dd>No longer prints <samp>=&gt; svn ...</samp> on STDOUT.</dd>
+
+    <dt>Misc fixes</dt>
+
+    <dd>Misc fixes related to changes in Perl 5.10 and Subversion 1.6.</dd>
+  </dl>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>See the <a href="../user_guide/make.html#build.inherit">User Guide &gt;
+    FCM Make &gt; Build &gt; Build Inheritance</a> for detail.</dd>
+  </dl>
+
+  <h2 id="req">System Requirements</h2>
+
+  <h3 id="req.perl">Perl</h3>
+
+  <p>The core part of FCM is a set of Perl scripts and modules. At the Met
+  Office, FCM runs on:</p>
+
+  <dl>
+    <dt>Perl 5.8.2 on AIX 5.3</dt>
+
+    <dd>
+      <p><code>Text::ParseWords</code> (core Perl module) is upgraded to
+      version 3.22.</p>
+
+      <p>Met Office users do not use the code management commands and the
+      extract system on this platform.</p>
+    </dd>
+
+    <dt>Perl 5.8.5 on RHEL 4</dt>
+
+    <dd>
+      <p><a href=
+      "http://search.cpan.org/~gaas/libwww-perl/lib/HTTP/Date.pm">HTTP::Date</a>
+      in <a href="http://search.cpan.org/~gaas/libwww-perl/">libwww-perl</a> is
+      required by <code>fcm extract</code> and the extract system in <code>fcm
+      make</code>. (libwww-perl 5.79 installed.)</p>
+
+      <p><a href=
+      "http://search.cpan.org/~enno/libxml-enno/lib/XML/DOM.pm">XML::DOM</a> in
+      <a href="http://search.cpan.org/~enno/libxml-enno/">libxml-enno</a> is
+      required by the code management commands. (libxml-enno 1.02
+      installed.)</p>
+
+      <p><a href="http://search.cpan.org/~srezic/Tk/">Tk</a> is required by the
+      <code>fcm gui</code> command. (Tk 804.027 installed.)</p>
+    </dd>
+  </dl>
+
+  <h3 id="req.svn">Subversion</h3>
+
+  <p>To use the code management commands (and relevant parts of the extract
+  system) you need to have <a href=
+  "http://subversion.tigris.org/">Subversion</a> installed.</p>
+
+  <p>FCM requires Subversion 1.4.x or above. At the Met Office we are currently
+  using Subversion 1.4.3.</p>
+
+  <p>Note: you can use the extract system to mirror code to a remote platform
+  for building. Therefore it is only necessary to have Subversion installed on
+  the platform where you do your code development. If you use other platforms
+  purely for building and running then you do not need to have Subversion
+  installed on these platforms.</p>
+
+  <h3 id="req.trac">Trac</h3>
+
+  <p>The use of <a href="http://trac.edgewall.org/">Trac</a> is entirely
+  optional (although highly recommended if you are using Subversion).</p>
+
+  <p>At the Met Office we are currently using Trac 0.11.7. Note:</p>
+
+  <ul>
+    <li>The <code>--trac</code> and <code>--wiki</code> options to the
+    <code>fcm diff --branch</code> command allow you to view branch differences
+    using Trac. This requires Trac 0.10 or above.</li>
+
+    <li>Some of the example scripts in the <samp>examples/</samp> directory
+    require Trac 0.11.</li>
+  </ul>
+
+  <h3 id="req.other">Other Requirements</h3>
+
+  <p>The <code>fcm conflicts</code> command requires <a href=
+  "http://furius.ca/xxdiff/">xxdiff</a>. At the Met Office we are currently
+  using version 3.1. The <code>fcm diff --graphical</code> command also uses
+  xxdiff by default although other graphical diff tools can also be used.</p>
+
+  <p>The <code>fcm make</code> command uses <code>gzip</code>. At the Met
+  Office we are currently using gzip 1.2.4 on AIX 5.3 and gzip 1.3.3 on RHEL
+  4.</p>
+
+  <p>The extract system uses <code>diff3</code>, (which is part of <a href=
+  "http://www.gnu.org/software/diffutils/">GNU diffutils</a>), to merge
+  together changes where the same file is modified by two different branches
+  (compared with the base branch). At the Met Office we are currently using
+  version 2.8.1.</p>
+
+  <p>The mirror system uses <a href="http://rsync.samba.org/">rsync</a> to
+  mirror source file to another machine. At the Met Office we are currently
+  using version 2.6.3</p>
+
+  <p>The deprecated <code>fcm build</code> requires <a href=
+  "http://www.gnu.org/software/make/make.html">GNU make</a>. At the Met Office
+  we are currently using version 3.80.</p>
+
+  <p>FCM is intended to run on a Unix/Linux system. It is currently used at the
+  Met Office on Linux (RHEL 4.8) and AIX 5.3.</p>
+
+  <h2 id="ins">Installation</h2>
+
+  <p>FCM is distributed in the form of a compressed tar file. Un-pack the tar
+  file into an appropriate location on your system. Add the <samp>bin/</samp>
+  directory into your <var>PATH</var> environment variable. Once you have done
+  this you should now have full access to the FCM system, assuming that you
+  have met the requirements described in the previous section.</p>
+
+  <p>You should find the following contents in the distribution:</p>
+
+  <dl>
+    <dt>README</dt>
+
+    <dd>The README file contains the internal revision number of the
+    release.</dd>
+
+    <dt>COPYRIGHT.txt<br />
+    LICENSE.html</dt>
+
+    <dd>The FCM license and other copyright information.</dd>
+
+    <dt>bin/</dt>
+
+    <dd>Contains the <code>fcm</code> command and other utilities.</dd>
+
+    <dt>doc/</dt>
+
+    <dd>System documentation.</dd>
+
+    <dt>doc/release_notes/</dt>
+
+    <dd>Contains these release notes. It also contains the release notes for
+    all previous versions which may be useful if you have skipped any
+    versions.</dd>
+
+    <dt>doc/user_guide/</dt>
+
+    <dd>Contains the <a href="../user_guide/">FCM User Guide</a>.</dd>
+
+    <dt>doc/standards/</dt>
+
+    <dd>Contains the FCM <a href="../standards/perl_standard.html">Perl</a> and
+    <a href="../standards/fortran_standard.html">Fortran</a> coding standards.
+    The Perl standard describes the standards followed by the FCM code. The
+    Fortran standard contains some <a href=
+    "../standards/fortran_standard.html#fcm">specific advice</a> on the best
+    way of writing Fortran code for use with FCM as well as more general advice
+    on good practice.</dd>
+
+    <dt>doc/collaboration/</dt>
+
+    <dd>Contains the <a href="../collaboration/index.html">External
+    Distribution &amp; Collaboration for FCM Projects</a> document which
+    discusses how projects configured under FCM can be distributed
+    externally.</dd>
+
+    <dt>etc/</dt>
+
+    <dd>Miscellaneous items, including the <samp>fcm/keyword.cfg.eg</samp> file.
+    If you wish to define keywords for your site you will need to create the
+    <samp>etc/fcm/keyword.cfg</samp> file. An example file,
+    <samp>fcm/keyword.cfg.eg</samp>, is provided which is a copy of the file
+    currently used at the Met Office. For further details please refer to the
+    section <a href="../user_guide/system_admin.html#fcm-keywords">FCM
+    keywords</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>examples/</dt>
+
+    <dd>Contains various example scripts which you may find useful. Note that
+    these scripts are all specific to the Met Office and may contain hard coded
+    paths and email addresses. They are provided in the hope that you may find
+    them useful as examples for setting up similar scripts of your own.
+    However, they should only be used after careful review to adapt them to
+    your environment.</dd>
+
+    <dt>examples/etc/regular-update.eg</dt>
+
+    <dd>An example of how you might set up a cron job to make use of the
+    <samp>&lt;repos&gt;.latest</samp> file (see
+    <code>examples/svn-hooks/post-commit-background</code>).</dd>
+
+    <dt>examples/lib/</dt>
+
+    <dd>Contains the <code>FCM::Admin::*</code> Perl library, which implements
+    the functionalities of the FCM admin utility commands.</dd>
+
+    <dt>examples/sbin/</dt>
+
+    <dd>Contains a selection of useful admin utility commands.</dd>
+
+    <dt>examples/svn-hooks/pre-commit</dt>
+
+    <dd>
+      This script restricts write-access to the repository by checking the
+      following:
+
+      <ul>
+        <li>It executes the Subversion utility <code>svnperms.py</code> if it,
+        and the associated <samp>svnperms.conf</samp> file, exist. This utility
+        checks whether the author of the current transaction has enough
+        permission to write to particular paths in the repository.</li>
+
+        <li>It checks the disk space required by the current transaction. It
+        fails the commit if it requires more than 5Mb of disk space.</li>
+      </ul>
+    </dd>
+
+    <dt>examples/svn-hooks/post-commit</dt>
+
+    <dd>A simple post-commit hook script which runs the script
+    <code>post-commit-background</code> in the background.</dd>
+
+    <dt>examples/svn-hooks/post-commit-background</dt>
+
+    <dd>
+      This script runs in the background after each commit.
+
+      <ul>
+        <li>It updates a <samp>&lt;repos&gt;.latest</samp> file with the latest
+        revision number.</li>
+
+        <li>It creates a dump of the new revision.</li>
+
+        <li>It calls <code>post-commit-background-custom</code> if it
+        exists.</li>
+      </ul>
+    </dd>
+
+    <dt>examples/svn-hooks/pre-revprop-change</dt>
+
+    <dd>A simple pre-revprop-change hook script which runs the script
+    <code>pre-revprop-change.pl</code>.</dd>
+
+    <dt>examples/svn-hooks/pre-revprop-change.pl</dt>
+
+    <dd>If a user attempts to modify the log message of a changeset and he/she
+    is not the original author of the changeset, this script will e-mail the
+    original author. You can also set up a watch facility to monitor changes of
+    log messages that affect particular paths in the repository. For further
+    details please refer to the section <a href=
+    "../user_guide/system_admin.html#svn_watch">Watching changes in log
+    messages</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>examples/svn-hooks/post-revprop-change</dt>
+
+    <dd>A simple post-revprop-change hook script which invokes the
+    <code>trac-admin</code> command to <code>resync</code> the revision
+    property cache stored in the corresponding Trac environment.</dd>
+
+    <dt>lib/</dt>
+
+    <dd>Contains the Perl library of FCM.</dd>
+
+    <dt>man/</dt>
+
+    <dd>Contains a basic manual page for <code>fcm</code>.</dd>
+
+    <dt>t/</dt>
+
+    <dd>Contains unit test for FCM.</dd>
+
+    <dt>test/</dt>
+
+    <dd>Contains regression tests for FCM.</dd>
+
+    <dt>test/test_include/</dt>
+
+    <dd>Contains simple test code to check how your chosen compilers handle
+    include files (see <a href="#issues">Known Issues</a>).</dd>
+
+    <dt>tutorial/</dt>
+
+    <dd>Contains the files necessary to set up a Subversion repository for the
+    FCM tutorial. This will allow you to follow the <a href=
+    "../user_guide/getting_started.html#tutorial">tutorial section</a> in the
+    User Guide. See <samp>tutorial/README</samp> on how to set it up.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-1.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-1.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-1.html	(revision 5095)
@@ -0,0 +1,461 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 2-1 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 2-1 Release Notes <small>22 July 2011</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM 2-1. You can use this release of FCM
+  freely under the terms of the FCM LICENSE, which you should receive with the
+  distribution of this release.</p>
+
+  <p>FCM is maintained by the FCM team at the Met Office. Please feedback any
+  bug reports or feature requests to us by <a href=
+  "mailto:fcm-team@metoffice.gov.uk">e-mail</a>.</p>
+
+  <h2 id="new">What's New?</h2>
+
+  <p>No major new features in this release.</p>
+
+  <h2 id="fix">Minor Changes and Bug Fixes</h2>
+
+  <dl>
+    <dt><code>fcm branch-create</code></dt>
+
+    <dd>
+      <p>if the <code>--ticket=N</code> option is not specified and
+      <var>NAME</var> contains only a list of positive integers separated by
+      <code>[_-]</code> (an underscore or a hyphen), the command will now assume
+      that <var>NAME</var> also specifies the related ticket numbers.</p>
+
+      <p>ticket numbers will now prefix the 1st line of the automatic commit log
+      message.</p>
+    </dd>
+
+    <dt><code>fcm cmp-ext-cfg</code></dt>
+
+    <dd>
+      <p>fixed <code>--wiki-format=TARGET</code> option, broken at <a href=
+      "2-0.html">2-0</a>.</p>
+    </dd>
+
+    <dt><code>fcm export-items</code></dt>
+
+    <dd>
+      <p>fixed <code>--new</code> option, broken at <a href=
+      "2-0.html">2-0</a>.</p>
+
+      <p>now generate real <samp>v*</samp> directories and revision directories
+      as symbolic links.</p>
+
+      <p><code>fcm_update_version_dir.pl</code> removed.</p>
+    </dd>
+
+    <dt><code>fcm make</code></dt>
+
+    <dd>
+      <p>build: allow space between <code>#</code> and <code>include</code> for
+      <code>#include</code> syntax in C or Fortran source files requiring
+      pre-processing.</p>
+
+      <p>build: will now fail when an invalid target key is specified.</p>
+
+      <p>build: correctly support compilers and linkers that require a space
+      between an option flag and its argument.</p>
+
+      <p>build: will no longer call <code>ar</code> for single file link.</p>
+
+      <p>build: improve error message if a target does not exist in the expected
+      location following an update.</p>
+
+      <p>build: <code>target-rename</code> declarations are now non-cumulative,
+      as documented.</p>
+
+      <p>build: <code>build.prop{file-name-option.f90-mod} = case=upper</code>
+      can now be specified for compilers that generate <samp>MODULE.mod</samp>
+      files for Fortran modules (as opposed to the more common
+      <samp>module.mod</samp> convention).</p>
+
+      <p>extract: <code>path-incl</code>, <code>path-excl</code>,
+      <code>path-root</code> declarations no longer require a name-space.</p>
+
+      <p>extract: fixed error handling when multiple processing.</p>
+    </dd>
+
+    <dt><code>fcm mkpatch</code></dt>
+
+    <dd>
+      <p>fixed CLI prompt to confirm removal of old output directory.</p>
+
+      <p>ensure initial branch creation is ignored.</p>
+    </dd>
+
+    <dt>commit message text editor</dt>
+
+    <dd>
+      <p><code>fcm branch-create</code>, <code>fcm
+      branch-delete</code> and <code>fcm commit</code> now support the
+      <var>[helpers] editor-cmd</var> option defined in
+      <samp>$HOME/.subversion/config</samp>.</p>
+
+      <p><code>nedit</code> is no longer the default external text editor.
+      <code>vi</code> is the default external text editor for the command line
+      interface. <code>gedit</code> is the default external text editor for
+      <code>fcm gui</code>.</p>
+    </dd>
+
+    <dt>configuration file syntax</dt>
+
+    <dd>
+      <p>the <code>inc=LOCATION</code> is no longer supported for configuration
+      files in the new part of the system.</p>
+
+      <p>incorrect variable assignment syntax will now trigger an exception.</p>
+
+      <p>leading and trailing spaces in modifiers and name-space are now
+      ignored without triggering Perl warnings.</p>
+    </dd>
+
+    <dt>installation dependency</dt>
+
+    <dd>
+      <p><code>fcm</code> no longer requires the <code>XML::DOM</code> Perl
+      module. It now uses <code>XML::Parser</code> instead. The latter is
+      normally installed by default on most Unix/Linux platforms.</p>
+    </dd>
+  </dl>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>See the <a href="../user_guide/make.html#build.inherit">User Guide &gt;
+    FCM Make &gt; Build &gt; Build Inheritance</a> for detail.</dd>
+  </dl>
+
+  <h2 id="req">System Requirements</h2>
+
+  <h3 id="req.perl">Perl</h3>
+
+  <p>The core part of FCM is a set of Perl scripts and modules. At the Met
+  Office, FCM runs on:</p>
+
+  <dl>
+    <dt>Perl 5.8.2 on AIX 5.3</dt>
+
+    <dd>
+      <p><code>Text::ParseWords</code> (core Perl module) is upgraded to
+      version 3.22.</p>
+
+      <p>Met Office users do not use the code management commands and the
+      extract system on this platform.</p>
+    </dd>
+
+    <dt>Perl 5.8.5 on RHEL 4</dt>
+
+    <dd>
+      <p><a href=
+      "http://search.cpan.org/~gaas/libwww-perl/lib/HTTP/Date.pm">HTTP::Date</a>
+      in <a href="http://search.cpan.org/~gaas/libwww-perl/">libwww-perl</a> is
+      required by <code>fcm extract</code> and the extract system in <code>fcm
+      make</code>. (libwww-perl 5.79 installed.)</p>
+
+      <p><a href="http://search.cpan.org/dist/XML-Parser/">XML::Parser</a> is
+      required by the code management commands. (2.34 installed.)</p>
+
+      <p><a href="http://search.cpan.org/~srezic/Tk/">Tk</a> is required by the
+      <code>fcm gui</code> command. (Tk 804.027 installed.)</p>
+    </dd>
+  </dl>
+
+  <h3 id="req.svn">Subversion</h3>
+
+  <p>To use the code management commands (and relevant parts of the extract
+  system) you need to have <a href=
+  "http://subversion.tigris.org/">Subversion</a> installed.</p>
+
+  <p>FCM requires Subversion 1.4.x or above. At the Met Office we are currently
+  using Subversion 1.4.3.</p>
+
+  <p>Note: you can use the extract system to mirror code to a remote platform
+  for building. Therefore it is only necessary to have Subversion installed on
+  the platform where you do your code development. If you use other platforms
+  purely for building and running then you do not need to have Subversion
+  installed on these platforms.</p>
+
+  <h3 id="req.trac">Trac</h3>
+
+  <p>The use of <a href="http://trac.edgewall.org/">Trac</a> is entirely
+  optional (although highly recommended if you are using Subversion).</p>
+
+  <p>At the Met Office we are currently using Trac 0.11.7. Note:</p>
+
+  <ul>
+    <li>The <code>--trac</code> and <code>--wiki</code> options to the
+    <code>fcm diff --branch</code> command allow you to view branch differences
+    using Trac. This requires Trac 0.10 or above.</li>
+
+    <li>Some of the example scripts in the <samp>examples/</samp> directory
+    require Trac 0.11.</li>
+  </ul>
+
+  <h3 id="req.other">Other Requirements</h3>
+
+  <p>The <code>fcm conflicts</code> command requires <a href=
+  "http://furius.ca/xxdiff/">xxdiff</a>. At the Met Office we are currently
+  using version 3.1. The <code>fcm diff --graphical</code> command also uses
+  xxdiff by default although other graphical diff tools can also be used.</p>
+
+  <p>The <code>fcm make</code> command uses <code>gzip</code>. At the Met
+  Office we are currently using gzip 1.2.4 on AIX 5.3 and gzip 1.3.3 on RHEL
+  4.</p>
+
+  <p>The extract system uses <code>diff3</code>, (which is part of <a href=
+  "http://www.gnu.org/software/diffutils/">GNU diffutils</a>), to merge
+  together changes where the same file is modified by two different branches
+  (compared with the base branch). At the Met Office we are currently using
+  version 2.8.1.</p>
+
+  <p>The mirror system uses <a href="http://rsync.samba.org/">rsync</a> to
+  mirror source file to another machine. At the Met Office we are currently
+  using version 2.6.3</p>
+
+  <p>The deprecated <code>fcm build</code> requires <a href=
+  "http://www.gnu.org/software/make/make.html">GNU make</a>. At the Met Office
+  we are currently using version 3.80.</p>
+
+  <p>FCM is intended to run on a Unix/Linux system. It is currently used at the
+  Met Office on Linux (RHEL 4.8) and AIX 5.3.</p>
+
+  <h2 id="ins">Installation</h2>
+
+  <p>FCM is distributed in the form of a compressed tar file. Un-pack the tar
+  file into an appropriate location on your system. Add the <samp>bin/</samp>
+  directory into your <var>PATH</var> environment variable. Once you have done
+  this you should now have full access to the FCM system, assuming that you
+  have met the requirements described in the previous section.</p>
+
+  <p>You should find the following contents in the distribution:</p>
+
+  <dl>
+    <dt>README</dt>
+
+    <dd>The README file contains the internal revision number of the
+    release.</dd>
+
+    <dt>COPYRIGHT.txt<br />
+    LICENSE.html</dt>
+
+    <dd>The FCM license and other copyright information.</dd>
+
+    <dt>bin/</dt>
+
+    <dd>Contains the <code>fcm</code> command and other utilities.</dd>
+
+    <dt>doc/</dt>
+
+    <dd>System documentation.</dd>
+
+    <dt>doc/release_notes/</dt>
+
+    <dd>Contains these release notes. It also contains the release notes for
+    all previous versions which may be useful if you have skipped any
+    versions.</dd>
+
+    <dt>doc/user_guide/</dt>
+
+    <dd>Contains the <a href="../user_guide/">FCM User Guide</a>.</dd>
+
+    <dt>doc/standards/</dt>
+
+    <dd>Contains the FCM <a href="../standards/perl_standard.html">Perl</a> and
+    <a href="../standards/fortran_standard.html">Fortran</a> coding standards.
+    The Perl standard describes the standards followed by the FCM code. The
+    Fortran standard contains some <a href=
+    "../standards/fortran_standard.html#fcm">specific advice</a> on the best
+    way of writing Fortran code for use with FCM as well as more general advice
+    on good practice.</dd>
+
+    <dt>doc/collaboration/</dt>
+
+    <dd>Contains the <a href="../collaboration/index.html">External
+    Distribution &amp; Collaboration for FCM Projects</a> document which
+    discusses how projects configured under FCM can be distributed
+    externally.</dd>
+
+    <dt>etc/</dt>
+
+    <dd>Miscellaneous items, including the <samp>fcm/keyword.cfg.eg</samp> file.
+    If you wish to define keywords for your site you will need to create the
+    <samp>etc/fcm/keyword.cfg</samp> file. An example file,
+    <samp>fcm/keyword.cfg.eg</samp>, is provided which is a copy of the file
+    currently used at the Met Office. For further details please refer to the
+    section <a href="../user_guide/system_admin.html#fcm-keywords">FCM
+    keywords</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>examples/</dt>
+
+    <dd>Contains various example scripts which you may find useful. Note that
+    these scripts are all specific to the Met Office and may contain hard coded
+    paths and email addresses. They are provided in the hope that you may find
+    them useful as examples for setting up similar scripts of your own.
+    However, they should only be used after careful review to adapt them to
+    your environment.</dd>
+
+    <dt>examples/etc/regular-update.eg</dt>
+
+    <dd>An example of how you might set up a cron job to make use of the
+    <samp>&lt;repos&gt;.latest</samp> file (see
+    <code>examples/svn-hooks/post-commit-background</code>).</dd>
+
+    <dt>examples/lib/</dt>
+
+    <dd>Contains the <code>FCM::Admin::*</code> Perl library, which implements
+    the functionalities of the FCM admin utility commands.</dd>
+
+    <dt>examples/sbin/</dt>
+
+    <dd>Contains a selection of useful admin utility commands.</dd>
+
+    <dt>examples/svn-hooks/pre-commit</dt>
+
+    <dd>
+      This script restricts write-access to the repository by checking the
+      following:
+
+      <ul>
+        <li>It executes the Subversion utility <code>svnperms.py</code> if it,
+        and the associated <samp>svnperms.conf</samp> file, exist. This utility
+        checks whether the author of the current transaction has enough
+        permission to write to particular paths in the repository.</li>
+
+        <li>It checks the disk space required by the current transaction. It
+        fails the commit if it requires more than 5Mb of disk space.</li>
+      </ul>
+    </dd>
+
+    <dt>examples/svn-hooks/post-commit</dt>
+
+    <dd>A simple post-commit hook script which runs the script
+    <code>post-commit-background</code> in the background.</dd>
+
+    <dt>examples/svn-hooks/post-commit-background</dt>
+
+    <dd>
+      This script runs in the background after each commit.
+
+      <ul>
+        <li>It updates a <samp>&lt;repos&gt;.latest</samp> file with the latest
+        revision number.</li>
+
+        <li>It creates a dump of the new revision.</li>
+
+        <li>It calls <code>post-commit-background-custom</code> if it
+        exists.</li>
+      </ul>
+    </dd>
+
+    <dt>examples/svn-hooks/pre-revprop-change</dt>
+
+    <dd>A simple pre-revprop-change hook script which runs the script
+    <code>pre-revprop-change.pl</code>.</dd>
+
+    <dt>examples/svn-hooks/pre-revprop-change.pl</dt>
+
+    <dd>If a user attempts to modify the log message of a changeset and he/she
+    is not the original author of the changeset, this script will e-mail the
+    original author. You can also set up a watch facility to monitor changes of
+    log messages that affect particular paths in the repository. For further
+    details please refer to the section <a href=
+    "../user_guide/system_admin.html#svn_watch">Watching changes in log
+    messages</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>examples/svn-hooks/post-revprop-change</dt>
+
+    <dd>A simple post-revprop-change hook script which invokes the
+    <code>trac-admin</code> command to <code>resync</code> the revision
+    property cache stored in the corresponding Trac environment.</dd>
+
+    <dt>lib/</dt>
+
+    <dd>Contains the Perl library of FCM.</dd>
+
+    <dt>man/</dt>
+
+    <dd>Contains a basic manual page for <code>fcm</code>.</dd>
+
+    <dt>t/</dt>
+
+    <dd>Contains unit test for FCM.</dd>
+
+    <dt>test/</dt>
+
+    <dd>Contains regression tests for FCM.</dd>
+
+    <dt>test/test_include/</dt>
+
+    <dd>Contains simple test code to check how your chosen compilers handle
+    include files (see <a href="#issues">Known Issues</a>).</dd>
+
+    <dt>tutorial/</dt>
+
+    <dd>Contains the files necessary to set up a Subversion repository for the
+    FCM tutorial. This will allow you to follow the <a href=
+    "../user_guide/getting_started.html#tutorial">tutorial section</a> in the
+    User Guide. See <samp>tutorial/README</samp> on how to set it up.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-2.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-2.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-2.html	(revision 5095)
@@ -0,0 +1,604 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 2-2 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 2-2 Release Notes <small>15 June 2012</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM 2-2. You can use this release of FCM
+  freely under the terms of the FCM LICENSE, which you should receive with the
+  distribution of this release.</p>
+
+  <p>FCM is maintained by the FCM team at the Met Office. Please feedback any
+  bug reports or feature requests to us by <a href=
+  "mailto:fcm-team@metoffice.gov.uk">e-mail</a>.</p>
+
+  <h2>Contents</h2>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="changes-highlight">Highlight Changes</h2>
+
+  <dl>
+    <dt><code>Subversion 1.6</code></dt>
+
+    <dd>
+      <p>Code management commands and extract system tested with Subversion
+      1.6. Note that Subversion 1.4 is no longer supported and Subversion 1.5
+      has not been tested.</p>
+
+      <p>We experienced some non-repeatable problems using Subversion 1.6 where
+      we got incorrect results from a merge. Running <code>svn cleanup</code>
+      appears to avoid the problem. Therefore this is now run prior to any
+      update, switch or merge command.</p>
+    </dd>
+
+    <dt><code>fcm branch-create</code></dt>
+
+    <dd>
+      <p><code>-rREV</code> option no longer supported - add a peg revision to
+      the source if necessary.</p>
+    </dd>
+
+    <dt><code>fcm branch-list</code></dt>
+
+    <dd>
+      <p>significant improvement to speed.</p>
+
+      <p>multiple projects can be specified as arguments.</p>
+
+      <p>more options for advanced listing.</p>
+
+      <p><code>-rREV</code> option no longer supported - can use peg revision
+      for each project in the argument list.</p>
+
+      <p>no longer returns 1 if 0 branch is found - non-zero value reserved for
+      fatal errors.</p>
+
+      <p>new <code>--quiet</code> option to print names of matched branches
+      only.</p>
+
+      <p><code>-v</code> option no longer supported - use new
+      <code>--url</code> option to print branch names as full URLs instead of
+      FCM location keywords.</p>
+
+      <p>minor change to output format.</p>
+    </dd>
+
+    <dt><code>fcm conflicts</code></dt>
+
+    <dd>
+      <p>handle resolution of common cases of tree conflicts.</p>
+    </dd>
+
+    <dt><code>fcm make</code></dt>
+
+    <dd>
+      <p>build: change in how the dependency tree is created. This fixes the
+      behaviour where the system incorrectly reports cyclic dependency. An
+      example situation is where a recursive <code>subroutine a</code> calls
+      <code>subroutine b</code> which calls <code>subroutine a</code>. The
+      object file target <samp>a.o</samp> has a dependency on
+      <samp>b.interface</samp> and the object file target <samp>b.o</samp> has a
+      dependency on <samp>a.interface</samp>. Previously,
+      <samp>b.interface</samp> would also depend on <samp>b.o</samp> which would
+      result in a cyclic dependency. This fix changes the internal data
+      structure for representing a build target. <strong>Use of <code>fcm
+      make</code> at release 2-2 to build incrementally or to inherit a build
+      created by <code>fcm make</code> at release 2-1 or before will result in an
+      incorrect behaviour or failure. Use the <code>--new</code> option to start
+      a new build if the build destination is known to contain an existing build
+      created by <code>fcm make</code> at release 2-1 or before.</strong></p>
+    </dd>
+
+    <dt>Flexible repository layout</dt>
+
+    <dd>
+      <p>FCM code management commands no longer insist on a rigid layout of its
+      Subversion repositories. Behaviours can now be configured per repository.
+      In its default setting, FCM code management commands no longer insist on
+      <samp>branches/</samp> and <samp>tags/</samp> being present in a
+      <em>project</em>.</p>
+    </dd>
+  </dl>
+
+  <h2 id="changes-minor">Minor Changes and Bug Fixes</h2>
+
+  <dl>
+    <dt><code>fcm branch-create</code></dt>
+
+    <dd>
+      <p>new <code>--switch</code> option to switch the current working copy to
+      point to the newly created branch.</p>
+    </dd>
+
+    <dt><code>fcm branch-delete</code></dt>
+
+    <dd>
+      <p>new <code>--switch</code> option to switch the current working copy to
+      point to the <em>trunk</em> after the branch deletion.</p>
+    </dd>
+
+    <dt><code>fcm branch-diff</code></dt>
+
+    <dd>
+      <p>fixed: the command will no longer fail when called with an unmodified
+      sub-tree of a branch.</p>
+    </dd>
+
+    <dt><code>fcm build/extract</code></dt>
+
+    <dd>
+      <p>fixed multi-level inheritance destination search path.</p>
+    </dd>
+
+    <dt><code>fcm commit</code></dt>
+
+    <dd>
+      <p>fixed incorrect behaviour on <code>svn commit</code> failure.</p>
+    </dd>
+
+    <dt><code>fcm make</code></dt>
+
+    <dd>
+      <p>build: fixed multi-level inheritance destination search path.</p>
+
+      <p>build: do not use relative path for inherited include paths and object
+      file paths in command lines.</p>
+
+      <p>build: fixed duplicated dependent targets being reported as missing
+      dependencies.</p>
+
+      <p>build: fixed incorrect behaviour with Fortran <samp>*.mod</samp>
+      target in incremental mode where only a build property (e.g. a compiler
+      flag) associated with the source file of the <samp>*.mod</samp> target is
+      modified.</p>
+
+      <p>extract: fixed handling of add/delete of files in file system
+      locations during incremental extract.</p>
+
+      <p>extract: fixed Perl warning when a file system location contains a
+      symbolic link with a non-existent target.</p>
+
+      <p>extract: fixed Perl warning when 2 diff locations both add a file of
+      the same name but with different contents.</p>
+
+      <p>extract: improved merge conflict diagnostics.</p>
+
+      <p>extract: allow unmodified location in the configuration to pass in an
+      inherited mode.</p>
+
+      <p>mirror: fixed: create configuration file in target even if there is no
+      source to mirror.</p>
+
+      <p>mirror: always expand a relative path in a mirror target to a full path
+      to allow it to be inherited. Some infrequently used modifiers in the
+      <code>mirror.prop</code> declarations are added/removed for this fix.</p>
+    </dd>
+
+    <dt><code>fcm merge</code></dt>
+
+    <dd>
+      <p>simplified prompts and diagnostic outputs.</p>
+
+      <p>new <code>--auto-log</code> option for automatic merge. If the option
+      is specified, the command will include the logs of the merged revisions in
+      the commit log message automatically.</p>
+    </dd>
+
+    <dt><code>fcm mkpatch</code></dt>
+
+    <dd>
+      <p>no longer uses patch files if they include a carrriage return in the
+      middle of a line.</p>
+    </dd>
+
+    <dt><code>fcm project-create</code></dt>
+
+    <dd>
+      <p>new command to create a new project and its trunk directory in a
+      repository.</p>
+    </dd>
+
+    <dt>code management command line</dt>
+
+    <dd>
+      <p>fixed logic for parsing <var>URL@REV</var> where <var>URL</var> is not
+      a location keyword but <var>REV</var> is a revision keyword.</p>
+    </dd>
+
+    <dt>revision keyword</dt>
+
+    <dd>
+      <p>fixed: <var>fcm:revision</var> property setting: trailing spaces in
+      each line will now be ignored.</p>
+    </dd>
+
+    <dt>user guide</dt>
+
+    <dd>
+      <p>tutorial: added section on tree conflict resolution.</p>
+
+      <p>tutorial: removed references to <code>fcm gui</code>.</p>
+
+      <p>added new annex: quick reference: tree conflict resolution.</p>
+    </dd>
+
+    <dt>Perl 5.12</dt>
+
+    <dd>
+      <p>fixed: <a href=
+      "http://search.cpan.org/~jesse/perl-5.12.0/pod/perl5120delta.pod#REGEXPs_are_now_first_class">
+      incompatibility problem</a>.</p>
+    </dd>
+  </dl>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>See the <a href="../user_guide/make.html#build.inherit">User Guide &gt;
+    FCM Make &gt; Build &gt; Build Inheritance</a> for detail.</dd>
+  </dl>
+
+  <h2 id="requirements">System Requirements</h2>
+
+  <p>FCM is intended to run on a Unix/Linux system. It is currently used at the
+  Met Office on AIX-5.3 and RHEL-6.1.</p>
+
+  <dl>
+    <dt><a href="http://www.perl.org/">Perl</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> <code>fcm</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> AIX-5.3: 5.8.2 (see remark),
+      RHEL-6.1: 5.10.1.</p>
+
+      <p><dfn>remark:</dfn> 5.8.2 on AIX-5.3: code management commands, the
+      extract system of <code>fcm make</code> and the deprecated <code>fcm
+      extract</code> are not used by Met Office users on this platform.</p>
+
+      <p><dfn>remark:</dfn> 5.8.2 on AIX-5.3: <code>Text::ParseWords</code>
+      (core Perl module) is upgraded to version 3.22.</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/~gaas/libwww-perl-5.834/lib/HTTP/Date.pm">HTTP::Date</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the extract system in <code>fcm make</code> and
+      the deprecated <code>fcm extract</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> RHEL-6.1: 5.833.</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/~msergeant/XML-Parser-2.36/">XML::Parser</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the code management commands.</p>
+
+      <p><dfn>versions at Met Office:</dfn> RHEL-6.1: 2.36.</p>
+    </dd>
+
+    <dt>Perl module <a href=
+    "http://search.cpan.org/~srezic/Tk-804.028/">Tk</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> <code>fcm gui</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> RHEL-6.1: 804.028.</p>
+    </dd>
+
+    <dt><a href="http://subversion.apache.org/">Subversion</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the code management commands, the extract system
+      of <code>fcm make</code>, the deprecated <code>fcm extract</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> RHEL-6.1: 1.6.17.</p>
+
+      <p><dfn>remark:</dfn> you can use the extract system to mirror code to a
+      remote platform for building. Therefore it is only necessary to have
+      Subversion installed on the platform where you do your code development.
+      If you use other platforms purely for building and running then you do
+      not need to have Subversion installed on these platforms.</p>
+    </dd>
+
+    <dt><a href="http://trac.edgewall.org/">Trac</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> (optional, but highly recommended as a companion
+      to Subversion)</p>
+
+      <p><dfn>versions at Met Office:</dfn> 0.11.7.</p>
+    </dd>
+
+    <dt><a href="http://furius.ca/xxdiff/">xxdiff</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> <code>fcm branch-diff --graphical</code>,
+      <code>fcm conflicts</code>, <code>fcm diff --graphical</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> RHEL-6.1: 3.2.</p>
+
+      <p><dfn>remark:</dfn> The <code>fcm branch-diff --graphical</code> and
+      <code>fcm diff --graphical</code> commands use xxdiff by default but can
+      also use other graphical diff tools.</p>
+    </dd>
+
+    <dt><a href="http://www.gzip.org/">gzip</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> <code>fcm make</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> AIX-5.3: 1.2.4, RHEL-6.1: 1.3.12.</p>
+    </dd>
+
+    <dt><a href="http://www.gnu.org/software/diffutils/">GNU diffutils</a>:
+    diff3</dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the extract system of <code>fcm make</code>, the
+      deprecated <code>fcm extract</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> RHEL-6.1: 2.8.1.</p>
+
+      <p><dfn>remark:</dfn>: used to merge changes to source files modified by
+      2+ diff source trees (compared with the base).</p>
+    </dd>
+
+    <dt><a href="http://rsync.samba.org/">rsync</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the mirror system of <code>fcm make</code>, the
+      deprecated <code>fcm extract</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> AIX-5.3: 2.6.2, RHEL-6.1: 3.0.6.</p>
+
+      <p><dfn>remark:</dfn> used to mirror source file to another
+      <var>USER@HOST</var>.</p>
+    </dd>
+
+    <dt><a href="http://www.gnu.org/software/make/make.html">GNU make</a></dt>
+
+    <dd>
+      <p><dfn>used by:</dfn> the deprecated <code>fcm build</code>.</p>
+
+      <p><dfn>versions at Met Office:</dfn> AIX-5.3: 3.80, RHEL-6.1: 3.81.</p>
+    </dd>
+  </dl>
+
+  <h2 id="installation">Installation</h2>
+
+  <p>FCM is distributed in the form of a compressed tar file. Un-pack the tar
+  file into an appropriate location on your system. Add the <samp>bin/</samp>
+  directory into your <var>PATH</var> environment variable. Once you have done
+  this you should now have full access to the FCM system, assuming that you
+  have met the requirements described in the previous section.</p>
+
+  <p>You should find the following contents in the distribution:</p>
+
+  <dl>
+    <dt>README</dt>
+
+    <dd>The README file contains the internal revision number of the
+    release.</dd>
+
+    <dt>COPYRIGHT.txt<br />
+    LICENSE.html</dt>
+
+    <dd>The FCM license and other copyright information.</dd>
+
+    <dt>bin/</dt>
+
+    <dd>Contains the <code>fcm</code> command and other utilities.</dd>
+
+    <dt>doc/</dt>
+
+    <dd>System documentation.</dd>
+
+    <dt>doc/release_notes/</dt>
+
+    <dd>Contains these release notes. It also contains the release notes for
+    all previous versions which may be useful if you have skipped any
+    versions.</dd>
+
+    <dt>doc/user_guide/</dt>
+
+    <dd>Contains the <a href="../user_guide/">FCM User Guide</a>.</dd>
+
+    <dt>doc/standards/</dt>
+
+    <dd>Contains the FCM <a href="../standards/perl_standard.html">Perl</a> and
+    <a href="../standards/fortran_standard.html">Fortran</a> coding standards.
+    The Perl standard describes the standards followed by the FCM code. The
+    Fortran standard contains some <a href=
+    "../standards/fortran_standard.html#fcm">specific advice</a> on the best
+    way of writing Fortran code for use with FCM as well as more general advice
+    on good practice.</dd>
+
+    <dt>doc/collaboration/</dt>
+
+    <dd>Contains the <a href="../collaboration/index.html">External
+    Distribution &amp; Collaboration for FCM Projects</a> document which
+    discusses how projects configured under FCM can be distributed
+    externally.</dd>
+
+    <dt>etc/</dt>
+
+    <dd>Miscellaneous items, including the <samp>fcm/keyword.cfg.eg</samp>
+    file. If you wish to define keywords for your site you will need to create
+    the <samp>etc/fcm/keyword.cfg</samp> file. An example file,
+    <samp>fcm/keyword.cfg.eg</samp>, is provided which is a copy of the file
+    currently used at the Met Office. For further details please refer to the
+    section <a href="../user_guide/system_admin.html#fcm-keywords">FCM
+    keywords</a> in the System Admin chapter of the User Guide.</dd>
+
+    <dt>examples/</dt>
+
+    <dd>Contains various example scripts which you may find useful. Note that
+    these scripts are all specific to the Met Office and may contain hard coded
+    paths and email addresses. They are provided in the hope that you may find
+    them useful as examples for setting up similar scripts of your own.
+    However, they should only be used after careful review to adapt them to
+    your environment.</dd>
+
+    <dt>examples/etc/regular-update.eg</dt>
+
+    <dd>An example of how you might set up a cron job to make use of the
+    <samp>&lt;repos&gt;.latest</samp> file (see
+    <code>examples/svn-hooks/post-commit-background</code>).</dd>
+
+    <dt>examples/lib/</dt>
+
+    <dd>Contains the <code>FCM::Admin::*</code> Perl library, which implements
+    the functionalities of the FCM admin utility commands.</dd>
+
+    <dt>examples/sbin/</dt>
+
+    <dd>Contains a selection of useful admin utility commands.</dd>
+
+    <dt>examples/svn-hooks/pre-commit</dt>
+
+    <dd>
+      This script restricts write-access to the repository by checking the
+      following:
+
+      <ul>
+        <li>It executes the Subversion utility <code>svnperms.py</code> if it,
+        and the associated <samp>svnperms.conf</samp> file, exist. This utility
+        checks whether the author of the current transaction has enough
+        permission to write to particular paths in the repository.</li>
+
+        <li>It checks the disk space required by the current transaction. It
+        fails the commit if it requires more than 10MB of disk space (or
+        whatever is specified in the
+        <code>pre-commit-size-threshold.conf</code> file.</li>
+      </ul>
+    </dd>
+
+    <dt>examples/svn-hooks/post-commit</dt>
+
+    <dd>This script runs <code>post-commit-background</code> in the
+    background.</dd>
+
+    <dt>examples/svn-hooks/post-commit-background</dt>
+
+    <dd>
+      This script runs in the background after each commit.
+
+      <ul>
+        <li>It updates a <samp>&lt;repos&gt;.latest</samp> file with the latest
+        revision number.</li>
+
+        <li>It creates a dump of the new revision.</li>
+
+        <li>It calls <code>post-commit-background-custom</code> if it
+        exists.</li>
+      </ul>
+    </dd>
+
+    <dt>examples/svn-hooks/pre-revprop-change</dt>
+
+    <dd>This script only allows the modification of <var>svn:log</var>.</dd>
+
+    <dt>examples/svn-hooks/post-revprop-change</dt>
+
+    <dd>This script runs <code>post-revprop-change-background</code> in the
+    background.</dd>
+
+    <dt>examples/svn-hooks/post-revprop-change-background</dt>
+
+    <dd>This script invokes the <code>trac-admin</code> command to
+    <code>resync</code> the revision property cache stored in the corresponding
+    Trac environment. If a user modifies the log message of a changeset and
+    he/she is not the original author of the changeset, this script will e-mail
+    the original author. If the file
+    <code>post-revprop-change-background-cc.list</code> exits, the script will
+    also e-mail those in the list.</dd>
+
+    <dt>lib/</dt>
+
+    <dd>Contains the Perl library of FCM.</dd>
+
+    <dt>man/</dt>
+
+    <dd>Contains a basic manual page for <code>fcm</code>.</dd>
+
+    <dt>t/</dt>
+
+    <dd>Contains unit test for FCM.</dd>
+
+    <dt>test/</dt>
+
+    <dd>Contains regression tests for FCM.</dd>
+
+    <dt>test/test_include/</dt>
+
+    <dd>Contains simple test code to check how your chosen compilers handle
+    include files (see <a href="#issues">Known Issues</a>).</dd>
+
+    <dt>tutorial/</dt>
+
+    <dd>Contains the files necessary to set up a Subversion repository for the
+    FCM tutorial. This will allow you to follow the <a href=
+    "../user_guide/getting_started.html#tutorial">tutorial section</a> in the
+    User Guide. See <samp>tutorial/README</samp> on how to set it up.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-3-1.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-3-1.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-3-1.html	(revision 5095)
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 2-3-1 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 2-3-1 Release Notes <small>2013-04-05</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM 2-3-1. This release of FCM is distributed
+  under the terms of the <a href="http://www.gnu.org/licenses/gpl.html"
+  rel="license">GNU General Public License</a>. See <a href=
+  "../etc/fcm-terms-of-use.html">Terms of Use</a> for detail.</p>
+
+  <p>If FCM is not yet installed at your site, please refer to <a href=
+  "../installation/">FCM: Installation</a> for detail.</p>
+
+  <h2>Contents</h2>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="changes-highlight">Highlight Changes</h2>
+
+  <p>This is a bug fix release.</p>
+
+  <h2 id="changes-minor">Minor Changes and Bug Fixes</h2>
+
+  <dl>
+    <dt>Configuration file syntax</dt>
+
+    <dd>If a setting accepts a space delimited list of locations, it is now
+    possible to use <var>$HERE</var> in front of any of the locations in the
+    list.</dd>
+
+    <dt>User configuration file location</dt>
+
+    <dd>The user configuration file should now be placed at
+    <var>$HOME/.metomi/fcm/</var>. The old location
+    <var>$HOME/.met-um/fcm/</var> is still supported but is deprecated.</dd>
+
+    <dt><code>fcm make</code></dt>
+
+    <dd>
+      <p>Reduced run time memory footprint.</p>
+
+      <p>Build: Create the <var>include/</var> sub-directory for <var>*.o</var>
+      targets of Fortran source files containing modules. Some compilers will
+      put the <var>*.mod</var> files directly into the <var>include/</var>
+      sub-directory.</p>
+
+      <p>Build: <var>ns-dep.o</var> property improved. Its value will now accept
+      namespaces at the source file level. It will also accept <kbd>/</kbd> as
+      the root namespace. (Previously, it required a pair of quotes
+      <kbd>""</kbd>.) A bad namespace in the value will now trigger a missing
+      dependency error.</p>
+
+      <p>Build: handle <var>.</var> and <var>..</var> in value of
+      <var>source</var> declaration.</p>
+    </dd>
+  </dl>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>See the <a href="../user_guide/make.html#build.inherit">User Guide &gt;
+    FCM Make &gt; Build &gt; Build Inheritance</a> for detail.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-3.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-3.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/2-3.html	(revision 5095)
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM 2-3 Release Notes</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM 2-3 Release Notes <small>26 October 2012</small></h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>These are the release notes for FCM 2-3. This release of FCM is distributed
+  under the terms of the <a href="http://www.gnu.org/licenses/gpl.html"
+  rel="license">GNU General Public License</a>. See <a href=
+  "../etc/fcm-terms-of-use.html">Terms of Use</a> for detail.</p>
+
+  <p>If FCM is not yet installed at your site, please refer to <a href=
+  "../installation/">FCM: Installation</a> for detail.</p>
+
+  <h2>Contents</h2>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="changes-highlight">Highlight Changes</h2>
+
+  <dl>
+    <dt><a href="http://www.gnu.org/licenses/gpl.html" rel="license">GNU General
+    Public License</a></dt>
+
+    <dd>
+      <p>From this release onwards, FCM will be released under the terms of the
+      GNU General Public License.</p>
+    </dd>
+
+    <dt><code>fcm make</code></dt>
+
+    <dd>
+      <p>Build system now recognises all text files starting with a
+      <code>#!</code> line as scripts.</p>
+
+      <p>Build system target selection logic has been improved to work more
+      efficiently, and to work correctly with a complex case of non-fatal cyclic
+      dependency.</p>
+    </dd>
+  </dl>
+
+  <h2 id="changes-minor">Minor Changes and Bug Fixes</h2>
+
+  <dl>
+    <dt><code>fcm branch-create</code></dt>
+
+    <dd>
+      <p>Fixed: no longer issue an incorrect error when creating a branch for a
+      project that resides at the root level of a repository.</p>
+    </dd>
+
+    <dt><code>fcm make</code></dt>
+
+    <dd>
+      <p>The behaviour of the <code>--new</code> option is modified to remove
+      only step directories defined in the <code>steps</code> declaration
+      instead of all known steps in the configuration.</p>
+
+      <p>Mirror: write to the mirror target a <samp>fcm-make.cfg.orig</samp> file
+      containing the configuration of the original <code>fcm make</code>
+      invocation.</p>
+    </dd>
+
+    <dt><code>fcm loc-layout</code></dt>
+
+    <dd>
+      <p>New command to parse a Subversion target and print the FCM layout
+      information of its URL.</p>
+
+    <dt><code>fcm mkpatch</code></dt>
+
+    <dd>
+      <p>Import scripts now uses <code>/bin/bash</code> instead of
+      <code>/bin/sh</code>.</p>
+    </dd>
+  </dl>
+
+  <h2 id="issues">Known Issues</h2>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>See the <a href="../user_guide/make.html#build.inherit">User Guide &gt;
+    FCM Make &gt; Build &gt; Build Inheritance</a> for detail.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/index.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/index.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/release_notes/index.html	(revision 5095)
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM Release Notes (Old)</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a href="../user_guide/">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <!--div class="fcm-page-content pull-right well well-sm"></div-->
+    <h1>FCM Release Notes (Old)</h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>Current release notes can be found at
+  <a href="https://github.com/metomi/fcm/blob/master/CHANGES.md">FCM Changes</a>
+  or the <var>CHANGES.md</var> file of your local installation of FCM.</p>
+
+  <p>Old release notes:</p>
+
+  <dl>
+    <dt><a href="2-3-1.html">2-3-1</a></dt>
+
+    <dd>Released on 2013-04-05. Bug fix release.</dd>
+
+    <dt><a href="2-3.html">2-3</a></dt>
+
+    <dd>Released on 2012-10-26. GPL release.</dd>
+
+    <dt><a href="2-2.html">2-2</a></dt>
+
+    <dd>Released on 2012-06-15. Subversion 1.6 upgrade.</dd>
+
+    <dt><a href="2-1.html">2-1</a></dt>
+
+    <dd>Released on 2011-07-22. Bug fix release.</dd>
+
+    <dt><a href="2-0.html">2-0</a></dt>
+
+    <dd>Released on 2011-03-11. New extract and build system.</dd>
+
+    <dt><a href="1-5.html">1-5</a></dt>
+
+    <dd>Released on 2010-01-22. New features and bug fixes.</dd>
+
+    <dt><a href="1-4.html">1-4</a></dt>
+
+    <dd>Released on 2009-02-12. Bug fix release.</dd>
+
+    <dt><a href="1-3.html">1-3</a></dt>
+
+    <dd>Released on 2008-01-30. Major update to the extract and build
+    systems.</dd>
+
+    <dt><a href="1-2.html">1-2</a></dt>
+
+    <dd>Released on 2007-03-22. Bug fix release.</dd>
+
+    <dt><a href="1-1.html">1-1</a></dt>
+
+    <dd>Released on 2006-11-06. First external release of FCM.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/annex_cfg.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/annex_cfg.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/annex_cfg.html	(revision 5095)
@@ -0,0 +1,1538 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM: User Guide: Annex: FCM Configuration File</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a class="active" href="#">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM: User Guide: Annex: FCM Configuration File</h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <h2 id="syntax">Syntax</h2>
+
+  <h3 id="syntax.comment">Syntax: comment</h3>
+
+  <p>An empty line, a line with only space characters and a line beginning with
+  a <kbd>#</kbd> character is a comment line. For example:</p>
+  <pre>
+# This is a comment.
+    # This is also a comment.
+</pre>
+
+  <p>Also, if a line contains a space character followed by a <kbd>#</kbd>
+  character then this, and any following characters, are treated as an end of
+  line comment and are ignored.</p>
+
+  <h3 id="syntax.declaration">Syntax: declaration</h3>
+
+  <p>The general syntax of a declaration consists of a label, a set of
+  modifiers, a list of name-spaces, and a value:</p>
+  <pre>
+label{mod:mod-value}[ns] = value
+label{mod:mod-value} = value
+label[ns] = value
+label = value
+label = value\
+    value # "value    value"
+label = value\
+    \value # "valuevalue"
+</pre>
+
+  <p>The label is compulsory. It defines the declaration, and may have one or
+  more characters in the alpha-numeric, the underscore, the minus sign and the
+  full stop. The label cannot be substituted with a variable.</p>
+
+  <p>The modifier is an optional part of the syntax, but may be a compulsory
+  part of some declarations. The modifiers are embedded between a pair of curly
+  braces and must come after the label, but before the name-spaces and the
+  equal sign. It should be a comma-separated list of colon-separated key-value
+  pairs, (i.e. <kbd>{key1: value1, key2: value2, ...}</kbd>). The value in each
+  modifier is optional - if a key is set without a value, its value is assumed
+  to be 1. The contents of the modifiers can be substituted using a
+  variable.</p>
+
+  <p>The name-space is an optional part of the syntax, but may be a compulsory
+  part of some declarations. The name-spaces are embedded between a pair of
+  square braces and must come after the label and/or modifiers but before the
+  equal sign. It should be a space-separated list of names. If a name contains
+  space, the space character can be escaped using a backslash character or the
+  name can be quoted. The contents of the name-spaces can be substituted using
+  a variable.</p>
+
+  <p>The first non-space character after the equal sign begins the value of the
+  declaration. If there is nothing after the equal sign, the value is an empty
+  string. The contents of the value can be substituted using a variable.
+  Trailing space characters are ignored. The end of line can be escaped using a
+  backslash, and the value of the declaration will continue from the next
+  non-comment line. If the first non-space character on that line is a
+  backslash then the line contents up to and including that backslash are
+  ignored.</p>
+
+  <h3 id="syntax.variable">Syntax: variable</h3>
+
+  <p>A variable is used to substitute commonly used values in a declaration. It
+  can refer to an environment variable or can be defined locally using the
+  syntax:</p>
+  <pre>
+$var = value    # Sets "var" variable to "value"
+$var{?} = value # Sets "var" variable to "value" only if "var" is not defined
+# E.g.:
+$projects = foo bar baz
+$long_path = src/path/is/very/very/very/long
+$UMDIR{?} = /projects/um1
+# time passes...
+extract.ns = $projects
+extract.path-excl[$projects egg ham] = doc man ${long_path}_longer
+build.prop{cc.defs} = foo=\$foo # escape substitution
+</pre>
+
+  <p>The name of a variable must begin with an alphabet or an underscore and
+  followed by 0 or more alpha-numeric or underscore characters, (i.e. it must
+  match this regular expression: <var>^[A-Za-z_][A-Za-z0-9_]*$</var>).</p>
+
+  <p>Locally defined variables only apply in the context of the configuration
+  file and are not passed down, for instance, to the compiler. If there is an
+  environment variable with the same identifier, its value is normally
+  overridden (within the context of the configuration file) by any local
+  setting. However, if a <kbd>?</kbd> modifier is given, it assigns the value
+  to the variable only if the variable is not already defined (either as an
+  environment variable or locally). Substitution can be escaped using a
+  backslash character in front of the dollar sign. Any attempt to reference an
+  undefined variable will trigger an exception.</p>
+
+  <p>Note: <var>$HERE</var> is a reserved variable to indicate the parent
+  directory of the current configuration file. (The system ignores the $HERE
+  environment variable, if there is one.) Any attempt to assign a value to
+  <var>$HERE</var> will trigger an exception.</p>
+
+  <h3 id="syntax.include">Syntax: include</h3>
+
+  <p>An <code>include</code> declaration specifies the logical locations of a
+  list of configuration files, where contents are to be included inline:</p>
+  <pre>
+include{type:type} = location ...
+# E.g.:
+include = fcm:foo/path/to/config/file
+include = svn://server/path/to/config/file@1234
+include{type:svn} = http://server/path/to/config/file
+include = /path/to/config/file $HERE/another-config-file
+include = $HERE/another-config-file
+include = ~/path/to/config/file ~fred/path/to/config/file
+</pre>
+
+  <p>If the value of an <code>include</code> declaration is a relative path,
+  the system will search the directory containing the current configuration
+  file for the include file. More include search paths can be specified using
+  the <code>include-path</code> declaration. E.g.:</p>
+
+  <pre>
+# Define or replace include search paths
+include-path=/path/to/some/cfg /path/to/more/cfg
+# Append to include search paths
+include-path{+}=host2:/path/to/cfg
+include-path{type:svn,+}=https://host1/path/to/cfg
+# ... Include files can now be relative paths
+include=foo.cfg bar.cfg
+</pre>
+
+  <p>Some commands, e.g. <code>fcm make</code>, accept one or more
+  <code>--config-file-path=PATH</code> command line option, which can be used
+  to pre-define the include search paths when they read their configuration
+  files.</p>
+
+  <p>Currently, the location can be a path in the file system, a Subversion URL,
+  a FCM keyword pointing to a Subversion URL, or a location in the file system
+  of a remote host accessible via passphrase-less SSH. The system will attempt
+  to make an intelligent guess of the location type. To allow for future
+  expansion, both the <code>include</code> and the <code>include-path</code>
+  declarations accept a <var>type:TYPE</var> modifier (where TYPE can currently
+  be <samp>fs</samp> for a file system path or <samp>svn</samp> for a Subversion
+  location) to allow the location type to be defined where it is ambiguous.</p>
+
+  <h2 id="keyword">FCM Keyword Configuration</h2>
+
+  <p>The keyword configuration files are mainly used to define FCM location
+  keywords and related settings. The <code>fcm</code> command searches for
+  keyword configuration files from the following locations:</p>
+
+  <ol>
+    <li><dfn>(Deprecated)</dfn> <samp>$HOME/.fcm</samp> - expects FCM 1
+    configuration file format. See <a href="annex_fcm_cfg.html">Annex:
+    Declarations in FCM 1 central/user configuration file</a> for detail.</li>
+
+    <li><samp>$FCM/etc/fcm/keyword.cfg</samp> where <var>$FCM/bin/</var> is the
+    path at which <code>fcm</code> is installed.</li>
+
+    <li><samp>$HOME/.metomi/fcm/keyword.cfg</samp>.</li>
+  </ol>
+
+  <p>The following are declarations recognised by the keyword configuration
+  files:</p>
+
+  <dl>
+    <dt id="keyword.location">location</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Associates a namespace with a location, and
+      allows the use of <samp>fcm:namespace</samp> as a substitute of the
+      location.</p>
+
+      <p><dfn>modifier</dfn>: <var>primary</var>: Optional. If specified, the
+      location is a primary location, i.e. the system will create the
+      <samp>_tr</samp>, <samp>_br</samp>, <samp>_tg</samp>, <samp>-tr</samp>,
+      <samp>-br</samp> and <samp>-tg</samp> keywords for this location.</p>
+
+      <p><dfn>modifier</dfn>: <var>type</var>: Optional. Specifies the type of
+      the location. The system currently supports <samp>svn</samp> for a
+      Subversion location and <samp>fs</samp> for a file system location. If
+      not specified, the system will make an intelligent guess based on the
+      given value.</p>
+
+      <p><dfn>namespace</dfn>: The namespace to be associated with the
+      location.</p>
+
+      <p><dfn>value</dfn>: A valid location of a type supported by the
+      system.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+location{primary}[var] = svn://fcm5/VAR_svn/VAR
+location{primary, type:svn}[egg] = http://chicken/egg
+</pre>
+    </dd>
+
+    <dt id="keyword.revision">revision</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Associates a keyword with a revision of a
+      location in a version control system.</p>
+
+      <p><dfn>namespace</dfn>: The namespace of a location (already defined
+      using the <code>location</code> declaration), followed by a colon and the
+      namespace of the revision.</p>
+
+      <p><dfn>value</dfn>: A valid revision of the location.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+revision[um:vn7.5] = 18479
+revision[egg:free-range] = 1
+</pre>
+    </dd>
+
+    <dt id="keyword.browser.comp-pat">browser.comp-pat</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies a regular expression to capture
+      components in the scheme-specific part of a version control system
+      location (already defined using the <code>location{primary}</code>
+      declaration). These components can then be used in the
+      <code>browser.loc-tmpl</code> template string.</p>
+
+      <p><dfn>namespace</dfn>: Optional. The namespace of a location. If not
+      specified, the declaration applies globally.</p>
+
+      <p><dfn>value</dfn>: A valid regular expression.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+browser.comp-pat = (?msx-i:\A // ([^/]+) /+ ([^/]+)_svn /*(.*) \z) # default
+browser.comp-pat[egg] = (?msx-i:\A//([^/]+)/(.*)\z)
+</pre>
+    </dd>
+
+    <dt id="keyword.browser.loc-tmpl">browser.loc-tmpl</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies a template string, in which the
+      components captured by the <code>browser.comp.pat</code> regular
+      expression are used to fill in the numbered fields. The template should
+      have one more field than the number of components captured by
+      <code>browser.comp-pat</code>. The final field is used to place the
+      revision, which is generated via the <code>browser.rev-tmpl</code>.</p>
+
+      <p><dfn>namespace</dfn>: Optional. The namespace of a location. If not
+      specified, the declaration applies globally.</p>
+
+      <p><dfn>value</dfn>: A valid template.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+browser.loc-tmpl = http://{1}/projects/{2}/intertrac/source:/{3}{4} # default
+browser.loc-tmpl[egg] = http://{1}/intertrac/source:/{3}{4}
+</pre>
+    </dd>
+
+    <dt id="keyword.browser.rev-tmpl">browser.rev-tmpl</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies a template string, which should have
+      a single numbered field for filling in the revision number.</p>
+
+      <p><dfn>namespace</dfn>: Optional. The namespace of a location. If not
+      specified, the declaration applies globally.</p>
+
+      <p><dfn>value</dfn>: A valid template.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+browser.rev-tmpl = @{1} # default
+</pre>
+    </dd>
+  </dl>
+
+  <h2 id="external">FCM External Configuration</h2>
+
+  <p>The external configuration files are used to define the name of external
+  commands used by the FCM system. The <code>fcm</code> command searches for
+  external configuration files from the following locations:</p>
+
+  <ol>
+    <li><samp>$FCM/etc/fcm/external.cfg</samp> where <var>$FCM/bin/</var> is
+    the path at which <code>fcm</code> is installed.</li>
+
+    <li><samp>$HOME/.metomi/fcm/external.cfg</samp>.</li>
+  </ol>
+
+  <p>The following are declarations recognised by the external configuration
+  files:</p>
+
+  <dl>
+    <dt id="external.browser">browser</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the command to invoke the web
+      browser. (default=<samp>firefox</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+browser = konqueror
+</pre>
+    </dd>
+
+    <dt id="external.diff3">diff3</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: The shell command used by the extract system of
+      FCM Make to perform a 3-way merge. (default=<samp>diff3</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+diff3 = diff3
+</pre>
+    </dd>
+
+    <dt id="external.diff3.flags">diff3.flags</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: The options used by the 3-way merge shell
+      command. (default=<samp>-E -m</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+diff3.flags = -E -m
+</pre>
+    </dd>
+
+    <dt id="external.graphic-diff">graphic-diff</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the command to invoke the graphic
+      diff tool. (default=<samp>xxdiff</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+graphic-diff = tkdiff
+</pre>
+    </dd>
+
+    <dt id="external.graphic-merge">graphic-merge</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the command to invoke the graphic
+      merge tool. (default=<samp>xxdiff</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+graphic-merge = xxdiff
+</pre>
+    </dd>
+
+    <dt id="external.ssh">ssh</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: The secure remote shell command to execute
+      commands on a remote host.  (default=<samp>ssh</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+ssh = ssh
+</pre>
+    </dd>
+
+    <dt id="external.ssh.flags">ssh.flags</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: The options used by the secure shell command to
+      execute commands on a remote host. (default=<samp>-n
+      -oBatchMode=yes</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+ssh.flags = -n -oBatchMode=yes
+</pre>
+    </dd>
+
+    <dt id="external.rsync">rsync</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: The <code>rsync</code> command.
+      (default=<samp>rsync</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+rsync = rsync
+</pre>
+    </dd>
+
+    <dt id="external.rsync.flags">rsync.flags</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: The options used by the <code>rsync</code>
+      command. (default=<samp>-a --exclude=.* --delete-excluded --timeout=900
+      --rsh=ssh\ -oBatchMode=yes</samp>)</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+rsync.flags = -a --exclude=.* --delete-excluded --timeout=900 --rsh=ssh\ -oBatchMode=yes
+</pre>
+    </dd>
+  </dl>
+
+  <h2 id="make">FCM Make Configuration</h2>
+
+  <p>A typical FCM make configuration consists of some top level declarations
+  to define the make, and specific declarations for each step. The top level
+  declarations are described below, and the specific declarations for each type
+  of step will be described in the sub-sections to follow.</p>
+
+  <p>Note:</p>
+
+  <ul>
+    <li>Unless stated otherwise, FCM make configuration declarations are
+    non-cumulative, i.e. if more than one declarations apply to the
+    <dfn>same</dfn> configuration, the value of the last declaration overrides
+    those of the earlier ones. E.g.:
+      <pre>
+# Sets the Fortran compiler flags for the root name-space
+build.prop{fc.flags} = -O3
+# build.prop{fc.flags}[/] = -O3
+
+# Sets the C/Fortran compiler flags for the "foo/bar" name-space
+build.prop{cc.flags, fc.flags}[foo/bar] = -g -C
+# build.prop{cc.flags}[foo/bar] = -g -C
+# build.prop{fc.flags}[/] = -O3
+# build.prop{fc.flags}[foo/bar] = -g -C
+
+# Sets the Fortran compiler flags for the root and "foo/bar" name-spaces
+build.prop{fc.flags}[/ foo/bar] = -O2
+# build.prop{cc.flags}[foo/bar] = -g -C
+# build.prop{fc.flags}[/] = -O2
+# build.prop{fc.flags}[foo/bar] = -O2
+
+# Sets the Fortran compiler flags for the "foo/bar" name-spaces
+build.prop{fc.flags}[foo/bar] = -O1
+# build.prop{cc.flags}[foo/bar] = -g -C
+# build.prop{fc.flags}[/] = -O2
+# build.prop{fc.flags}[foo/bar] = -O1
+</pre>
+    </li>
+
+    <li>Unless stated otherwise, declarations are inherited.</li>
+
+    <li>The default values of the property settings of each step class (e.g.
+    <code>build.prop{fc}</code>) can be modified in:
+
+      <ol>
+        <li>The site configuration file <samp>$FCM/etc/fcm/make.cfg</samp>
+        where <var>$FCM/bin/</var> is the path at which <code>fcm</code> is
+        installed.</li>
+
+        <li>The user configuration file
+        <samp>$HOME/.metomi/fcm/make.cfg</samp>.</li>
+
+        <li><samp>*.prop{class,*}</samp> declarations in the FCM make
+        configuration file of the current session.</li>
+      </ol>
+
+      <p>The syntax is identical to a normal FCM make configuration file
+      declaration except that:</p>
+
+      <ul>
+        <li>Only <samp>*.prop{*}</samp> declarations are accepted.</li>
+
+        <li>The settings apply to a step class, not an individual step.</li>
+
+        <li>Name-space is not allowed.</li>
+      </ul>
+    </li>
+  </ul>
+
+  <p>For further details, please refer to the chapter on <a href=
+  "make.html">FCM Make</a>.</p>
+
+  <dl>
+    <dt id="make.description">description</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies a description string for the current
+      make.</p>
+
+      <p><dfn>value</dfn>: A string.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+description = Release 2.71.82
+</pre>
+    </dd>
+
+    <dt id="make.dest">dest</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the output destination of the make
+      system. If not specified, the system assumes the output destination to be
+      the current working directory. This setting is not inherited.</p>
+
+      <p><dfn>value</dfn>: A writable directory path.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+dest = path
+# E.g.:
+dest = $HERE
+</pre>
+    </dd>
+
+    <dt id="make.name">name</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the NAME of the make, so that the
+      command will search for <q>fcm-make<var>NAME</var>.cfg</q> instead of
+      <q>fcm-make.cfg</q>, and will write context files in
+      <q>.fcm-make<var>NAME</var>/</q> and log files as
+      <q>fcm-make<var>NAME</var>.log</q>, etc.</p>
+
+      <p><dfn>value</dfn>: A file name friendly string.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+name = -friend
+</pre>
+    </dd>
+
+    <dt id="make.require-version">require-version</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the minimum FCM version and
+      optionally the maximum FCM version that can be used on the current
+      configuration. If specified, FCM make fails immediately if its version
+      does not meet the criteria.</p>
+
+      <p><dfn>value</dfn>: The minimum version required, and optionally the
+      maximum version required.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+# Requires minimum FCM release at 2016.02.0
+require-version = 2016.02.0
+# Requires minimum FCM release at 2016.02.0, and maximum at 2038.01.0
+require-version = 2016.02.0 2038.01.0
+</pre>
+    </dd>
+
+    <dt id="make.steps">steps</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies a list of steps for the make system
+      to invoke. Each specified step can either be the ID of a known
+      sub-system, i.e. <samp>extract</samp>, <samp>mirror</samp>,
+      <samp>preprocess</samp> and <samp>build</samp>, or an ID defined using
+      the <code>step.class</code> declaration. (See below.)</p>
+
+      <p><dfn>value</dfn>: A list of steps for the make system to invoke.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+steps = id-1 id-2 ...
+# E.g.:
+steps = extract mirror
+</pre>
+    </dd>
+
+    <dt id="make.step.class">step.class</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Defines custom step IDs. This allows built-in
+      steps to be renamed, or re-used in multiple steps.</p>
+
+      <p><dfn>namespace</dfn>: Specifies the step IDs.</p>
+
+      <p><dfn>value</dfn>: An ID of a known sub-system, i.e.
+      <samp>extract</samp>, <samp>mirror</samp>, <samp>preprocess</samp> and
+      <samp>build</samp>.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+step.class[id ...] = id-of-class
+# E.g.:
+step.class[build-qxrecon build-ieee] = build
+</pre>
+    </dd>
+
+    <dt id="make.use">use</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies inheritance locations. It is worth
+      noting that this declaration is cumulative, i.e. a <code>use</code>
+      declaration adds more inheritance locations rather than overrides the
+      inheritance locations of any previous <code>use</code> declarations.</p>
+
+      <p><dfn>value</dfn>: A list of paths where this make can inherit items
+      and properties from.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+use = path1 path2 ...
+E.g.:
+use = /path/to/inherit
+</pre>
+    </dd>
+  </dl>
+
+  <h3 id="make.extract">FCM Make Configuration: Extract</h3>
+
+  <p>All declarations are prefixed with <samp>extract.*</samp>. Where
+  appropriate, replace the leading <samp>extract</samp> with the appropriate
+  step ID. With the exception of the <code>extract.location{diff}</code>
+  declaration, any attempt to specify any extract declarations in an inherited
+  extract will result in an exception.</p>
+
+  <dl>
+    <dt id="make.extract.ns">extract.ns</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies a list of names of the projects to
+      extract.</p>
+
+      <p><dfn>value</dfn>: A space-delimited list of names of the projects to
+      extract.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+extract.ns = gen ops um var ver
+</pre>
+    </dd>
+
+    <dt id="make.extract.location">extract.location</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the location of the base source tree
+      a project. If the base source tree is not specified for a Subversion
+      project, the system will try to use <samp>trunk@HEAD</samp>.</p>
+
+      <p><dfn>modifier</dfn>: <var>type</var>: Specifies the type of the
+      locations. The system currently supports <samp>svn</samp> for a Subversion
+      location, <samp>ssh</samp> for a file system on a remote host accessible
+      via SSH and RSYNC, and <samp>fs</samp> for a file system location.
+      (default=<samp>svn</samp> if the location is recognised as a Subversion
+      URL, <samp>ssh</samp> if the location has the form
+      <var>[USER@]HOST:PATH</var> and <var>HOST</var> is a valid remote host,
+      <samp>fs</samp> otherwise)</p>
+
+      <p><dfn>namespace</dfn>: A space-delimited list of project IDs.</p>
+
+      <p><dfn>value</dfn>: A location, which can be a full path in the file
+      system, or a relative location of the project root.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+extract.location[um] = trunk@vn7.5
+extract.location[gen ops var] = trunk@HEAD
+</pre>
+    </dd>
+
+    <dt id="make.extract.location.diff">extract.location{diff}</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the location(s) of one or more diff
+      source trees of a project. It is OK to add more diff source trees in an
+      inheriting extract.</p>
+
+      <p><dfn>modifier</dfn>: <var>type</var>: -as above-</p>
+
+      <p><dfn>namespace</dfn>: A project ID.</p>
+
+      <p><dfn>value</dfn>: A space-delimited list of locations. A location can
+      be a full path in the file system, or a relative location of the project
+      root.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+extract.location{diff}[gen] = \
+    branches/dev/fred/r12345_678 \
+    branches/dev/free/r12345_678@123
+</pre>
+    </dd>
+
+    <dt id="make.extract.location.primary">extract.location{primary}</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the project locations for an
+      extract, if they are not already defined in the keyword
+      configuration.</p>
+
+      <p><dfn>modifier</dfn>: <var>type</var>: -as above-</p>
+
+      <p><dfn>namespace</dfn>: A project ID.</p>
+
+      <p><dfn>value</dfn>: A project location. The location can be a path in
+      the file system, a Subversion URL or a FCM keyword pointing to a
+      Subversion URL.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+extract.location{primary}[egg] = svn://server2/egg
+extract.location{primary,type:svn}[foo] = http://server1/foo
+</pre>
+    </dd>
+
+    <dt id="make.extract.path-excl">extract.path-excl / <span id=
+    "make.extract.path-incl">extract.path-incl</span></dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the paths in a project tree to
+      exclude/include. (A path-incl takes precedence over a path-excl
+      declaration.)</p>
+
+      <p><dfn>namespace</dfn>: A space-delimited list of project IDs.</p>
+
+      <p><dfn>value</dfn>: A space-delimited list of paths in a project
+      tree.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+extract.path-excl[foo] = / # everything
+extract.path-incl[foo] = bin lib src/code
+</pre>
+    </dd>
+
+    <dt id="make.extract.path-root">extract.path-root</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the root paths in the source trees
+      of the projects to be extracted. By default, the root path is the
+      root of a source tree. If this setting is specified, the system will only
+      extract source files under the specified root path. The system will also
+      adjust the name-space of each source file to be relative to the specified
+      root path. The <code>extract.path-excl</code> and
+      <code>extract.path-incl</code> declarations will apply from the level of
+      the specified root path.</p>
+
+      <p><dfn>namespace</dfn>: A space-delimited list of project IDs.</p>
+
+      <p><dfn>value</dfn>: A relative path in the project source trees.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+extract.path-root[gen] = src/code
+</pre>
+    </dd>
+
+    <dt id="make.extract.prop">extract.prop</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Sets or modifies a property.</p>
+
+      <p><dfn>modifier</dfn>: <var>diff3</var>: The shell command to perform a
+      3-way merge. If not specified, use the <a href="#external.diff3">diff3</a>
+      setting of the FCM external configuration.</p>
+
+      <p><dfn>modifier</dfn>: <var>diff3.flags</var>: The options used by the
+      3-way merge shell command. If not specified, use the <a href=
+      "#external.diff3.flags">diff3.flags</a> setting of the FCM external
+      configuration.</p>
+
+      <p><dfn>value</dfn>: The value of the property.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+extract.prop{diff3} = diff3
+extract.prop{diff3.flags} = -E -m
+</pre>
+    </dd>
+  </dl>
+
+  <h3 id="make.mirror">FCM Make Configuration: Mirror</h3>
+
+  <p>All declarations are prefixed with <samp>mirror.*</samp>. Where
+  appropriate, replace the leading <samp>mirror</samp> with the appropriate
+  ID.</p>
+
+  <dl>
+    <dt id="make.mirror.target">mirror.target</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the mirror target. This setting is
+      not inherited.</p>
+
+      <p><dfn>value</dfn>: The mirror target.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+mirror.target = fred@server:/path/of/dest
+
+# Or if the current user ID is "fred"
+# Or if the user ID for "server" is configured under "~/.ssh/config"
+mirror.target = server:/path/of/dest
+
+# Or if mirroring to a path accessible only by user ID "bob"
+mirror.target = bob@localhost:/path/of/dest
+
+# Or if the current user ID is "fred" and local host is "server"
+mirror.target = /path/of/dest
+</pre>
+    </dd>
+
+    <dt id="make.mirror.prop">mirror.prop</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Sets or modifies a property.</p>
+
+      <p><dfn>modifier</dfn>: See below.</p>
+
+      <p><dfn>value</dfn>: The value of the property.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+mirror.prop{rsync} = cnysr
+</pre>
+    </dd>
+  </dl>
+
+  <p>The following is a list of properties accepted by the
+  <code>mirror.prop</code> declaration:</p>
+
+  <dl>
+    <dt id="make.mirror.prop.config-file.name">config-file.name</dt>
+
+    <dd>The context name to be included in the generated configuration file in
+    the mirror destination.</dd>
+
+    <dt id="make.mirror.prop.config-file.steps">config-file.steps</dt>
+
+    <dd>The steps to be included in the generated configuration file in the
+    mirror destination.</dd>
+
+    <dt id="make.mirror.prop.no-config-file">no-config-file</dt>
+
+    <dd>If specified, do not generate a <samp>fcm-make.cfg</samp> in the mirror
+    target.</dd>
+
+    <dt id="make.mirror.prop.ssh">ssh</dt>
+
+    <dd>The secure remote shell command to execute commands on a remote machine.
+    If not specified, use the <a href= "#external.ssh">ssh</a> setting of the
+    FCM external configuration.</dd>
+
+    <dt id="make.mirror.prop.ssh.flags">ssh.flags</dt>
+
+    <dd>The flags used by the remote shell command to execute commands on a
+    remote machine. If not specified, use the <a href=
+    "#external.ssh.flags">ssh.flags</a> setting of the FCM external
+    configuration.</dd>
+
+    <dt id="make.mirror.prop.rsync">rsync</dt>
+
+    <dd>The <code>rsync</code> command. If not specified, use the <a href=
+    "#external.rsync">rsync</a> setting of the FCM external configuration.</dd>
+
+    <dt id="make.mirror.prop.rsync.flags">rsync.flags</dt>
+
+    <dd>The options used by the <code>rsync</code> command. If not specified,
+    use the <a href= "#external.rsync.flags">rsync.flags</a> setting of the FCM
+    external configuration.</dd>
+  </dl>
+
+  <h3 id="make.build">FCM Make Configuration: Build</h3>
+
+  <p>All declarations are prefixed with <samp>build.*</samp>. Where
+  appropriate, replace the leading <samp>build</samp> with the appropriate
+  ID.</p>
+
+  <dl>
+    <dt id="make.build.source">build.source</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies one or more source files and/or
+      directories.</p>
+
+      <p><dfn>namespace</dfn>: Optional. Specifies the namespace of the
+      specified paths.</p>
+
+      <p><dfn>value</dfn>: A list of space-delimited file system paths.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+build.source[foo] = $HOME/foo $DATADIR/foo
+</pre>
+    </dd>
+
+    <dt id="make.build.ns-excl">build.ns-excl / <span id=
+    "make.build.ns-incl">build.ns-incl</span></dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Specifies the name-spaces in the source tree
+      to exclude/include. (A <code>build.ns-incl</code> takes precedence over a
+      <code>build.ns-excl</code> declaration.)</p>
+
+      <p><dfn>value</dfn>: A list of space-delimited items. Each item is a
+      name-space to exclude/include.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+build.ns-excl = / # exclude everything
+build.ns-incl = foo bar/baz # include items in these name-spaces
+</pre>
+    </dd>
+
+    <dt id="make.build.target">build.target</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Selects targets to build according to their
+      keys, categories and tasks.</p>
+
+      <p><dfn>modifier</dfn>: <samp>key</samp> (default if no modifier
+      specified), <samp>category</samp> or <samp>task</samp></p>
+
+      <p><dfn>namespace</dfn>: Not allowed if modifier is <samp>key</samp>.
+      Optional if modifier is <samp>category</samp> or <samp>task</samp>. If
+      specified, setting only applies to given name-spaces. Otherwise, setting
+      applies to all name-spaces.</p>
+
+      <p><dfn>value</dfn>: A list of space-delimited items (according to the
+      modifier).</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+build.target{category}[etc/namelists etc/configs] = etc
+build.target{category}[src/hello src/greet src/world] = bin lib
+build.target{task}[src/public] = archive install
+build.target = egg.sh mince.py
+</pre>
+    </dd>
+
+    <dt id="make.build.target-rename">build.target-rename</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Rename targets.</p>
+
+      <p><dfn>value</dfn>: A list of space-delimited items. Each item should be
+      the (automatic) key of a target, followed by a colon and its preferred
+      key.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+build.target-rename = hello.exe:hello
+</pre>
+    </dd>
+
+    <dt id="make.build.prop">build.prop</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Sets or modifies a property.</p>
+
+      <p><dfn>modifier</dfn>: The name of one (or more) property. See
+      below.</p>
+
+      <p><dfn>namespace</dfn>: Optional. A list of space-delimited namespaces to
+      which this setting applies. The namespaces can be target names, when used
+      with target properties (dependency properties are regarded as source
+      properties), or the hierarchical namespaces of source files. If not
+      specified, the setting applies to the global/root namespace. N.B. Not all
+      build properties accept a namespace. See below for detail.</p>
+
+      <p><dfn>value</dfn>: The value of the property.</p>
+
+      <p><dfn>example</dfn>:</p>
+      <pre>
+build.prop{fc} = gfortran
+build.prop{cc.flags,fc.flags} = -C -g
+build.prop{fc.flags}[foo/bar] = -C -g -W
+build.prop{fc.flags}[egg ham bacon] = -C -W
+build.prop{fc.flag-define} = -D%s
+build.prop{cc.defs,fc.defs}[gen] = LOWERCASE UNDERSCORE
+build.prop{fc.libs}[myprog.exe] = netcdf grib
+</pre>
+    </dd>
+  </dl>
+
+  <p>The following is a list of properties accepted by the
+  <code>build.prop</code> declaration. The default value is an empty string
+  unless given otherwise. Properties that do not accept a namespace are marked
+  with an asterisk (*).</p>
+
+  <dl>
+    <dt id="make.build.prop.ar">ar</dt>
+
+    <dd>The archive command. (default=<samp>ar</samp>)</dd>
+
+    <dt id="make.build.prop.ar.flags">ar.flags</dt>
+
+    <dd>If a list of values is specified, the system will not inherit sources
+    with the name-spaces matching the specified values. If the value is a
+    <samp>*</samp>, the system will not inherit any sources.</dd>
+    <dd>The options used by the archive command. (default=<samp>rs</samp>)</dd>
+
+    <dt id=
+    "make.build.prop.archive-ok-target-category">archive-ok-target-category
+    *</dt>
+
+    <dd>If <code>fcm make</code> is invoked with the <code>--archive</code>
+    option, sub-directories in categories containing intermediate build files
+    will be put into TAR-GZIP files. E.g. with the default setting,
+    <samp>include/</samp> and <samp>o/</samp> will become
+    <samp>include.tar.gz</samp> and <samp>o.tar.gz</samp> respectively.
+    (default=<samp>include o</samp>)</dd>
+
+    <dt id="make.build.prop.cc">cc</dt>
+
+    <dd>The C compiler and linker. (default=<samp>gcc</samp>)</dd>
+
+    <dt id="make.build.prop.cc.defs">cc.defs</dt>
+
+    <dd>The C compiler will pre-define each word in this setting as a
+    macro.</dd>
+
+    <dt id="make.build.prop.cc.flags">cc.flags</dt>
+
+    <dd>The options used by the C compiler at compile time.</dd>
+
+    <dt id="make.build.prop.cc.flags-ld">cc.flags-ld</dt>
+
+    <dd>The options used by the C compiler at link time.</dd>
+
+    <dt id="make.build.prop.cc.flag-compile">cc.flag-compile</dt>
+
+    <dd>The option used by the C compiler to suppress the linking stage.
+    (default=<samp>-c</samp>)</dd>
+
+    <dt id="make.build.prop.cc.flag-define">cc.flag-define</dt>
+
+    <dd>The option used by the C compiler to define a macro.
+    (default=<samp>-D%s</samp>)</dd>
+
+    <dt id="make.build.prop.cc.flag-include">cc.flag-include</dt>
+
+    <dd>The option used by the C compiler to specify an include search
+    path. (default=<samp>-I%s</samp>)</dd>
+
+    <dt id="make.build.prop.cc.flag-lib">cc.flag-lib</dt>
+
+    <dd>The option used by the C compiler at link time to specify a
+    library. (default=<samp>-l%s</samp>)</dd>
+
+    <dt id="make.build.prop.cc.flag-lib-path">cc.flag-lib-path</dt>
+
+    <dd>The option used by the C compiler at link time to specify a library
+    search path. (default=<samp>-L%s</samp>)</dd>
+
+    <dt id="make.build.prop.cc.flag-omp">cc.flag-omp</dt>
+
+    <dd>The option used by the C compiler to switch on OpenMP.</dd>
+
+    <dt id="make.build.prop.cc.flag-output">cc.flag-output</dt>
+
+    <dd>The option used by the C compiler to specify the output file name.
+    (default=<samp>-o%s</samp>)</dd>
+
+    <dt id="make.build.prop.cc.include-paths">cc.include-paths</dt>
+
+    <dd>The C compiler will add each directory in this setting as an include
+    search path.</dd>
+
+    <dt id="make.build.prop.cc.libs">cc.libs</dt>
+
+    <dd>The C linker will add each item in this setting as a link
+    library.</dd>
+
+    <dt id="make.build.prop.cc.lib-paths">cc.lib-paths</dt>
+
+    <dd>The C linker will add each directory in this setting as a library
+    search path.</dd>
+
+    <dt id="make.build.prop.checksum-method">checksum-method</dt>
+
+    <dd>The checksum method to use to determine if the content of a file is
+    changed or not. Either MD5 or one of the SHA algorithms supported by the
+    Perl module <a href="http://perldoc.perl.org/Digest/SHA.html">Digest::SHA</a>
+    can be used. (default=<samp>md5</samp>)</dd>
+
+    <dt id="make.build.prop.cxx">cxx</dt>
+
+    <dd>The C++ compiler and linker. (default=<samp>g++</samp>)</dd>
+
+    <dt id="make.build.prop.cxx.defs">cxx.defs</dt>
+
+    <dd>The C++ compiler will pre-define each word in this setting as a
+    macro.</dd>
+
+    <dt id="make.build.prop.cxx.flags">cxx.flags</dt>
+
+    <dd>The options used by the C++ compiler at compile time.</dd>
+
+    <dt id="make.build.prop.cxx.flags-ld">cxx.flags-ld</dt>
+
+    <dd>The options used by the C++ compiler at link time.</dd>
+
+    <dt id="make.build.prop.cxx.flag-compile">cxx.flag-compile</dt>
+
+    <dd>The option used by the C++ compiler to suppress the linking stage.
+    (default=<samp>-c</samp>)</dd>
+
+    <dt id="make.build.prop.cxx.flag-define">cxx.flag-define</dt>
+
+    <dd>The option used by the C++ compiler to define a macro.
+    (default=<samp>-D%s</samp>)</dd>
+
+    <dt id="make.build.prop.cxx.flag-include">cxx.flag-include</dt>
+
+    <dd>The option used by the C++ compiler to specify an include search
+    path. (default=<samp>-I%s</samp>)</dd>
+
+    <dt id="make.build.prop.cxx.flag-lib">cxx.flag-lib</dt>
+
+    <dd>The option used by the C++ compiler at link time to specify a
+    library. (default=<samp>-l%s</samp>)</dd>
+
+    <dt id="make.build.prop.cxx.flag-lib-path">cxx.flag-lib-path</dt>
+
+    <dd>The option used by the C++ compiler at link time to specify a library
+    search path. (default=<samp>-L%s</samp>)</dd>
+
+    <dt id="make.build.prop.cxx.flag-omp">cxx.flag-omp</dt>
+
+    <dd>The option used by the C++ compiler to switch on OpenMP.</dd>
+
+    <dt id="make.build.prop.cxx.flag-output">cxx.flag-output</dt>
+
+    <dd>The option used by the C++ compiler to specify the output file name.
+    (default=<samp>-o%s</samp>)</dd>
+
+    <dt id="make.build.prop.cxx.include-paths">cxx.include-paths</dt>
+
+    <dd>The C++ compiler will add each directory in this setting as an include
+    search path.</dd>
+
+    <dt id="make.build.prop.cxx.libs">cxx.libs</dt>
+
+    <dd>The C++ linker will add each item in this setting as a link
+    library.</dd>
+
+    <dt id="make.build.prop.cxx.lib-paths">cxx.lib-paths</dt>
+
+    <dd>The C++ linker will add each directory in this setting as a library
+    search path.</dd>
+
+    <dt id="make.build.prop.dep.bin">dep.bin</dt>
+
+    <dd>Specifies a list of manual dependencies on external executable
+    commands.</dd>
+
+    <dt id="make.build.prop.dep.f.module">dep.f.module</dt>
+
+    <dd>Specifies a list of manual Fortran module dependencies. Note: This
+    modifier does not work if the name-space is specified using a target
+    name.</dd>
+
+    <dt id="make.build.prop.dep.include">dep.include</dt>
+
+    <dd>Specifies a list of manual include dependencies.</dd>
+
+    <dt id="make.build.prop.dep.o">dep.o</dt>
+
+    <dd>Specifies a list of manual object dependencies.</dd>
+
+    <dt id="make.build.prop.dep.o.special">dep.o.special</dt>
+
+    <dd>Specifies a list of manual object dependencies, which must appear on
+    the command line of the linker, (e.g. an object containing a Fortran
+    blockdata program unit).</dd>
+
+    <dt id="make.build.prop.fc">fc</dt>
+
+    <dd>The Fortran compiler and linker. (default=<samp>gfortran</samp>)</dd>
+
+    <dt id="make.build.prop.fc.defs">fc.defs</dt>
+
+    <dd>The Fortran compiler will pre-define each word in this setting as a
+    macro.</dd>
+
+    <dt id="make.build.prop.fc.flags">fc.flags</dt>
+
+    <dd>The options used by the Fortran compiler at compile time.</dd>
+
+    <dt id="make.build.prop.fc.flags-ld">fc.flags-ld</dt>
+
+    <dd>The options used by the Fortran compiler at link time.</dd>
+
+    <dt id="make.build.prop.fc.flag-compile">fc.flag-compile</dt>
+
+    <dd>The option used by the Fortran compiler to suppress the linking stage.
+    (default=<samp>-c</samp>)</dd>
+
+    <dt id="make.build.prop.fc.flag-define">fc.flag-define</dt>
+
+    <dd>The option used by the Fortran compiler to define a macro.
+    (default=<samp>-D%s</samp>)</dd>
+
+    <dt id="make.build.prop.fc.flag-include">fc.flag-include</dt>
+
+    <dd>The option used by the Fortran compiler to specify an include search
+    path. (default=<samp>-I%s</samp>)</dd>
+
+    <dt id="make.build.prop.fc.flag-lib">fc.flag-lib</dt>
+
+    <dd>The option used by the Fortran compiler at link time to specify a
+    library. (default=<samp>-l%s</samp>)</dd>
+
+    <dt id="make.build.prop.fc.flag-lib-path">fc.flag-lib-path</dt>
+
+    <dd>The option used by the Fortran compiler at link time to specify a
+    library search path. (default=<samp>-L%s</samp>)</dd>
+
+    <dt id="make.build.prop.fc.flag-module">fc.flag-module</dt>
+
+    <dd>The option used by the Fortran compiler to specify a module search
+    path.</dd>
+
+    <dt id="make.build.prop.fc.flag-omp">fc.flag-omp</dt>
+
+    <dd>The option used by the Fortran compiler to switch on OpenMP. If
+    specified, the build system will treat <code>USE</code> and
+    <code>INCLUDE</code> statements that are protected by <code>!$ </code>
+    sentinels as normal dependency statements.</dd>
+
+    <dt id="make.build.prop.fc.flag-output">fc.flag-output</dt>
+
+    <dd>The option used by the Fortran compiler to specify the output file
+    name. (default=<samp>-o%s</samp>)</dd>
+
+    <dt id="make.build.prop.fc.include-paths">fc.include-paths</dt>
+
+    <dd>The Fortran compiler will add each directory in this setting as an
+    include search path.</dd>
+
+    <dt id="make.build.prop.fc.libs">fc.libs</dt>
+
+    <dd>The Fortran linker will add each item in this setting as a link
+    library.</dd>
+
+    <dt id="make.build.prop.fc.lib-paths">fc.lib-paths</dt>
+
+    <dd>The Fortran linker will add each directory in this setting as a library
+    search path.</dd>
+
+    <dt id="make.build.prop.file-ext.a">file-ext.a *</dt>
+
+    <dd>Specifies the extension of an object archive file.
+    (default=<samp>.a</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.bin">file-ext.bin *</dt>
+
+    <dd>Specifies the extension of a binary executable file.
+    (default=<samp>.exe</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.c">file-ext.c *</dt>
+
+    <dd>Specifies the extensions of a C source file.
+    (default=<samp>.c .i .m .mi</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.cxx">file-ext.cxx *</dt>
+
+    <dd>Specifies the extensions of a C++ source file.
+    (default=<samp>.cc .cp .cxx .cpp .CPP .c++ .C .mm .M .mii</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.fortran">file-ext.fortran *</dt>
+
+    <dd>Specifies the extensions of a Fortran source file. (default=<samp>.F
+    .FOR .FTN .F90 .F95 .f .for .ftn .f90 .f95 .inc</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.f90-interface">file-ext.f90-interface
+    *</dt>
+
+    <dd>Specifies the extension of a Fortran interface file.
+    (default=<samp>.interface</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.f90-mod">file-ext.f90-mod *</dt>
+
+    <dd>Specifies the extension of a compiled Fortran module file.
+    (default=<samp>.mod</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.h">file-ext.h *</dt>
+
+    <dd>Specifies the extensions of a C/C++ header file.
+    (default=<samp>.h</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.o">file-ext.o *</dt>
+
+    <dd>Specifies the extension of a compiled object file.
+    (default=<samp>.o</samp>)</dd>
+
+    <dt id="make.build.prop.file-ext.script">file-ext.script *</dt>
+
+    <dd>Specifies the extensions of script files.</dd>
+
+    <dt id="make.build.prop.file-name-option.f90-mod">file-name-option.f90-mod
+    *</dt>
+
+    <dd>Specifies other options for naming a compiled Fortran module file.
+    Accepts <samp>case=upper</samp> or <samp>case=lower</samp> (default).</dd>
+
+    <dt id="make.build.prop.file-pat.script">file-pat.script *</dt>
+
+    <dd>Specifies a regular expression for matching the name of script
+    files.</dd>
+
+    <dt id="make.build.prop.ignore-missing-dep-ns">ignore-missing-dep-ns *</dt>
+
+    <dd>Specifies a list of source name-spaces, in which targets can ignore
+    missing dependencies.</dd>
+
+    <dt id="make.build.prop.keep-lib-o">keep-lib-o</dt>
+
+    <dd>Relevant when linking a binary executable and <a href=
+    "#make.build.prop.link-without-ar">link-without-ar</a> is not set to
+    <kbd>true</kbd>. If <kbd>true</kbd>, create and keep the dependent object
+    library as <samp>lib/libNAME.a</samp>, where <var>NAME</var> is the root
+    name of the executable. In the absence of this setting, the behaviour is to
+    create the dependent object library in a temporary directory, which is
+    removed after the linker command is completed.</dd>
+
+    <dt id="make.build.prop.ld">ld</dt>
+
+    <dd>The linker command. If not specified, use the compiler of the source
+    file.</dd>
+
+    <dt id="make.build.prop.link-without-ar">link-without-ar</dt>
+
+    <dd>Relevant when linking a binary executable. If <kbd>true</kbd>, do not
+    create the dependent object library when linking. Instead, locations of the
+    dependent objects will be specified on the command line of the linker. In
+    the absence of this setting, the behaviour is to create the dependent object
+    library in a temporary directory, with the location of the object library
+    specified using the relevant options of the linker.</dd>
+
+    <dt id="make.build.prop.no-dep.bin">no-dep.bin</dt>
+
+    <dd>Switches off a list of automatic dependencies on external executable.
+    If the value is a <samp>*</samp>, switches off all automatic external
+    executable dependencies.</dd>
+
+    <dt id="make.build.prop.no-dep.f.module">no-dep.f.module</dt>
+
+    <dd>Switches off a list of automatic Fortran module dependencies. If the
+    value is a <samp>*</samp>, switches off all automatic Fortran module
+    dependencies. Note: This modifier does not work if the name-space is
+    specified using a target name.</dd>
+
+    <dt id="make.build.prop.no-dep.include">no-dep.include</dt>
+
+    <dd>Switches off a list of automatic include dependencies. If the value is
+    a <samp>*</samp>, switches off all automatic include dependencies.</dd>
+
+    <dt id="make.build.prop.no-dep.o">no-dep.o</dt>
+
+    <dd>Switches off a list of automatic object dependencies. If the value is a
+    <samp>*</samp>, switches off all automatic object dependencies.</dd>
+
+    <dt id="make.build.prop.no-inherit-source">no-inherit-source *</dt>
+
+    <dd>If a list of values is specified, the system will not inherit sources
+    with the name-spaces matching the specified values. If the value is a
+    <samp>*</samp>, the system will not inherit any sources.</dd>
+
+    <dt id="make.build.prop.no-inherit-target-category">
+    no-inherit-target-category *</dt>
+
+    <dd>If a list of values is specified, the system will not inherit a target
+    in a category matching a specified value. (default=<samp>bin etc
+    lib</samp>)</dd>
+
+    <dt id="make.build.prop.no-step-source">no-step-source *</dt>
+
+    <dd>If a list of make steps is specified, the system will not search for
+    source files from the specified make steps.</dd>
+
+    <dt id="make.build.prop.ns-dep.o">ns-dep.o</dt>
+
+    <dd>Specifies a list of link-time object dependencies on all objects in the
+    list of name-spaces in the value.</dd>
+  </dl>
+
+  <h3 id="make.preprocess">FCM Make Configuration: Preprocess</h3>
+
+  <p>The preprocess system uses the same declarations as the build system (see
+  <a href="#make.build">FCM Make Configuration: Build</a>), although their
+  prefixes should be replaced with <samp>preprocess.*</samp> or the appropriate
+  step ID. The following is a list of modifiers accepted by the
+  <code>preprocess.prop</code> declaration. The default value of a property is
+  an empty string unless given otherwise. Properties that do not accept a
+  namespace are marked with an asterisk (*).</p>
+
+  <dl>
+    <dt id="make.preprocess.prop.cpp">cpp</dt>
+
+    <dd>The command of the C/C++ pre-processor. (default=<samp>cpp</samp>)</dd>
+
+    <dt id="make.preprocess.prop.cpp.defs">cpp.defs</dt>
+
+    <dd>The C/C++ pre-processor will pre-define each word in this setting as a
+    macro.</dd>
+
+    <dt id="make.preprocess.prop.cpp.flags">cpp.flags</dt>
+
+    <dd>The options used by the C/C++ pre-processor.</dd>
+
+    <dt id="make.preprocess.prop.cpp.flag-define">cpp.flag-define</dt>
+
+    <dd>The option used by the C/C++ pre-processor to define a macro.
+    (default=<samp>-D%s</samp>)</dd>
+
+    <dt id="make.preprocess.prop.cpp.flag-include">cpp.flag-include</dt>
+
+    <dd>The option used by the C/C++ pre-processor to specify the include
+    search path. (default=<samp>-I%s</samp>)</dd>
+
+    <dt id="make.preprocess.prop.cpp.include-paths">cpp.include-paths</dt>
+
+    <dd>The C/C++ preprocessor will add each directory in this setting as an
+    include search path.</dd>
+
+    <dt id="make.preprocess.prop.dep.include">dep.include</dt>
+
+    <dd>Specifies a list of manual include dependencies.</dd>
+
+    <dt id="make.preprocess.prop.file-ext.cpp">file-ext.cpp *</dt>
+
+    <dd>Specifies the extensions of C/C++ source file.
+    (default=<samp>.c .m .cc .cp .cxx .cpp .CPP .c++ .C .mm .M</samp>)</dd>
+
+    <dt id="make.preprocess.prop.file-ext.fpp">file-ext.fpp *</dt>
+
+    <dd>Specifies the extensions of Fortran source file requiring
+    preprocessing. (default=<samp>.F .FOR .FTN .F90 .F95</samp>)</dd>
+
+    <dt id="make.preprocess.prop.file-ext.h">file-ext.h *</dt>
+
+    <dd>Specifies the extensions of a C/C++ header file.
+    (default=<samp>.h</samp>)</dd>
+
+    <dt id="make.preprocess.prop.fpp">fpp</dt>
+
+    <dd>The command of the Fortran pre-processor.
+    (default=<samp>cpp</samp>)</dd>
+
+    <dt id="make.preprocess.prop.fpp.defs">fpp.defs</dt>
+
+    <dd>The Fortran pre-processor will pre-define each word in this setting as
+    a macro.</dd>
+
+    <dt id="make.preprocess.prop.fpp.flags">fpp.flags</dt>
+
+    <dd>The options used by the Fortran pre-processor. (default=<samp>-P
+    -traditional</samp>)</dd>
+
+    <dt id="make.preprocess.prop.fpp.flag-define">fpp.flag-define</dt>
+
+    <dd>The option used by the Fortran pre-processor to define a macro.
+    (default=<samp>-D%s</samp>)</dd>
+
+    <dt id="make.preprocess.prop.fpp.flag-include">fpp.flag-include</dt>
+
+    <dd>The option used by the Fortran pre-processor to specify the include
+    search path. (default=<samp>-I%s</samp>)</dd>
+
+    <dt id="make.preprocess.prop.fpp.include-paths">fpp.include-paths</dt>
+
+    <dd>The Fortran preprocessor will add each directory in this setting as an
+    include search path.</dd>
+
+    <dt id="make.preprocess.prop.no-dep.include">no-dep.include</dt>
+
+    <dd>Switches off a list of automatic include dependencies. If the value is
+    a <samp>*</samp>, it switches off all automatic include dependencies.</dd>
+
+    <dt id="make.preprocess.prop.no-inherit-source">no-inherit-source *</dt>
+
+    <dd>Same as the build property of the same name.</dd>
+
+    <dt id="make.preprocess.prop.no-inherit-target-category">
+    no-inherit-target-category *</dt>
+
+    <dd>Same as the build property of the same name.</dd>
+
+    <dt id="make.preprocess.prop.no-step-source">no-step-source *</dt>
+
+    <dd>Same as the build property of the same name.</dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/annex_quick_ref_tree_conflicts.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/annex_quick_ref_tree_conflicts.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/annex_quick_ref_tree_conflicts.html	(revision 5095)
@@ -0,0 +1,302 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM: User Guide: Annex: Quick Reference: Tree Conflict Resolution</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a class="active" href="#">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM: User Guide: Annex: Quick Reference: Tree Conflict Resolution</h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <h2 id="tree:reference:intro">Introduction</h2>
+
+  <p>A tree conflict appears in <code>fcm status</code> like this:</p>
+  <pre>
+!     C subroutine/hello_sub_dummy.h
+      &gt;   local missing, incoming edit upon merge
+</pre>
+
+  <p>and can require complex action to solve, based on the situation. Happily,
+  <code>fcm conflicts</code> automates the resolution of most ordinary tree
+  conflicts.</p>
+
+  <p>This page is intended to give more information about the meaning of the
+  common tree conflicts, and to give guidance on those that aren't handled
+  automatically, such as directory tree conflicts.</p>
+
+  <p>The list below is ordered by output from <code>fcm status</code>. There
+  are two choices that the user must make:</p>
+
+  <ul>
+    <li><em>keep local</em>: Choose the local branch or revision version of a
+    file or directory. Answer <samp>y</samp> in <code>fcm
+    conflicts</code>.</li>
+
+    <li><em>discard local</em>: Choose the merge branch or other revision
+    version of a file or directory. Answer <samp>n</samp> in <code>fcm
+    conflicts</code>.</li>
+  </ul>
+
+  <p>For example, if you are merging the trunk into a working copy of a branch,
+  <em>keep local</em> would refer to <strong>keeping</strong> the changes as
+  they existed on the branch; <em>discard local</em> would refer to accepting
+  the trunk changes and <strong>discarding</strong> the branch ones.</p>
+
+  <p>In this page, we use the word <em>local</em> for your local working copy,
+  and <em>external</em> for the outside source you are updating or merging in
+  from. In the example above, <em>local</em> would mean your working copy of
+  the branch; <em>external</em> would mean the trunk.</p>
+
+  <p>Subversion implements rename as a copy-and-delete operation. This means a
+  rename can show up as a delete (or <em>missing</em>) in the tree conflict
+  information.</p>
+
+  <p>It's very important to find out if your tree conflict arises from a
+  rename, but this information has to be dug out of <code>fcm log</code>. A
+  rename can have occurred locally or externally. An external rename would show
+  up in <code>fcm status</code> as an addition with history (<samp>A</samp>
+  with <samp>+</samp>): for example:</p>
+  <pre>
+A   +   FILENAME
+</pre>
+
+  <p><code>fcm log -v FILENAME</code> can be used to examine if this is really
+  just a rename - it will show up as FILENAME (from ORIGINAL_FILENAME).</p>
+
+  <p>Local renames that have been committed won't show up in <code>fcm
+  status</code>. These can still be found using <code>fcm log -v</code> for
+  each filename, or you can try to remember what happened!</p>
+
+  <h2 id="tree:reference:list">Resolution List</h2>
+
+  <p>This section contains specific help on different types of tree
+  conflict.</p>
+
+  <p>Find the relevant section below by running <code>fcm status</code> and
+  looking up the information below the file in conflict - e.g.:</p>
+  <pre>
+!     C subroutine/hello_sub_dummy.h
+      &gt;   local delete, incoming delete upon merge
+</pre>
+
+  <p>in this case the <samp>local delete, incoming delete upon merge</samp> is
+  the correct section header.</p>
+
+  <p>There are some situations not covered below - <samp>local
+  obstruction</samp> is not covered here, as it is a case of the user
+  corrupting the working copy - try a new checkout. Similarly, <samp>local
+  unversioned</samp> is just a case of a problem with something in the working
+  copy - an unversioned file or directory exists where Subversion wants to put
+  the new stuff. Delete or move it, and try the merge again.</p>
+
+  <p>If you know that a rename has happened, use the <samp>(renaming)</samp>
+  suffix for your section below. Otherwise, choose the <samp>(no
+  renaming)</samp> suffix.</p>
+
+  <p><code>fcm resolve</code> always takes the form <code>fcm resolve --accept
+  working FILENAME</code> for tree conflicts.</p>
+
+  <dl>
+    <dt id="add:add"><samp>local add, incoming add upon merge</samp></dt>
+
+    <dd>
+      <p><dfn>what it means</dfn>: files or directories added with the same
+      name independently</p>
+
+      <p><dfn>what keep local does</dfn>: uses rename to shuffle the old file
+      to a different name, copies the new file in, renames the new file to the
+      original name but with a temporary-style suffix (e.g. hello.F90 -&gt;
+      hello.F90.xD4r), and again renames the old file to the original name.
+      (Then runs <code>fcm resolve</code>).</p>
+
+      <p><dfn>what discarding local does</dfn>: renames the old file to give it
+      a temporary-style suffix (e.g. hello.F90 -&gt; hello.F90.r6Ys), and
+      copies the new file into the original name. (Then runs <code>fcm
+      resolve</code>).</p>
+    </dd>
+
+    <dt id="edit:delete:no_rename"><samp>local edit, incoming delete upon merge
+    (no renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: file or directory modified on the branch locally,
+      but deleted on the merge branch</p>
+
+      <p><dfn>what keep local does</dfn>: just runs <code>fcm
+      resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: deletes the file or directory
+      and runs <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="edit:delete:rename"><samp>local edit, incoming delete upon merge
+    (renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: file modified on branch locally, but renamed on
+      merge branch</p>
+
+      <p><dfn>what keep local does</dfn>: copies over the renamed file, and the
+      common ancestor of the file on the branches, and uses them for a text
+      conflict style merge into the old (local) filename. It then removes the
+      renamed file and runs <code>fcm resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: copies over the renamed file,
+      and the common ancestor of the file on the branches, and uses them for a
+      text conflict style merge into the new renamed file. It then deletes the
+      old file and runs <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="edit:replace"><samp>local edit, incoming replace upon
+    merge</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: file modified on branch locally, but replaced
+      (deleted, and new file added) on merge branch</p>
+
+      <p><dfn>what keep local does</dfn>: just runs <code>fcm
+      resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: <code>svn delete</code> local
+      file, and <code>svn copy</code> the incoming file into it. It then runs
+      <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="delete:delete:no_rename"><samp>local delete, incoming delete upon
+    merge (no renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: same filename deleted both locally and
+      externally.</p>
+
+      <p><dfn>in both cases</dfn>: just runs <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="delete:rename:rename"><samp>local delete, incoming delete upon
+    merge (just external renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: file deleted locally, but renamed externally</p>
+
+      <p><dfn>what keep local does</dfn>: just runs <code>fcm
+      resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: just deletes the new renamed
+      file and runs <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="rename:delete:rename"><samp>local delete, incoming delete upon
+    merge (just local renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: file renamed locally, but deleted externally</p>
+
+      <p><dfn>what keep local does</dfn>: just runs <code>fcm
+      resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: deletes the local renamed file
+      and runs <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="delete:delete:rename"><samp>local delete, incoming delete upon
+    merge (local renaming AND external renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: same file renamed locally AND externally, to two
+      different names.</p>
+
+      <p><dfn>what keep local does</dfn>: copies in the external file and
+      common ancestor file to construct a text-style merge using
+      <code>xxdiff</code> into the locally-renamed filename. Removes the
+      external rename and runs <code>fcm resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: copies in the external file and
+      common ancestor file to construct a text-style merge using
+      <code>xxdiff</code> into the externally-renamed filename. Removes the
+      local rename and runs <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="missing:edit:no_rename"><samp>local missing, incoming edit upon
+    merge (no renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: locally deleted file, add newer file from merge
+      branch?</p>
+
+      <p><dfn>what keep local does</dfn>: just runs <code>fcm
+      resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: copies in the external file
+      using its URL and runs <code>fcm resolve</code>.</p>
+    </dd>
+
+    <dt id="missing:edit:rename"><samp>local missing, incoming edit upon merge
+    (renaming)</samp></dt>
+
+    <dd>
+      <p><dfn>meaning</dfn>: locally renamed file, but external changes to the
+      old filename</p>
+
+      <p><dfn>what keep local does</dfn>: copies in the external file and
+      common ancestor to construct a text-style merge using
+      <code>xxdiff</code>, into the locally-renamed filename. Runs <code>fcm
+      resolve</code>.</p>
+
+      <p><dfn>what discarding local does</dfn>: copies in the external file and
+      common ancestor to construct a text-style merge using
+      <code>xxdiff</code>, into the original filename. Deletes the
+      locally-renamed file and adds the original filename, then runs <code>fcm
+      resolve</code>.</p>
+    </dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/api.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/api.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/api.html	(revision 5095)
@@ -0,0 +1,226 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM: User Guide: A Brief Introduction to the FCM Perl API</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a class="active" href="#">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM: User Guide: A Brief Introduction to the FCM Perl API</h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <p>The majority of FCM functionalities are provided by a set of Perl modules.
+  Old modules developed prior to <a href="../release_notes/2-0.html">release
+  2-0</a> reside in the <code>FCM1::*</code> name-space. Modules developed
+  thereafter reside in the <code>FCM::*</code> name-space. These are
+  sub-divided into the following name-spaces:</p>
+
+  <dl>
+    <dt><code>FCM::Class</code></dt>
+
+    <dd>
+      <p>Provides an internal object class framework.</p>
+
+      <p>The majority of the classes in the <code>FCM::*</code> name-space are
+      sub-classes of either <code>FCM::Class::CODE</code> or
+      <code>FCM::Class::HASH</code>. The former creates classes that are
+      blessed <code>CODE</code> references, and is intended for configurable
+      functional (i.e. mostly <em>stateless</em>) objects. The latter creates
+      classes that are blessed <code>HASH</code> references, and is intended
+      for data objects.</p>
+
+      <p>Note: In theory, we could use the standard module <code><a href=
+      "http://search.cpan.org/~rjbs/perl-5.12.3/lib/Class/Struct.pm">Class::Struct</a></code>
+      or the modern <a href="http://www.iinteractive.com/moose/">MOOSE</a>
+      framework. The problem is that the former is not powerful enough to give
+      us what we need, and the latter is not a standard module and is too heavy
+      weight for our intends and purposes. Instead, the developer decides that
+      it is easier to go for a light weight and in house solution.</p>
+    </dd>
+
+    <dt><code>FCM::CLI</code></dt>
+
+    <dd>
+      <p>Provides the logic and configuration of the command line interface
+      (CLI).</p>
+
+      <p>It is made up of the following components:</p>
+
+      <dl>
+        <dt><code>FCM::CLI</code></dt>
+
+        <dd>Logic to provide help and invoke functions of
+        <code>FCM::System</code>.</dd>
+
+        <dt><code>FCM::CLI::Exception</code></dt>
+
+        <dd>CLI exception.</dd>
+
+        <dt><code>FCM::CLI::Parser</code></dt>
+
+        <dd>CLI option parser and configuration.</dd>
+
+        <dt><samp>fcm-*.pod</samp></dt>
+
+        <dd>Help files for the CLI commands.</dd>
+      </dl>
+    </dd>
+
+    <dt><code>FCM::Context::*</code></dt>
+
+    <dd>
+      <p>Provides the data structures for storing the run time contexts.</p>
+
+      <p>The objects of these classes do very little, but they provide the data
+      structures that define the <em>states</em> of the program at run
+      time.</p>
+    </dd>
+
+    <dt><code>FCM::Exception</code></dt>
+
+    <dd>
+      <p>Provides the base class for exceptions.</p>
+    </dd>
+
+    <dt><code>FCM::System</code></dt>
+
+    <dd>
+      <p>Provides a façade to the functionalities of the FCM system.</p>
+
+      <p>The actual implementation is delegated to the following:</p>
+
+      <dl>
+        <dt><code>FCM::System::CM</code></dt>
+
+        <dd>The code management system. Currently a thin adapter to
+        <code>FCM1::Cm</code>.</dd>
+
+        <dt><code>FCM::System::Misc</code></dt>
+
+        <dd>Miscellaneous functions, e.g. <code>browse</code>,
+        <code>cfg-print</code>, <code>keyword-print</code>.</dd>
+
+        <dt><code>FCM::System::Old</code></dt>
+
+        <dd>Thin adapter to the old extract and build systems.</dd>
+
+        <dt><code>FCM::System::Make</code></dt>
+
+        <dd>The logic of the FCM make system.</dd>
+
+        <dt><code>FCM::System::Make::Build</code></dt>
+
+        <dd>FCM make: build system logic.</dd>
+
+        <dt><code>FCM::System::Make::Build::*</code></dt>
+
+        <dd>FCM make: build system components: File type and task specific
+        logic.</dd>
+
+        <dt><code>FCM::System::Make::Extract</code></dt>
+
+        <dd>FCM make: extract system logic.</dd>
+
+        <dt><code>FCM::System::Make::Mirror</code></dt>
+
+        <dd>FCM make: mirror system logic.</dd>
+
+        <dt><code>FCM::System::Make::Preprocess</code></dt>
+
+        <dd>FCM make: preprocess system logic, actually a configuration of
+        <code>FCM::System::Make::Build</code>.</dd>
+
+        <dt><code>FCM::System::Make::Share::*</code></dt>
+
+        <dd>Shared logic between all subsystems in
+        <code>FCM::System::Make::*</code>.</dd>
+      </dl>
+    </dd>
+
+    <dt><code>FCM::Util</code></dt>
+
+    <dd>
+      <p>Provides supporting utilities.</p>
+
+      <p>Functionalities include:</p>
+
+      <ul>
+        <li>abstract utilities for SVN URLs and file system paths.</li>
+
+        <li>configuration file reader.</li>
+
+        <li>event handler.</li>
+
+        <li>file utilities.</li>
+
+        <li>message report.</li>
+
+        <li>name space utilities.</li>
+
+        <li>shell invocation.</li>
+
+        <li>multi-process task runner.</li>
+
+        <li>timer.</li>
+      </ul>
+
+      <p>The logic of the more complex utilities are delegated to modules in
+      the <code>FCM::Util::*</code> name space.</p>
+    </dd>
+  </dl>
+
+  <p>Note: The majority of modules in the old <code>FCM1::*</code> name space
+  are considered deprecated, with the exception of <code>FCM1::Cm</code> and
+  those providing support functionalities for it. The functionalities of these
+  modules will eventually be absorbed into the <code>FCM::System::CM</code>
+  framework.</p>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/make.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/make.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/doc/user_guide/make.html	(revision 5095)
@@ -0,0 +1,2422 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>FCM: User Guide: FCM Make</title>
+  <meta name="author" content="FCM team" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link rel="shortcut icon" href="../etc/fcm-icon.png" type="image/png" />
+  <link href="../etc/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+  <link href="../etc/fcm.css" rel="stylesheet" media="screen" />
+</head>
+<body>
+  <div class="navbar navbar-inverse">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand" href=".."><span class="fcm-version">FCM</span></a>
+      </div>
+      <div class="collapse navbar-collapse">
+        <ul class="nav navbar-nav">
+          <li><a href="../installation/">Installation</a></li>
+
+          <li><a class="active" href="#">User Guide</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="page-header">
+    <div class="fcm-page-content pull-right well well-sm"></div>
+    <h1>FCM: User Guide: FCM Make</h1>
+  </div>
+
+  <div class="container">
+  <div class="row">
+  <div class="col-md-12">
+
+  <h2 id="introduction">Introduction</h2>
+
+  <p>The FCM make system provides a common environment for running the extract
+  system, build system, and other utilities. Simply put, it controls a chain of
+  steps. It is NOT to be confused with the standard Unix utility
+  <code>make</code>.</p>
+
+  <p>See <a href="annex_cfg.html#make">Annex: FCM Configuration File &gt; FCM
+  Make Configuration</a> for a full list of declarations in an FCM Make
+  configuration file.</p>
+
+  <h2 id="concept">FCM Make Concept</h2>
+
+  <h3 id="concept.cli">FCM Make Concept: Command Line Interface</h3>
+
+  <p>The FCM make system can be invoked using the command line interface:</p>
+  <pre>
+fcm make [OPTIONS] [DECLARAION ...]
+</pre>
+
+  <p>The FCM make system relies on a configuration file to tell it what to do.
+  It looks for configurations with the following logic:</p>
+
+  <ol>
+    <li>It reads each configuration file specified using the
+    <code>--config-file=PATH</code> option in the order they are
+    specified.</li>
+    
+    <li>If the <code>--config-file=PATH</code> option is not specified, it will
+    attempt to read <code>fcm-make.cfg</code> if it exists.</li>
+
+    <li>It looks at the current working directory, or the directory specified
+    in the <code>--directory=PATH</code> option for configuration files
+    specified as relative paths.</li>
+
+    <li>If one or more <code>--config-file-path=PATH</code> option is
+    specified, it also searches for configuration files under the specified
+    values, in the order the options are specified.</li>
+
+    <li>Finally, each <var>KEY=VALUE</var> command line argument is considered
+    a configuration declaration line.</li>
+  </ol>
+
+  <p>For details of the other command line options please see <a href=
+  "command_ref.html#fcm-make">FCM Command Reference &gt; fcm make</a>.</p>
+
+  <h3 id="concept.cfg">FCM Make Concept: Configuration File</h3>
+
+  <p>The FCM make configuration file is expected to be an FCM configuration
+  file. (See <a href="annex_cfg.html">Annex: FCM Configuration File</a>.) A
+  typical FCM make configuration file may look like:</p>
+  <pre>
+steps = extract build                           # 1
+
+extract.ns = egg ham bacon                      # 2
+# ... more extract configuration
+
+build.target = egg.exe ham.exe bacon            # 3
+# ... more build configuration
+</pre>
+
+  <p>At point 1, the <code><a href="annex_cfg.html#make.steps">steps</a></code>
+  declaration tells the system to invoke two steps. Their IDs are
+  <samp>extract</samp> and <samp>build</samp>, both are IDs for built-in
+  systems. (The other 2 being <samp>mirror</samp> and <samp>preprocess</samp>.)
+  Each step has its own declarations, which are prefixed with the step ID and a
+  full stop. The declarations at point 2 are used by the logic for running the
+  <samp>extract</samp> step and the declarations at point 3 are used by the
+  logic for running the <samp>build</samp> step.</p>
+
+  <p>There are times when you may need to invoke the same system (e.g. the
+  build system) in slightly different configurations. To do this you need to
+  use the <code><a href="annex_cfg.html#make.step.class">step.class</a></code>
+  declaration to define custom step IDs for each build. E.g.:</p>
+  <pre>
+step.class[build-this build-that] = build       # 1
+steps = extract build-this build-that           # 2
+
+extract.ns = egg ham bacon                      # 3
+# ... more extract configuration
+
+build-this.prop{fc.defs} = DEFS TO BUILD THIS   # 4
+build-this.target = this.exe
+# ... more build configuration for "build-this"
+
+build-that.prop{fc.defs} = DEFS TO BUILD THAT   # 5
+build-that.target = that.exe
+# ... more build configuration for "build-that"
+</pre>
+
+  <p>At point 1, we define the IDs <samp>build-this</samp> and
+  <samp>build-that</samp> to be an instance of the <samp>build</samp> class. At
+  point 2, we tell the system to run <samp>extract</samp>, then
+  <samp>build-this</samp>, then <samp>build-that</samp>. At point 4 and 5, we
+  define the configurations for <samp>build-this</samp> and
+  <samp>build-that</samp> respectively.</p>
+
+  <h3 id="concept.use">FCM Make Concept: Inheritance</h3>
+
+  <p>The FCM Make system allows inheritance of configuration and files from
+  succeeded makes, so the current make does not have to re-do everything that
+  may already be available else where. To do this, specify the inheritance with
+  a <code><a href= "annex_cfg.html#make.use">use</a> declaration</code>,
+  e.g.:</p>
+
+  <pre>
+use = /path/to/succeeded/make/
+</pre>
+
+  <p>For more information on how an inheritance behaves, see:</p>
+
+  <ul>
+    <li><a href="#extract.inherit">Extract Inheritance</a></li>
+
+    <li><a href="#build.inherit">Build Inheritance</a></li>
+  </ul>
+
+  <h3 id="concept.dest">FCM Make Concept: Directory Structure</h3>
+
+  <p>When you run <code>fcm make</code>, it will create a directory
+  structure that may look like:</p>
+
+  <pre>
+.fcm-make/...
+extract/...
+build/...
+...
+</pre>
+
+  <p>Each normal sub-directory, such as <samp>extract/</samp> and
+  <samp>build/</samp>, contains the result of each step. The hidden
+  sub-directory <samp>.fcm-make/</samp> is used by <code>fcm make</code> as a
+  working area. It may contain the following items:</p>
+
+  <dl>
+    <dt><samp>.fcm-make/cache/</samp></dt>
+
+    <dd>An area for caching non-local items. E.g. the extract system exports
+    source trees from the version control system into this cache. If <code>fcm
+    make</code> is invoked with the <code>--archive</code> option, contents in
+    this directory will be compressed into TAR-GZIP files, e.g.
+    <samp>.fcm-make/cache/extract/</samp> will become
+    <samp>.fcm-make/cache/extract.tar.gz</samp>.</dd>
+
+    <dt><samp>fcm-make-as-parsed.cfg -&gt;
+    .fcm-make/config-as-parsed.cfg</samp></dt>
+
+    <dd>The configuration file as parsed by the latest <code>fcm make</code>
+    command at this destination.</dd>
+
+    <dt><samp>fcm-make-on-success.cfg -&gt;
+    .fcm-make/config-on-success.cfg</samp></dt>
+
+    <dd>The configuration file that can be used to repeat the latest successful
+    <code>fcm make</code> command at this destination.</dd>
+
+    <dt><samp>.fcm-make/ctx.gz</samp></dt>
+    
+    <dd>A serialised data structure that represents the context of the latest
+    <code>fcm make</code> command at this destination.  It is a
+    <code>gzip</code> file containing data in the <a
+    href="http://perldoc.perl.org/Storable.html">Perl Storable</a> format.</dd>
+
+    <dt><samp>fcm-make.log -&gt; .fcm-make/log</samp></dt>
+    
+    <dd>A diagnostic log generated by the latest <code>fcm make</code> command
+    at this destination. The content should be equivalent to the diagnostic
+    output in STDOUT and STDERR at <code>-vv</code> verbosity.</dd>
+  </dl>
+
+  <h3 id="concept.name">FCM Make Concept: Context Name</h3>
+
+  <p>You may sometimes need to run <code>fcm make</code> in the same location of
+  the file system. Perhaps you are on a network with a shared file system, but
+  only certain tools are available on different hosts, e.g. you can only extract
+  on one host and build on a different host, you should name the makes so that
+  they don't end up overwriting each other's context, logs and other
+  outputs. To do so, you can use the <code>--name=NAME</code> option on the
+  command line, and/or the <a href="annex_cfg.html#make.name">name</a>
+  declaration in the configuration file. Using <code>--name=2</code> as an
+  example, you will get the following:</p>
+
+  <ul>
+    <li>The command will search for <code>fcm-make2.cfg</code> instead of
+    <code>fcm-make.cfg</code>.</li>
+
+    <li>The command will dump context, logs, etc with file names such as
+    <samp>.fcm-make2/*</samp>, <samp>fcm-make2-as-parsed.cfg</samp>,
+    <samp>fcm-make2.log</samp>, etc.</li>
+
+    <li>The command will only allow inheritance from a location where a
+    successful make with the same name exists.</li>
+  </ul>
+
+  <h2 id="extract">Extract</h2>
+
+  <p>The extract system provides an interface between the version control
+  system (currently Subversion) and the build system. Where appropriate, it
+  extracts code and combines changes from the repositories and other
+  user-defined locations to a directory tree suitable for feeding into the build
+  system.</p>
+
+  <p>The extract system supports the <code>--jobs=N</code> option of the
+  <code>fcm make</code> command. It uses <var>N</var> child processes to get
+  source trees information and to export source trees from their repositories in
+  parallel.</p>
+
+  <h3 id="extract.basic">Extract: Basic</h3>
+
+  <p>The following is an example of how to extract the source trees from a file
+  system path and the trunks of 2 known projects (version controlled in
+  Subversion repositories with a standard FCM layout):</p>
+  <pre>
+steps = extract                    # line 1
+extract.ns = ops var um            # line 2
+extract.location[ops] = $HOME/ops  # line 3
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 1</dfn>: the <code><a href=
+    "annex_cfg.html#make.steps">steps</a></code> declaration tells the make
+    system to invoke the extract system.</li>
+
+    <li><dfn>line 2</dfn>: the <code><a href=
+    "annex_cfg.html#make.extract.ns">extract.ns</a></code> declaration is used
+    to specify a space delimited list of names of the projects to extract.</li>
+
+    <li><dfn>line 3</dfn>: the <code><a href=
+    "annex_cfg.html#make.extract.location">extract.location</a></code>
+    declaration is used to specify the base source tree of a project
+    (<samp>ops</samp> in this case) to extract. The value of the declaration
+    can be a path in the file system or a location in a version control system.
+    In this case, the declaration specifies the base source tree of the
+    <samp>ops</samp> project to be a path in the file system at
+    <samp>$HOME/ops</samp>. If the base source tree of a specified project
+    name-space is not declared, the system will make an assumption. For a
+    project hosted in a Subversion repository, the system will assume
+    <samp>trunk@HEAD</samp> if the project URL is registered in the keyword
+    configuration file or with the <code><a href=
+    "annex_cfg.html#make.extract.location.primary">extract.location{primary}</a></code>
+    declaration (see below).</li>
+  </ul>
+
+  <p>If you save this file as <samp>fcm-make.cfg</samp> and invoke the
+  <code>fcm make</code> command you should end up with a source tree in the
+  current working directory that looks like (hidden path not shown):</p>
+  <pre>
+extract/ops/...
+extract/um/...
+extract/var/...
+</pre>
+
+  <p>The result of the extract can be found in the <samp>extract/</samp>
+  sub-directory. Note: The generated source tree will not contain any symbolic
+  links or hidden files (e.g. file names beginning with a <samp>.</samp>),
+  because the extract system ignores them.</p>
+
+  <div class="well">
+    <p><strong><span class="glyphicon glyphicon-pencil"
+    aria-hidden="true"></span> Note - project name-space and
+    location</strong></p>
+
+    <p>Whoever installed FCM at your site would have defined the name-spaces
+    and the locations of the common projects at your site using the keyword
+    configuration file at <samp>$FCM/etc/fcm-keyword.cfg</samp> (where
+    <samp>$FCM/bin</samp> is the path at which FCM is installed).
+    Alternatively, users can define their own set of project name-spaces and
+    their locations using <samp>$HOME/.metomi/fcm/keyword.cfg</samp>. For
+    information on keyword configuration files, please refer to <a href=
+    "system_admin.html#fcm-keywords">System Administration &gt; FCM
+    keywords</a>. If a project name-space is not defined in a keyword
+    configuration, it can be defined in the FCM make configuration using the
+    <code><a href=
+    "annex_cfg.html#make.extract.location.primary">extract.location{primary}</a></code>
+    declaration. E.g. to define the location of the <samp>foo</samp> project,
+    you can do: <samp>extract.location{primary}[foo] =
+    svn://server/repos/foo</samp>.</p>
+  </div>
+
+  <div class="well">
+    <p><strong><span class="glyphicon glyphicon-pencil"
+    aria-hidden="true"></span> Note - incremental mode</strong></p>
+
+    <p>Suppose you have already invoked <code>fcm make</code> using the above
+    configuration file. At a later time, you have made some changes to some of
+    the files in the source trees. Re-running <code>fcm make</code> on the same
+    configuration will trigger the incremental mode. In the incremental mode,
+    the extract system will update only those files that are modified. If the
+    last modified time (or last commit revision) of a source file in the
+    current extract differs from that in the previous extract, the system will
+    attempt a content comparison. The system updates the destination only if
+    the content and/or file access permission of the source differs from that
+    of the destination. To avoid the incremental mode and start afresh, invoke
+    <code>fcm make</code> with the <code>--new</code> option.</p>
+  </div>
+
+  <h3 id="extract.location-types">Extract: Location Types</h3>
+
+  <p>The extract system currently supports the following location types:</p>
+
+  <dl class="dl-horizontal">
+    <dt>fs</dt>
+
+    <dd>A readable location in the local file system. E.g.
+    <code>~/my-project</code>, <code>/home/lily/my-project</code>, etc.</dd>
+
+    <dt>ssh</dt>
+
+    <dd>A readable location in the file system of a remote host accessible via
+    <code>ssh</code> and <code>rsync</code>, specified in the form
+    <var>[USER@]HOST:PATH</var>. The <code>ssh</code> access must be
+    without a password, and you must be able to run the <a href=
+    "http://www.gnu.org/software/coreutils/">GNU coreutils</a> version of the
+    <code>find</code> and <code>stat</code> on the remote host. E.g.
+    <code>mylinuxbox:my-project</code>,
+    <code>holly@hpc:/data/holly/my-project</code>.</dd>
+
+    <dt>svn</dt>
+
+    <dd>A Subversion location, which may be a working copy or a valid Subversion
+    URL. Supported URL schemes are: <code>file</code>, <code>http</code>,
+    <code>https</code>, <code>svn</code> and <code>svn+ssh</code>. E.g.
+    <code>svn://mysvnhost/my-project/trunk@40778</code>,
+    <code>~/my-project@32734</code>.</dd>
+  </dl>
+
+  <p>See also <a href=
+  "annex_cfg.html#make.extract.location">extract.location</a>.</p>
+
+  <h3 id="extract.path-root">Extract: Redefine the Root of the Source Tree of a
+  Project</h3>
+
+  <p>Consider a project called <samp>foo</samp> with a source tree that looks
+  like:</p>
+  <pre>
+doc/...
+src/bar/...
+src/baz/...
+</pre>
+
+  <p>Suppose you are only interested in the contents of the <samp>src/</samp>
+  sub-tree. You can specify the root of the extract using the <code><a href=
+  "annex_cfg.html#make.extract.path-root">extract.path-root</a></code>
+  declaration. E.g.:</p>
+  <pre>
+steps = extract              # line 1
+extract.ns = foo             # line 2
+extract.path-root[foo] = src # line 3
+</pre>
+
+  <p>Running <code>fcm make</code> with this configuration should give a source
+  tree that looks like (hidden path not shown):</p>
+  <pre>
+extract/foo/bar/...
+extract/foo/baz/...
+</pre>
+
+  <h3 id="extract.path-filter">Extract: Filter the Paths in the Source Tree of
+  a Project</h3>
+
+  <p>Going back to the above source tree in the <samp>foo</samp> project,
+  imagine the <samp>src/bar/</samp> sub-directory contains:</p>
+  <pre>
+src/bar/wine/red.c
+src/bar/wine/rose.c
+src/bar/wine/white.c
+src/bar/...
+</pre>
+
+  <p>If you do not want <samp>src/bar/wine/rose.c</samp> in the extract, you
+  can ask for it to be excluded using the <code><a href=
+  "annex_cfg.html#make.extract.path-excl">extract.path-excl</a></code>
+  declaration. E.g.:</p>
+  <pre>
+steps = extract                          # line 1
+extract.ns = foo
+extract.path-root[foo] = src             # line 3
+extract.path-excl[foo] = bar/wine/rose.c # line 4
+</pre>
+
+  <p>Note: Because the root is redefined in line 3, the path in the
+  <code><a href=
+  "annex_cfg.html#make.extract.path-excl">extract.path-excl</a></code>
+  declaration in line 4 is declared from the new root level.</p>
+
+  <p>Running <code>fcm make</code> with this configuration should give a source
+  tree that looks like (hidden path not shown):</p>
+  <pre>
+extract/foo/bar/wine/red.c
+extract/foo/bar/wine/white.c
+extract/foo/bar/...
+extract/foo/baz/...
+</pre>
+
+  <p>On the other hand, if you only want <samp>src/bar/wine/rose.c</samp> in
+  the extract, you can ask the system to exclude everything in
+  <samp>src/bar/wine/</samp> but include <samp>src/bar/wine/rose.c</samp> using
+  the <code><a href=
+  "annex_cfg.html#make.extract.path-incl">extract.path-incl</a></code>
+  declaration. E.g.:</p>
+  <pre>
+steps = extract                          # line 1
+extract.ns = foo                         # line 2
+extract.path-root[foo] = src             # line 3
+extract.path-excl[foo] = bar/wine        # line 4
+extract.path-incl[foo] = bar/wine/rose.c # line 5
+</pre>
+
+  <p>Running <code>fcm make</code> with this configuration should give a source
+  tree that looks like (hidden path not shown):</p>
+  <pre>
+extract/foo/bar/wine/rose.c
+extract/foo/bar/...
+extract/foo/baz/...
+</pre>
+
+  <h3 id="extract.location-diff">Extract: Combining Changes from Multiple
+  Branches of a Project</h3>
+
+  <p>We have so far only dealt with extracts from a single base source tree in
+  each project. The extract system can also be used to <em>combine</em> changes
+  from different source trees (against a base source tree) of a project.</p>
+
+  <p>E.g. consider a project called <samp>food</samp>. In the latest release
+  (<samp>trunk@3739</samp>), the source tree looks like this:</p>
+  <pre>
+doc/...
+src/egg/boiled.y
+src/egg/microwave.x
+src/egg/omelette.c
+src/egg/poached.f90
+src/...
+</pre>
+
+  <p>Jamie, Gordon and Rick are all developing changes against the latest
+  release of the project.</p>
+
+  <p>Jamie has made the following changes (displayed using the notation of
+  <code>svn status</code>) in his branch at
+  <samp>branches/dev/jamie/r3739_t381@3984</samp>:</p>
+  <pre>
+D      src/egg/boiled.y
+A      src/egg/fried.pl
+M      src/egg/omelette.c
+</pre>
+
+  <p>Gordon has made the following changes in his branch at
+  <samp>branches/dev/gordon/r3739_t376@3993</samp>:</p>
+  <pre>
+M      src/egg/omelette.c
+M      src/egg/poached.f90
+</pre>
+
+  <p>Rick has made the following changes in his working copy at
+  <samp>~rick/food</samp>, but has yet to commit his changes back:</p>
+  <pre>
+M      src/egg/boiled.y
+A      src/egg/scrambled.bash
+</pre>
+
+  <p>To combine their changes in an extract, the FCM make configuration file
+  should look like:</p>
+  <pre id="example.extract">
+steps = extract
+extract.ns = food
+extract.location[food] = trunk@3739
+extract.location{diff}[food] = \
+    branches/dev/jamie/r3739_t381@3984 \
+    branches/dev/gordon/r3739_t376@3993 \
+    ~rick/food
+</pre>
+
+  <p>Here we have an <code><a href=
+  "annex_cfg.html#make.extract.location">extract.location</a></code>
+  declaration like before. This time it is pointing to the latest release of
+  the <samp>food</samp> project. The changes against the base source tree are
+  declared using the <code><a href=
+  "annex_cfg.html#make.extract.location.diff">extract.location{diff}</a></code>
+  declaration.</p>
+
+  <p>Invoking <code>fcm make</code> with this configuration file will result in
+  a source tree that looks like (hidden file not shown):</p>
+  <pre>
+extract/doc/...
+extract/src/egg/fried.pl
+extract/src/egg/microwave.x
+extract/src/egg/omelette.c
+extract/src/egg/poached.f90
+extract/src/egg/scrambled.bash
+extract/src/...
+</pre>
+
+  <p>Note:</p>
+
+  <ul>
+    <li>There is no <samp>extract/src/egg/boiled.y</samp> file in the extract
+    tree because it is deleted by Jamie's branch. Even though this file is
+    modified in Rick's working copy, the extract will ignore this and assume
+    that the deletion takes precedence.</li>
+
+    <li>The <samp>extract/src/egg/fried.pl</samp> file comes from Jamie's
+    branch.</li>
+
+    <li>The <samp>extract/src/egg/microwave.x</samp> file comes from the latest
+    release (base).</li>
+
+    <li>The <samp>extract/src/egg/omelette.c</samp> file is the result of a
+    merge of the changes in Jamie's and Gordon's branches. This example assumes
+    that there is no conflict in the merge. If the merge results in a conflict,
+    the extract will fail.</li>
+
+    <li>The <samp>extract/src/egg/poached.f90</samp> file comes from Gordon's
+    branch.</li>
+
+    <li>The <samp>extract/src/egg/scrambled.bash</samp> file comes from Rick's
+    working copy.</li>
+  </ul>
+
+  <h3 id="extract.inherit">Extract Inheritance</h3>
+
+  <p>If a previous extract with a similar configuration exists in another
+  location, it can be more efficient to inherit from this previous extract in
+  your current extract. This works like a normal incremental extract, except
+  that your extract will only contain the changes you have specified (compared
+  with the inherited extract) instead of the full directory tree in the
+  destination. This type of incremental extract is useful in several ways. For
+  instance:</p>
+
+  <ul>
+    <li>It is fast, because you only have to extract files that you have
+    changed.</li>
+
+    <li>The subsequent build will also be fast, since it will act like an
+    incremental build.</li>
+
+    <li>You do not need write access to the original extract. A system
+    administrator can set up a stable version in a central account, which
+    developers can then inherit from.</li>
+
+    <li>You want an incremental extract, but you need to leave the original
+    extract unmodified.</li>
+  </ul>
+
+  <p>Consider the <a href="#example.extract">previous example</a>. Imagine an
+  extract already exists for the latest release for the <samp>food</samp>
+  project at <samp>/home/food/latest/</samp>, and now you want to test the
+  changes introduced by Jamie, Gordon and Rick. You can <code><a href=
+  "annex_cfg.html#make.use">use</a></code> the original extract with the
+  changes:</p>
+  <pre>
+use = /home/food/latest
+extract.location{diff}[food] = \
+    branches/dev/jamie/r3739_t381@3984 \
+    branches/dev/gordon/r3739_t376@3993 \
+    ~rick/food
+</pre>
+
+  <p>Invoking <code>fcm make</code> with this configuration file will result in
+  a source tree that looks like (hidden file not shown):</p>
+  <pre>
+extract/src/egg/fried.pl
+extract/src/egg/omelette.c
+extract/src/egg/poached.f90
+extract/src/egg/scrambled.bash
+</pre>
+
+  <p>The extract will work in an incremental-like mode. The only difference is
+  that the original extract at <samp>/home/food/latest/</samp> will be left
+  untouched, and the new extract will contain only the changes introduced by
+  the diff locations. Note that, although
+  <samp>extract/src/egg/boiled.y</samp> remains in the original extract, it
+  will not be used in any subsequent build step.</p>
+
+  <dl>
+    <dt>Extract inheritance limitation</dt>
+
+    <dd>
+      <p>Extract inheritance allows you to add more diff locations to a project,
+      but you should not include any other declarations relating to the extract.
+      Doing so is not safe and should trigger an exception.</p>
+
+      <p>In some situations this implies that it will not be possible to use
+      inherited extracts. You should use a new extract if, for instance, a new
+      diff location contains a change which requires the use of source files in
+      a previously excluded name-space.</p>
+    </dd>
+  </dl>
+
+  <h3 id="extract.diagnostic">Extract Diagnostic</h3>
+
+  <p>The amount of diagnostic messages generated by the extract system is
+  dependent on the diagnostic verbosity level that can be modified by the
+  <code>-v</code> and <code>-q</code> options to the <code>fcm make</code>
+  command.</p>
+
+  <p>The following is a list of diagnostic output at each verbosity level:</p>
+
+  <dl>
+    <dt>-q</dt>
+
+    <dd>
+      <ul>
+        <li>Exceptions.</li>
+      </ul>
+    </dd>
+
+    <dt>default</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the -q level.</li>
+
+        <li>Start time of the extract.</li>
+
+        <li>The number and location of each source tree in each project. The
+        base source tree is number 0. E.g.:
+          <pre>
+[info] location um: 0: svn://fcm2/UM_svn/UM/trunk@11732
+[info] location um: 1: svn://fcm2/UM_svn/UM/branches/dev/Share/VN7.3_hg3_dust_443@11858
+[info] location um: 2: svn://fcm2/UM_svn/UM/branches/dev/Share/VN7.3_hg3_ccw_precip@11857
+[info] location um: 3: svn://fcm2/UM_svn/UM/branches/dev/hadco/VN7.3_HG3_porting_lsp_fixes@12029
+...
+</pre>
+        </li>
+
+        <li>The number of targets by their destination status and their source
+        status. E.g.:
+          <pre>
+[info]   dest:    3 [A added]
+[info]   dest:  134 [a added, overriding inherited]
+[info]   dest:    6 [d deleted, overriding inherited]
+[info]   dest: 2818 [U unchanged]
+[info] source:    3 [A added by a diff source tree]
+[info] source:    6 [D deleted by a diff source tree]
+[info] source:   16 [G merged from 2+ diff source trees]
+[info] source:  118 [M modified by a diff source tree]
+[info] source: 2818 [U from base]
+</pre>
+        </li>
+
+        <li>Total time.</li>
+      </ul>
+    </dd>
+
+    <dt>-v</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the default level.</li>
+
+        <li>The destination and source status for each modified target. E.g.:
+          <pre>
+...
+[info] aM um:0,13    atmosphere/short_wave_radiation/r2_lwrad3c.F90
+[info] aG um:0,6,13  control/top_level/scm_main.F90
+[info] AA um:-,8     include/constant/cnv_parc_lim.h
+...
+</pre>
+
+          <p>The 2 letters following <samp>[info]</samp> is the destination
+          status and the source status of the target. This is followed by the
+          name-space of the project, a colon and the source tree number(s)
+          providing the source for the target (in a comma separated list). This
+          is then followed by the name-space of the target path. The source
+          tree number <samp>0</samp> denotes the base source tree, a dash
+          <samp>-</samp> in place of a <samp>0</samp> means that the source
+          only exists in a diff source tree.</p>
+        </li>
+      </ul>
+    </dd>
+
+    <dt>-vv</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the -v level.</li>
+
+        <li>Each shell command invoked with elapsed time and return code.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <p>Here is an explanation of each target destination status:</p>
+
+  <dl>
+    <dt><samp>[A added]</samp></dt>
+
+    <dd>Target newly added to the destination.</dd>
+
+    <dt><samp>[a added, overriding inherited]</samp></dt>
+
+    <dd>Target added to the current extract destination, i.e. modified compared
+    with the target with the same name-space in an inherited extract
+    destination.</dd>
+
+    <dt><samp>[D deleted]</samp></dt>
+
+    <dd>Target deleted from the extract destination during an incremental
+    extract.</dd>
+
+    <dt><samp>[d deleted, overriding inherited]</samp></dt>
+
+    <dd>Target deleted from the current extract destination, i.e. a target with
+    the same name-space exists in an inherited extract destination.</dd>
+
+    <dt><samp>[M modified]</samp></dt>
+
+    <dd>Target modified in the extract destination during an incremental
+    extract.</dd>
+
+    <dt><samp>[U unchanged]</samp></dt>
+
+    <dd>Target unchanged compared with the previous (or any inherited)
+    extract.</dd>
+
+    <dt><samp>[? unknown]</samp></dt>
+
+    <dd>Target does not have a destination. This destination status is normally
+    associated with the <samp>[D deleted]</samp> source status.</dd>
+  </dl>
+
+  <p>Here is an explanation of each target source status:</p>
+
+  <dl>
+    <dt><samp>[A added by a diff source tree]</samp></dt>
+
+    <dd>The source of the target comes from a diff source tree, and the base
+    source tree does not have a source in the same name-space.</dd>
+
+    <dt><samp>[D deleted by a diff source tree]</samp></dt>
+
+    <dd>Target is deleted by a diff source tree, (i.e. exists in base source
+    tree, but missing from a diff source tree).</dd>
+
+    <dt><samp>[G merged from 2+ diff source trees]</samp></dt>
+
+    <dd>The source of the target comes from 2 or more diff source trees, (i.e.
+    the actual source is the result of a merge between all the changes).</dd>
+
+    <dt><samp>[M modified by a diff source tree]</samp></dt>
+
+    <dd>The source of the target comes from a diff source tree.</dd>
+
+    <dt><samp>[U from base]</samp></dt>
+
+    <dd>The source of the target comes from the base source tree, (i.e. the
+    source is unchanged by any diff source tree).</dd>
+
+    <dt><samp>[? unknown]</samp></dt>
+
+    <dd>The target has no source. This source status is normally displayed in an
+    incremental extract, where a target in a previous extract is not a target in
+    the current extract, and is normally associated with the <samp>[D
+    deleted]</samp> destination status.</dd>
+  </dl>
+
+  <h2 id="mirror">Mirror</h2>
+
+  <p>The mirror system provides a way to mirror the results of the make steps
+  to another location, where the FCM make may need to continue. It is typically
+  used after an extract to set up the build on an alternate platform.</p>
+
+  <h3 id="mirror.basic">Mirror: Basic</h3>
+
+  <p>Consider the following example:</p>
+  <pre>
+steps = extract mirror                            # 1
+# ... some extract declarations
+mirror.target = user@somewhere:/path/in/somewhere # 2
+mirror.prop{config-file.name} = -at-somewhere     # 3
+mirror.prop{config-file.steps} = preprocess build # 4
+# ... some preprocess declarations
+# ... some build declarations
+</pre>
+
+  <p>When the system runs with this configuration, the system will mirror the
+  result of the extract to the <a href=
+  "annex_cfg.html#make.mirror.target">mirror.target</a>, i.e.
+  <samp>somewhere:/path/in/somewhere</samp> using <code>ssh</code> and
+  <code>rsync</code>.</p>
+  
+  <p>With the <a href=
+  "annex_cfg.html#make.mirror.prop.config-file.name">config-file.name</a>
+  property set at point 3 and the <a href=
+  "annex_cfg.html#make.mirror.prop.config-file.steps">config-file.steps</a>
+  property set at point 4, it will write a configuration file called
+  <samp>fcm-make-at-somewhere.cfg</samp> with the make name set as
+  <samp>-at-somewhere</samp> in the mirror target so that the
+  <samp>preprocess</samp> and <samp>build</samp> steps can continue there.</p>
+
+  <h3 id="mirror.diagnostic">Mirror Diagnostic</h3>
+
+  <p>The amount of diagnostic messages generated by the mirror system is
+  dependent on the diagnostic verbosity level that can be modified by the
+  <code>-v</code> and <code>-q</code> options to the <code>fcm make</code>
+  command.</p>
+
+  <p>The following is a list of diagnostic output at each verbosity level:</p>
+
+  <dl>
+    <dt>-q</dt>
+
+    <dd>
+      <ul>
+        <li>Exceptions.</li>
+      </ul>
+    </dd>
+
+    <dt>default</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the -q level.</li>
+
+        <li>Start time of the mirror.</li>
+
+        <li>The updated destination and its source.</li>
+
+        <li>Total time.</li>
+      </ul>
+    </dd>
+
+    <dt>-v</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the default level.</li>
+      </ul>
+    </dd>
+
+    <dt>-vv</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the -v level.</li>
+
+        <li>Each shell command invoked with elapsed time and return code.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h2 id="build">Build</h2>
+
+  <p>The build system performs actions on a set of source files using its
+  predefined logic and the properties specified in the configuration. For
+  example, it will attempt to create a binary executable for a source file
+  containing a Fortran program.</p>
+
+  <p>The build system supports the <code>--jobs=N</code> option of the <code>fcm
+  make</code> command. It uses <var>N</var> child processes to analyse the
+  source files and to update the targets in parallel.</p>
+
+  <h3 id="build.basic">Build: Basic</h3>
+
+  <p>Consider a source tree at <samp>$HOME/my-source-tree/</samp> containing
+  some Fortran source files including at least one with a main program (and
+  maybe other supported types of source files), you can set up an FCM make
+  configuration file to build an executable. E.g.:</p>
+  <pre>
+steps = build
+build.target{task} = link
+build.source = $HOME/my-source-tree
+</pre>
+
+  <p>In this simple 3 line configuration, the <code><a href=
+  "annex_cfg.html#make.steps">steps</a></code> declaration tells the make
+  system to invoke the build system, the <code><a href=
+  "annex_cfg.html#make.build.target">build.target</a></code> declaration tells
+  the system to build any targets which require linking (i.e. any main
+  programs), and the <code><a href=
+  "annex_cfg.html#make.build.source">build.source</a></code> declaration
+  specifies the location of the source tree.</p>
+
+  <p>If you save this file as <samp>fcm-make.cfg</samp> and invoke the
+  <code>fcm make</code> command it should attempt to build the source tree in
+  the current working directory, using the default properties. If the default
+  Fortran compiler <samp>gfortran</samp> is installed, and nothing goes wrong,
+  you will end up with a directory tree that looks like (hidden path not
+  shown):</p>
+  <pre>
+build/bin/...
+build/include/...
+build/o/...
+</pre>
+
+  <p>The result of the build can be found in the sub-directories of the
+  <samp>build/</samp> sub-directory. Each <samp>build/*/</samp> sub-directory
+  contains a category of targets:</p>
+
+  <dl>
+    <dt id="build.category.bin"><samp>bin</samp></dt>
+
+    <dd>e.g. executable binary and script.</dd>
+
+    <dt id="build.category.etc"><samp>etc</samp></dt>
+
+    <dd>e.g. data files.</dd>
+
+    <dt id="build.category.include"><samp>include</samp></dt>
+
+    <dd>e.g. include files and Fortran module definition files.</dd>
+
+    <dt id="build.category.lib"><samp>lib</samp></dt>
+
+    <dd>e.g. object archives.</dd>
+
+    <dt id="build.category.o"><samp>o</samp></dt>
+
+    <dd>e.g. object files</dd>
+  </dl>
+
+  <p>Sub-directories are only created as necessary, so you may not find all of
+  the above in your destination tree.</p>
+
+  <p>If <code>fcm make</code> is invoked with the <code>--archive</code> option,
+  sub-directories in categories containing intermediate build files (or any
+  category specified in the
+  <a href="annex_cfg.html#make.build.prop.archive-ok-target-category">
+  archive-ok-target-category</a>
+  property) will be put into TAR-GZIP files. E.g. with the default setting,
+  <samp>include/</samp> and <samp>o/</samp> will become
+  <samp>include.tar.gz</samp> and <samp>o.tar.gz</samp> respectively.</p>
+
+  <p>To use a different compiler and/or compiler options for Fortran/C/C++, you
+  use the <a href="annex_cfg.html#make.build.prop">build.prop</a> declaration to
+  redefine the build properties. E.g.:</p>
+  <pre>
+steps = build
+build.target{task} = link
+build.source = $HOME/my-source-tree
+
+# Set Fortran compiler/linker
+build.prop{fc} = ifort
+# Set Fortran compiler options
+build.prop{fc.flags} = -i8 -r8 -O3
+# Add include paths to Fortran compiler
+build.prop{fc.include-paths} = /a/path/to/include /more/path/to/include
+# Set link libraries for Fortran executables
+build.prop{fc.lib-paths} = /path/to/my-lib
+build.prop{fc.libs} = mine
+# Set C compiler/linker
+build.prop{cc} = icc
+# Set C compiler options
+build.prop{cc.flags} = -O3
+# Set C++ compiler options
+build.prop{cxx.flags} = -O2
+# Set link libraries for C executables
+build.prop{cc.lib-paths} = /path/to/my-lib /path/to/your-lib
+build.prop{cc.libs} = mine yours
+# Set linker, if compiler cannot be used as linker
+#build.prop{ld} = ld
+</pre>
+
+  <h3 id="build.source-locations">Build Source Locations</h3>
+
+  <p>The build system locates its source files from various places,
+  including:</p>
+
+  <ul>
+    <li>Inherited locations. (See <a href="#build.inherit">Build
+    Inheritance</a>.)</li>
+
+    <li>Usable target locations of previous steps in the make. E.g. targets of
+    an extract step, and <samp><a href=
+    "#preprocess.category.src">src</a></samp> category targets of a preprocess
+    step can both be source files of the build system. The <code><a href=
+    "annex_cfg.html#make.build.prop.no-step-source">build.prop{no-step-source}</a></code>
+    declaration can be used to switch off this behaviour.</li>
+
+    <li>Locations specified by the <code><a href=
+    "annex_cfg.html#make.build.source">build.source</a></code> declaration.
+    Note: If you assign a relative path to the <code><a href=
+    "annex_cfg.html#make.build.source">build.source</a></code> declaration, the
+    system will assume the path to be relative to the make destination (not the
+    current working directory, unless they happen to be the same).</li>
+  </ul>
+
+  <p>There are situations when it may not be desirable to consider every source
+  file in a build. You can apply filters by source file name-spaces using the
+  <code><a href="annex_cfg.html#make.build.ns-excl">build.ns-excl</a></code>
+  and <code><a href=
+  "annex_cfg.html#make.build.ns-incl">build.ns-incl</a></code> declarations.
+  E.g.:</p>
+  <pre>
+# To include items in "foo" and "bar/baz" only
+build.ns-excl = /              # exclude everything ...
+build.ns-incl = foo bar/baz    # but include items from these name-spaces
+</pre>
+
+  <h3 id="build.source-ns">Build Source Name-spaces and Properties</h3>
+
+  <p>Each source file is assigned a name-space (which is used to fine tune the
+  build properties, such as the compiler flags). If the source file is a target
+  of an extract, the name-space will be the same as the extract target (the
+  relative path of the <samp>extract/</samp> sub-directory in the make
+  destination). If the source file is a file in a source tree specified by
+  <code><a href="annex_cfg.html#make.build.source">build.source</a></code>, the
+  name-space is the relative path to the specified value. The <code><a href=
+  "annex_cfg.html#make.build.source">build.source</a></code> declaration also
+  accepts an optional name-space, in which case the name-space of each source
+  file in the tree will be prefixed with the specified name-space. Suppose you
+  have a source tree in <samp>$HOME/food</samp>:</p>
+  <pre>
+$HOME/food/egg.c
+$HOME/food/ham.f90
+</pre>
+
+  <p>If you specify the source tree with <samp>build.source =
+  $HOME/food</samp>, then <samp>$HOME/food/egg.c</samp> will be given the
+  name-space <samp>egg.c</samp> and <samp>$HOME/food/ham.f90</samp> will be
+  given the name-space <samp>ham.f90</samp>.</p>
+
+  <p>On the other hand, if you specify the source tree with
+  <samp>build.source[food] = $HOME</samp>, then
+  <samp>$HOME/food/egg.c</samp> will be given the name-space
+  <samp>food/egg.c</samp> and <samp>$HOME/food/ham.f90</samp> will be given the
+  name-space <samp>food/ham.f90</samp>.</p>
+
+  <p>The name-space is organised in a simple hierarchy. For instance, the
+  <samp>foo/bar/egg</samp> name-space belongs to <samp>foo/bar</samp>, which
+  belongs to <samp>foo</samp>, which belongs to the root name-space. (The root
+  name-space is either an empty string or a <samp>/</samp>.)</p>
+
+  <p>For instance, you can set the flags of the Fortran compiler using the
+  <code><a href=
+  "annex_cfg.html#make.build.prop.fc.flags">build.prop{fc.flags}</a></code>
+  declaration at different name-space levels:</p>
+  <pre>
+# The global Fortran compiler flags
+build.prop{fc.flags} = -O3
+
+# The Fortran compiler flags for the "food" name-space
+build.prop{fc.flags}[food] = -O2 -i8 -r8
+
+# The Fortran compiler flags for a source file
+build.prop{fc.flags}[food/bacon.f90] = -O0 -g -C
+</pre>
+
+  <h3 id="build.source-types">Build Source Types</h3>
+
+  <p>Before the build system can do anything with its source files, it needs to
+  know what they are. It determines the type of each source file by looking at
+  its file name extension, then the file name itself, and then the
+  <code>#!</code> line for a text file. Source files without a type are treated
+  as data files.</p>
+
+  <p>The following types are associated with file extensions:</p>
+
+  <dl>
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.c">c</a> (C source
+    file)</dt>
+
+    <dd>.c .i .m .mi</dd>
+
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.cxx">cxx</a> (C++
+    source file)</dt>
+
+    <dd>.cc .cp .cxx .cpp .CPP .c++ .C .mm .M .mii</dd>
+
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.fortran">fortran</a>
+    (Fortran source file)</dt>
+
+    <dd>.F .FOR .FTN .F90 .F95 .f .for .ftn .f90 .f95 .inc</dd>
+
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.h">h</a> (Preprocessor
+    header file)</dt>
+
+    <dd>.h</dd>
+
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.script">script</a>
+    (script in various languages)</dt>
+
+    <dd>(empty)</dd>
+  </dl>
+
+  <p>The <code>prop{file-ext.type} = extensions</code> declaration can be used
+  to modify the extensions associated with a type. E.g. if you need to add
+  <samp>.fort</samp> as a file extension for a Fortran source file, you can
+  do:</p>
+  <pre>
+build.prop{file-ext.fortran} = .F .FOR .FORT .FTN .F90 .F95 \
+                               .f .for .fort .ftn .f90 .f95 .inc
+</pre>
+
+  <p>You can associate file names to some file types using a
+  <code>prop{file-pat.type} = regular-expression</code> declaration. E.g. if
+  you have executable scripts in the source tree with no <code>#!</code> lines
+  but are recognised by a <samp>*Scr_*</samp> pattern of their file names, you
+  can specify a regular expression to match their file names using the <a href=
+  "annex_cfg.html#make.build.prop.file-pat.script">file-pat.script</a>
+  property:</p>
+  <pre>
+build.prop{file-pat.script} = (?msx-i:\w+Scr_\w+)
+</pre>
+
+  <p>All other text files with a <code>#!</code> line are recognised as scripts
+  by the build system.</p>
+
+  <h3 id="build.source-analysis">Build Source Analysis</h3>
+
+  <p>Each source file with a known type (that is not ignored) is analysed by
+  the build system for dependencies and other information. Here is a list of
+  what the system looks for in each type of file:</p>
+
+  <dl>
+    <dt>c and cxx</dt>
+
+    <dd>
+      <p><dfn>main program</dfn>: e.g. <samp>int main()</samp>.</p>
+
+      <p><dfn>dependency on include</dfn>: e.g. <samp>#include
+      "name.h"</samp>.</p>
+
+      <p><dfn>dependency on object</dfn>: e.g. <samp>/* depends on: name.o
+      */</samp> (for <a href="#build.source-analysis.legacy">legacy
+      support</a>).</p>
+    </dd>
+
+    <dt>fortran</dt>
+
+    <dd>
+      <p><dfn>main program</dfn>: e.g. <samp>program name</samp>.</p>
+
+      <p><dfn>list of symbols</dfn>: i.e. names of top level program units
+      including blockdata, function, module, program and subroutine.</p>
+
+      <p><dfn>dependency on include</dfn>: e.g. <samp>#include "name.h"</samp>
+      and <samp>include 'name.f90'</samp>.</p>
+
+      <p><dfn>dependency on module</dfn>: e.g. <samp>use name</samp>.</p>
+
+      <p><dfn>dependency on object</dfn>: e.g. <samp>! depends on:
+      name.o</samp> (for <a href="#build.source-analysis.legacy">legacy
+      support</a>).</p>
+    </dd>
+
+    <dt>h</dt>
+
+    <dd>
+      <p><dfn>dependency on include</dfn>: e.g. <samp>#include
+      "file-name"</samp> (and <samp>include 'file-name'</samp> for <a href=
+      "#build.source-analysis.legacy">legacy support</a>).</p>
+    </dd>
+
+    <dt>script</dt>
+
+    <dd>
+      <p><dfn>dependency on executable</dfn>: e.g. <samp># calls: name</samp>
+      (for <a href="#build.source-analysis.legacy">legacy support</a>).</p>
+    </dd>
+  </dl>
+
+  <div class="well">
+    <p><strong><span class="glyphicon glyphicon-pencil"
+    aria-hidden="true"></span> Note: The following features are for
+    legacy support.</strong></p>
+
+    <dl id="build.source-analysis.legacy">
+      <dt><code>DEPENDS ON: x</code> directives in C/Fortran source files</dt>
+
+      <dd>The <code>DEPENDS ON: x</code> directive can be used to identify
+      dependencies on other compiled objects. However, it is much better to
+      specify this kind of dependency information in the configuration for the
+      build where necessary. In any case, in modern Fortran code almost all
+      dependencies should be identified automatically via the use of modules
+      and/or interface files.</dd>
+
+      <dt><code>calls: x</code> directives in scripts</dt>
+
+      <dd>The <code>calls: x</code> directive can be used to identify a
+      dependency on another executable. However, it is much better to specify
+      this kind of dependency information in the configuration for the build, and
+      leave the source code to concentrate on the run time logic.</dd>
+
+      <dt><samp>*.h</samp> files as Fortran include files</dt>
+
+      <dd><samp>*.h</samp> files are normally identified as C header files.
+      However, they are also being used by some old Fortran programs as include
+      files. Therefore, when the system analyses a <samp>*.h</samp> file, it has
+      to detect the Fortran include syntax, i.e. <samp>include 'file-name'</samp>
+      as well as the regular C preprocessor include syntax.</dd>
+    </dl>
+  </div>
+
+  <div class="well">
+    <p><strong><span class="glyphicon glyphicon-pencil"
+    aria-hidden="true"></span> Note: Dependency Analysis and Fortran
+    OpenMP Sentinels.</strong></p>
+
+    <p>The build system recognises statements with Fortran OpenMP sentinels that
+    affect build dependencies. E.g.:</p>
+
+    <pre>
+!$ USE my_omp_mod, ONLY: my_omp_sub
+! ...
+!$ INCLUDE 'my_omp_logic'
+</pre>
+
+    <p>These dependencies are normally ignored. However, if a relevant
+    <code>build.prop{fc.flag-omp}</code> property is specified, the build system
+    will treat these statements as normal dependency statements.</p>
+  </div>
+
+  <p>There are some situations when it is not possible for the system to
+  identify a dependency. E.g. a Fortran source file may depend on external
+  objects that are not detected by the automatic analysis. Therefore, the
+  system allows you to specify manual dependencies in the configuration file
+  using the <code>build.prop{dep.type}</code> and
+  <code>build.prop{ns-dep.type}</code> declarations. E.g.:</p>
+  <pre>
+# Tell the system that (the object of) food/egg.c depends on chicken.o
+build.prop{dep.o}[food/egg.c] = chicken.o
+
+# Tell the system that (the object of) meal/big.c depends on all objects in the
+# "food" and "drink" name-spaces
+build.prop{ns-dep.o}[meal/big.c] = food drink
+</pre>
+
+  <p>Like all declarations that accept name-spaces, if you specify a name-space
+  in this declaration, the property will apply to all source files in the
+  name-space. If you do not specify a name-space, it applies to the root
+  name-space (i.e. globally to all relevant source files).</p>
+
+  <p>The following manual dependency declarations are recognised:</p>
+
+  <dl>
+    <dt><a href=
+    "annex_cfg.html#make.build.prop.dep.bin">build.prop{dep.bin}</a></dt>
+
+    <dd>Specifies a list of dependencies on a script or a binary
+    executable.</dd>
+
+    <dt><a href=
+    "annex_cfg.html#make.build.prop.dep.f.module">build.prop{dep.f.module}</a></dt>
+
+    <dd>Specifies a list of Fortran module import dependencies. Note: a
+    dependency on a Fortran module called <samp>module_1</samp> becomes an
+    <dfn>include</dfn> dependency on <samp>module_1.mod</samp> when the system
+    turns the source file into its targets.</dd>
+
+    <dt><a href=
+    "annex_cfg.html#make.build.prop.dep.include">build.prop{dep.include}</a></dt>
+
+    <dd>Specifies a list of include file dependencies.</dd>
+
+    <dt><a href=
+    "annex_cfg.html#make.build.prop.dep.o">build.prop{dep.o}</a></dt>
+
+    <dd>Specifies a list of link-time object dependencies.</dd>
+
+    <dt><a href=
+    "annex_cfg.html#make.build.prop.dep.o.special">build.prop{dep.o.special}</a></dt>
+
+    <dd>Specifies a list of special type of link-time object dependencies.
+    Normally, an object file can be put in an object archive before being
+    linked with the main object. There are special cases when an object file
+    must be specified on the command line of the linker. (E.g. an object file
+    containing a Fortran blockdata program unit.) This special behaviour must
+    be declared using this declaration.</dd>
+
+    <dt><a href=
+    "annex_cfg.html#make.build.prop.ns-dep.o">build.prop{ns-dep.o}</a></dt>
+
+    <dd>Specifies a list of link-time object dependencies on all objects in the
+    specified name-space.</dd>
+  </dl>
+
+  <p>There are times when you know that your source tree does not contain a
+  particular type of dependency, in which case you can switch off the automatic
+  analysis by using the <code>build.prop{no-dep.type}</code> declaration. E.g.
+  if you know that all include files in the <samp>food</samp> name-space are
+  provided outside of the source tree, you can do:</p>
+  <pre>
+# Do not check for "include" dependencies
+build.prop{no-dep.include}[food] = *
+</pre>
+
+  <p>All the types supported by the <code>build.prop{dep.type}</code>
+  declarations are supported by the <code>build.prop{no-dep.type}</code>,
+  except that there is no <code>build.prop{no-dep.o.special}</code>
+  (because this type of dependency is never automatic).</p>
+
+  <h3 id="build.target-source">Build Targets from Source Files</h3>
+
+  <p>The system derives the build targets from the source files. E.g. a C
+  source file <samp>egg.c</samp> is turned into a compile target to generate
+  <samp>egg.o</samp>.</p>
+
+  <p>The following is a list of what targets are available for each type of
+  file. The title of each item in the list is in the format <dfn>source type
+  -&gt; target key</dfn>. The <dfn>description</dfn> of each target describes
+  what the target is, and where appropriate, explains how the target keys are
+  named. The <dfn>task</dfn> is the action the target needs to perform to get
+  up to date. The <dfn>category and destination</dfn> is the sub-directory and
+  destination of the target. The <dfn>properties</dfn> are the list of
+  properties that may be used by the <dfn>task</dfn> to update the target. The
+  <dfn>dependencies</dfn> list the types of dependencies the target may have.
+  The <dfn>update if</dfn> is the condition when the target is considered out
+  of date. The <dfn>pass on</dfn> information is a list of dependeny types
+  which a target can pass on the status, (see <a href=
+  "#build.target-update">Build Targets Update in Incremental Mode</a> for an
+  explanation of what this means.)</p>
+
+  <dl>
+    <dt>c/cxx -&gt; name</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: source file as an include file.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.install">install</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.include">include</a> and include/name</p>
+
+      <p><dfn>dependencies</dfn>: include and o (object).</p>
+
+      <p><dfn>update if</dfn>: source file is modified.</p>
+
+      <p><dfn>pass on</dfn>: include, o and o.special.</p>
+    </dd>
+
+    <dt>c/cxx -&gt; name.o</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: object file. The file is named by mapping the
+      base name of the original source file in lower case characters, with the
+      file extension replaced by the first value of the <code><a href=
+      "annex_cfg.html#make.build.prop.file-ext.o">file-ext.o</a></code>
+      property.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.compile">compile</a> (cc).</p>
+
+      <p><dfn>category and destination</dfn>: <a href="#build.category.o">o</a>
+      and o/name.o</p>
+
+      <p><dfn>properties</dfn>: <a href=
+      "annex_cfg.html#make.build.prop.cc">cc</a>, <a href=
+      "annex_cfg.html#make.build.prop.cc.flags">cc.flags</a>, <a href=
+      "annex_cfg.html#make.build.prop.cc.defs">cc.defs</a>, <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-compile">cc.flag-compile</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-define">cc.flag-define</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-include">cc.flag-include</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.include-paths">cc.include-paths</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-omp">cc.flag-omp</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-output">cc.flag-output</a></p>
+
+      <p><dfn>dependencies</dfn>: include and o (object).</p>
+
+      <p><dfn>update if</dfn>: source file or any of the required properties
+      are modified, or if any include dependencies are updated.</p>
+
+      <p><dfn>pass on</dfn>: o and o.special.</p>
+    </dd>
+
+    <dt>c/cxx (with main function) -&gt; name.exe</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: binary executable. The file is named after the
+      base name of the original source file, with the file extension replaced by
+      the first value of the <code><a href=
+      "annex_cfg.html#make.build.prop.file-ext.bin">file-ext.bin</a></code>
+      property.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.link">link</a> (cc).</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.bin">bin</a> and bin/name.exe</p>
+
+      <p><dfn>properties</dfn>:
+      <a href="annex_cfg.html#make.build.prop.ar">ar</a>,
+      <a href="annex_cfg.html#make.build.prop.ar.flags">ar.flags</a>,
+      <a href="annex_cfg.html#make.build.prop.file-ext.a">file-ext.a</a>,
+      <a href="annex_cfg.html#make.build.prop.cc">cc</a>,
+      <a href="annex_cfg.html#make.build.prop.cc.flags-ld">cc.flags-ld</a>,
+      <a href="annex_cfg.html#make.build.prop.cc.flag-lib">cc.flag-lib</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-lib-path">cc.flag-lib-path</a>,
+      <a href="annex_cfg.html#make.build.prop.cc.libs">cc.libs</a>,
+      <a href="annex_cfg.html#make.build.prop.cc.lib-paths">cc.lib-paths</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-omp">cc.flag-omp</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.cc.flag-output">cc.flag-output</a></p>
+
+      <p><dfn>dependencies</dfn>: name.o and other objects (o and
+      o.special).</p>
+
+      <p><dfn>update if</dfn>: source file or any of the required properties
+      are modified, or if any dependencies are updated.</p>
+    </dd>
+
+    <dt>fortran -&gt; name</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: source file as an include file.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.install">install</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.include">include</a> and include/name</p>
+
+      <p><dfn>dependencies</dfn>: include and o (object). A source's f.module
+      dependency on a module called <samp>xyz</samp> is turned into an include
+      dependency on the <samp>xyz.mod</samp>.</p>
+
+      <p><dfn>update if</dfn>: source file is modified.</p>
+
+      <p><dfn>pass on</dfn>: include, o and o.special.</p>
+    </dd>
+
+    <dt>fortran (with a valid Fortran program unit) -&gt; unit.o</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: object file. The file is named by concatenating
+      the lower case characters of the name of the first program unit in the
+      source file and the first value of the <code><a href=
+      "annex_cfg.html#make.build.prop.file-ext.o">file-ext.o</a></code>
+      property.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.compile">compile</a> (fc).</p>
+
+      <p><dfn>category and destination</dfn>: <a href="#build.category.o">o</a>
+      and o/unit.o</p>
+
+      <p><dfn>properties</dfn>: <a href=
+      "annex_cfg.html#make.build.prop.fc">fc</a>, <a href=
+      "annex_cfg.html#make.build.prop.fc.flags">fc.flags</a>, <a href=
+      "annex_cfg.html#make.build.prop.fc.defs">fc.defs</a>, <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-compile">fc.flag-compile</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-define">fc.flag-define</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-include">fc.flag-include</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.include-paths">fc.include-paths</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-module">fc.flag-module</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-omp">fc.flag-omp</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-output">fc.flag-output</a></p>
+
+      <p><dfn>dependencies</dfn>: include and o (object). A source's f.module
+      dependency on a module called <samp>xyz</samp> is turned into an include
+      dependency on the <samp>xyz.mod</samp>.</p>
+
+      <p><dfn>update if</dfn>: source file or any of the required properties
+      are modified, or if any include dependencies are updated.</p>
+
+      <p><dfn>pass on</dfn>: o and o.special.</p>
+
+      <p><dfn>remark</dfn>: trigger <samp>unit.mod</samp> targets if source
+      file contains a Fortran module.</p>
+    </dd>
+
+    <dt>fortran (with function or subroutine) -&gt; name.interface</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Fortran interface file. The file is named by
+      concatenating the base name of the source file with the file extension
+      replaced by the <code><a href=
+      "annex_cfg.html#make.build.prop.file-ext.f90-interface">file-ext.f90-interface</a></code>
+      property.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.ext-iface">ext-iface</a></p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.include">include</a> and include/name.interface</p>
+
+      <p><dfn>dependencies</dfn>: unit.o</p>
+
+      <p><dfn>update if</dfn>: source file or unit.o is modified.</p>
+
+      <p><dfn>pass on</dfn>: include, o and o.special.</p>
+    </dd>
+
+    <dt>fortran (each module in source) -&gt; unit.mod</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: Fortran module definition file. The file is
+      named by concatenating the lower case characters of the name of the
+      module and the first value of the <code><a href=
+      "annex_cfg.html#make.build.prop.file-ext.f90-mod">file-ext.f90-mod</a></code>
+      property.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.compile-plus">compile+</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.include">include</a> and include/unit.mod</p>
+
+      <p><dfn>dependencies</dfn>: unit.o</p>
+
+      <p><dfn>update if</dfn>: source file or unit.o is modified.</p>
+
+      <p><dfn>pass on</dfn>: o.</p>
+    </dd>
+
+    <dt>fortran (with program) -&gt; name.exe</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: binary executable. The object file is named
+      after the base name of the original source file, with the file extension
+      replaced by the first value of the <code><a href=
+      "annex_cfg.html#make.build.prop.file-ext.bin">file-ext.bin</a></code>
+      property.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.link">link</a> (fc).</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.bin">bin</a> and bin/name.exe</p>
+
+      <p><dfn>properties</dfn>:
+      <a href="annex_cfg.html#make.build.prop.ar">ar</a>,
+      <a href="annex_cfg.html#make.build.prop.ar.flags">ar.flags</a>,
+      <a href="annex_cfg.html#make.build.prop.fc">fc</a>,
+      <a href="annex_cfg.html#make.build.prop.fc.flags-ld">fc.flags-ld</a>,
+      <a href="annex_cfg.html#make.build.prop.fc.flag-lib">fc.flag-lib</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-lib-path">fc.flag-lib-path</a>,
+      <a href="annex_cfg.html#make.build.prop.fc.libs">fc.libs</a>,
+      <a href="annex_cfg.html#make.build.prop.fc.lib-paths">fc.lib-paths</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-omp">fc.flag-omp</a>,
+      <a href=
+      "annex_cfg.html#make.build.prop.fc.flag-output">fc.flag-output</a></p>
+
+      <p><dfn>dependencies</dfn>: unit.o and other objects (o and
+      o.special).</p>
+
+      <p><dfn>update if</dfn>: source file or any of the required properties
+      are modified, or if any dependencies are updated.</p>
+    </dd>
+
+    <dt>h -&gt; name</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: a header (include) file.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.install">install</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.include">include</a> and include/name</p>
+
+      <p><dfn>dependencies</dfn>: include and o (object).</p>
+
+      <p><dfn>update if</dfn>: source file is modified.</p>
+
+      <p><dfn>pass on</dfn>: include, o and o.special.</p>
+    </dd>
+
+    <dt>script -&gt; name</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: an executable script.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.install">install</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.bin">bin</a> and bin/name</p>
+
+      <p><dfn>dependencies</dfn>: bin (executable).</p>
+
+      <p><dfn>update if</dfn>: source file is modified.</p>
+
+      <p><dfn>pass on</dfn>: bin.</p>
+    </dd>
+
+    <dt>data -&gt; name-space</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: a data file.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.install">install</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.etc">etc</a> and etc/name-space</p>
+
+      <p><dfn>update if</dfn>: source file is modified.</p>
+    </dd>
+  </dl>
+
+  <p>Here is an explanation of what each build system <dfn>task</dfn> does:</p>
+
+  <dl>
+    <dt id="build.task.archive">archive</dt>
+
+    <dd>Creates an object archive by invoking an archiver command. (See
+    <a href="#build.target-ns">Build Targets from Name-space</a>.)</dd>
+
+    <dt id="build.task.compile">compile</dt>
+
+    <dd>Creates an object file by invoking the C/C++/Fortran compiler on the
+    source file.</dd>
+
+    <dt id="build.task.compile-plus">compile+</dt>
+
+    <dd>Copies the Fortran module definition file created by a compile task to
+    the include sub-directory.</dd>
+
+    <dt id="build.task.ext-iface">ext-iface</dt>
+
+    <dd>Extracts the calling interfaces of all functions and subroutines in a
+    Fortran source file (free format only) and writes the results in an
+    interface block that can be included by other Fortran source files with an
+    <samp>INCLUDE 'name.interface'</samp> statement. In an incremental build,
+    if you have modified a Fortran source file, its interface file will only be
+    re-generated if the content of the interface has changed. This can make
+    incremental build very efficient, as non-interface changes in a function or
+    subroutine will only trigger a re-link of the executable.</dd>
+
+    <dt id="build.task.install">install</dt>
+
+    <dd>Copies the source file to the destination.</dd>
+
+    <dt id="build.task.link">link</dt>
+
+    <dd>Creates an executable by invoking the archiver to load all required
+    objects into an archive, and then the C/C++/Fortran compiler on the object
+    file previously compiled using a source file containing a main program, with
+    the temporary archive.</dd>
+  </dl>
+
+  <h3 id="build.target-prop">Build Targets and Properties</h3>
+
+  <p>If you need to specify a property for a specific target, you can either use
+  their source file namespace or the target key. E.g. If the
+  <samp>sausage.o</samp> target is generated from the source file in the
+  <samp>src/food/sausage.f90</samp> namespace, you can specify its Fortran
+  compiler flags <code><a href=
+  "annex_cfg.html#make.build.prop.fc.flags">build.prop{fc.flags}</a></code> by
+  doing either:</p>
+
+  <pre>
+build.prop{fc.flags}[sausage.o] = -O4
+# would be the same as:
+build.prop{fc.flags}[src/food/sausage.f90] = -O4
+</pre>
+
+  <p>This works with most property modifiers, even for dependency related
+  modifiers such as <code>no-dep.o</code>. E.g.:</p>
+
+  <pre>
+build.prop{no-dep.include}[sausage.o] = enum.f90
+build.prop{include-paths}[sausage.o] = /path/to/additives
+</pre>
+  
+  <p>However, the following will not work:</p>
+
+  <pre>
+build.prop{no-dep.f.module}[sausage.o] = pork
+</pre>
+
+  <p>This is because an object file target is never dependent on a Fortran
+  module by its name. The following will work, however:</p>
+
+  <pre>
+build.prop{no-dep.include}[sausage.o] = pork.mod
+build.prop{no-dep.o}[sausage.mod] = pork.o
+# would be the same as:
+build.prop{no-dep.f.module}[src/food/sausage.f90] = pork
+</pre>
+
+  <h3 id="build.target-source-fortran">Build Targets from Source Files: Fortran
+  Specifics</h3>
+
+  <p>To ensure that a Fortran application is built automatically, its source
+  code should be designed with the following considerations:</p>
+
+  <p><dfn>The name of each compilable program unit should be unique</dfn> in
+  the source tree, bearing in mind that Fortran is NOT case sensitive.</p>
+
+  <p><dfn>Always supply an interface for functions and subroutines</dfn>,
+  i.e.:</p>
+
+  <ul>
+    <li>Place functions and subroutines in a module, and give them the
+    <code>PUBLIC</code> attribute. Import them with the <code>USE
+    &lt;module&gt;</code> statement. We recommend adding the <code>ONLY</code>
+    clause in a <code>USE &lt;module&gt;</code> when importing symbols from a
+    module. This makes it easier to locate the source of each symbol, and avoids
+    unintentional access to other <code>PUBLIC</code> symbols within the
+    <code>MODULE</code>. If you are importing from an intrinsic module, you
+    should add the <code>INTRINSIC</code> clause to the <code>USE
+    &lt;module&gt;</code> statement to tell the build system not to look for the
+    module from your source tree.</li>
+
+    <li>Place functions and subroutines in the <code>CONTAINS</code> section of
+    a standalone program unit. There are two advantages for this approach.
+    Firstly, the sub-programs will get an automatic interface when the container
+    program unit is compiled. Secondly, it should be easier for the compiler to
+    provide optimisation when the sub-programs are internal to the caller. The
+    disadvantage of this approach is that the sub-programs are local to the
+    caller, and so they cannot be called by other program units. Therefore, this
+    approach is only suitable for small sub-programs local to a particular
+    program unit.</li>
+
+    <li>Use the build system's automatic interface file feature. See below.</li>
+  </ul>
+
+  <p>For each free format Fortran source file, e.g. <samp>name.f90</samp>, with
+  1 or more top level function and/or subroutine, the system creates a target
+  with the <a href="#build.task.ext-iface">ext-iface</a> task in the
+  <a href="#build.category.include">include</a> category, e.g.
+  <samp>name.interface</samp>, to extract the calling interfaces of all
+  functions and subroutines into an interface block. Another Fortran source
+  file, e.g. <samp>caller.f90</samp> that relies on the functions and/or
+  subroutines in <samp>name.f90</samp> can have an <samp>INCLUDE
+  'name.interface'</samp> statement in its specification section, which serves
+  2 purposes:</p>
+
+  <ul>
+    <li>It allows <samp>caller.f90</samp> to call the functions and/or
+    subroutines in <samp>name.f90</samp> with explicit interfaces.</li>
+
+    <li>It introduces an <a href=
+    "annex_cfg.html#make.build.prop.dep.include">include</a> dependency for
+    <samp>caller.o</samp> on <samp>name.interface</samp>.</li>
+  </ul>
+
+  <p>In an incremental build, if you modify <samp>name.f90</samp>, the system
+  will only regenerate <samp>name.interface</samp> if only the calling
+  interfaces of the functions and/or subroutines in <samp>name.f90</samp> have
+  changed. Consequently, non-interface changes in <samp>name.f90</samp> will
+  not trigger the re-compile of <samp>caller.o</samp>, but will only trigger a
+  re-link of the executable.</p>
+
+  <h3 id="build.target-selection">Build Targets Selection and Rename</h3>
+
+  <p>You need to tell the build system what targets to build or it will do
+  nothing. The <code><a href=
+  "annex_cfg.html#make.build.target">build.target</a></code> declaration allows
+  you to select targets according to their categories, source name-spaces,
+  tasks and keys. The logic is demonstrated by the following example:</p>
+  <pre>
+# Select targets matching these keys
+build.target = egg.bin ham.o bacon.sh
+
+# Select all targets doing tasks "install" or "link"
+build.target{task} = install link
+
+# Select targets in name-space "foo" or "bar" doing tasks "link"
+build.target{task}[foo bar] = link
+
+# Select all targets in the "bin" category
+build.target{category} = bin
+
+# Select targets in name-space "foo" in the "etc" category
+build.target{category}[foo] = etc
+</pre>
+
+  <p>There are times when an automatic target name is not what you want. In
+  which case, you can rename a target using the <code><a href=
+  "annex_cfg.html#make.build.target-rename">build.target-rename</a></code>
+  declaration to specify an alternate name. E.g. if the target
+  <samp>bacon.sh</samp> should be called <samp>streaky</samp>, you can do:</p>
+  <pre>
+build.target-rename = bacon.sh:streaky
+</pre>
+
+  <p>In order for a target to build, all its dependencies must be satisfied. If
+  a target has a dependency that is not available in the list of targets, the
+  build will fail. Normally, you can avoid this by using one of the
+  <code>build.prop{no-dep.*}</code> declarations to switch off a non-existent
+  dependency, as described in the <a href="#build.source-analysis">Build Source
+  Analysis</a> section. However, there may be times when this is inefficient or
+  insufficient, in which case you can use the property <code><a href=
+  "annex_cfg.html#make.build.prop.ignore-missing-dep-ns">ignore-missing-dep-ns</a></code>
+  to specify a list of source name-spaces, in which targets can ignore missing
+  dependencies. E.g.:</p>
+  <pre>
+# Allows targets in the "foo" and "bar/baz"
+# name-spaces to ignore missing dependencies.
+build.prop{ignore-missing-dep-ns} = foo bar/baz
+</pre>
+
+  <h3 id="build.target-file-ext">Build Targets File Extensions</h3>
+
+  <p>You can rename the file name extension of the targets using
+  <code>build.prop{file-ext.type}</code> declaration (provided that the file
+  name extension is supported by your compiler, etc). E.g. if you want your
+  binary executables to have <samp>.bin</samp> extension rather than the
+  default <samp>.exe</samp>, you can do:</p>
+  <pre>
+build.prop{file-ext.bin} = .bin
+</pre>
+
+  <p>The following file extensions are currently used by the system:</p>
+
+  <dl>
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.a">a</a> (object
+    archive)</dt>
+
+    <dd>.a</dd>
+
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.bin">bin</a> (binary
+    executable)</dt>
+
+    <dd>.exe</dd>
+
+    <dt><a href=
+    "annex_cfg.html#make.build.prop.file-ext.f90-interface">f90-interface</a>
+    (Fortran free format interface file)</dt>
+
+    <dd>.interface</dd>
+
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.f90-mod">f90-mod</a>
+    (Fortran compiler module definition file)</dt>
+
+    <dd>.mod</dd>
+
+    <dt><a href="annex_cfg.html#make.build.prop.file-ext.o">o</a> (object
+    file)</dt>
+
+    <dd>.o</dd>
+  </dl>
+
+  <h3 id="build.target-ns">Build Targets from Name-space</h3>
+
+  <p>Apart from source file targets, the build system also generates targets
+  for each (directory-level) name-space. One target is for creating an object
+  archive to contain all object files in the name-space. The other target is a
+  convenient shorthand to allow all data files in the name-space to be
+  installed. The following is the full description:</p>
+
+  <dl>
+    <dt>name-space &gt; name-space/libo.a</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: object archive.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.archive">archive</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.lib">lib</a> and lib/name-space/libo.a</p>
+
+      <p><dfn>dependencies</dfn>: all o (object) targets in the name-space.</p>
+
+      <p><dfn>properties</dfn>: <a href=
+      "annex_cfg.html#make.build.prop.ar">ar</a>, <a href=
+      "annex_cfg.html#make.build.prop.ar.flags">ar.flags</a></p>
+
+      <p><dfn>update if</dfn>: any dependencies or properties are modified.</p>
+    </dd>
+
+    <dt>name-space &gt; name-space/.etc</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: dummy file.</p>
+
+      <p><dfn>task</dfn>: <a href="#build.task.install">install</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#build.category.etc">etc</a> and etc/name-space/.etc</p>
+
+      <p><dfn>dependencies</dfn>: all data files in the name-space.</p>
+
+      <p><dfn>update if</dfn>: any dependencies are modified.</p>
+    </dd>
+  </dl>
+
+  <h3 id="build.target-update">Build Targets Update in Incremental Mode</h3>
+
+  <p>In incremental mode, a target is only updated if it is marked out of date.
+  A target is considered out of date if:</p>
+
+  <ul>
+    <li>the source file's checksum is changed.</li>
+
+    <li>a required property is modified.</li>
+
+    <li>a dependency is marked as modified, and the dependency is a type that
+    the target cannot pass on. E.g. If <samp>object_1.o</samp> depends on
+    <samp>object_2.o</samp>, and <samp>object_2.o</samp> is marked as modified,
+    the system does not need to re-compile <samp>object_1.o</samp> (as long as
+    its source file and properties remain unchanged). However,
+    <samp>object_1.o</samp> will have to pass the information up the dependency
+    tree, so that a target with a <samp><a href=
+    "#build.task.link">link</a></samp> task to build an executable (or an
+    <samp><a href="#build.task.archive">archive</a></samp> task to build a
+    library) will know that it needs to be updated.</li>
+
+    <li>a dependency is passing on a <q>modified</q> status for a dependency
+    type, which cannot be passed on by the target.</li>
+
+    <li>the target does not exist or its checksum is changed.</li>
+  </ul>
+
+  <p>If, after an update, the target's checksum is the same as before, the
+  target will be considered unchanged and up to date. In an incremental build,
+  the use of checksum ensures that any targets manually modified by the user
+  after the previous build is rebuilt accordingly. It also prevents unnecessary
+  updates of targets in incremental and inherited builds.</p>
+
+  <p>E.g. Consider an incremental build where the only change is the content of
+  a Fortran module <samp>my_mod.f90</samp>. The content change should trigger
+  an update of the <samp>my_mod.o</samp> and <samp>my_mod.mod</samp> targets,
+  and everything depending on them. However, if the source content is modified
+  in such a way that it does not affect the module's public interface, most
+  compilers will generate an identical <samp>my_mod.mod</samp>. The system can
+  detect this by comparing the checksums. If <samp>my_mod.mod</samp> is
+  unchanged, the build system will not need to trigger the re-compile of all
+  targets depending on <samp>my_mod.mod</samp>, and it will only need to
+  re-link the executable. This allows incremental builds to be more
+  efficient.</p>
+
+  <div class="well">
+    <p><strong><span class="glyphicon glyphicon-pencil"
+    aria-hidden="true"></span> Note - checksum algorithm</strong></p>
+
+    <p>By default, the MD5 algorithm is used to calculate the checksum. This is
+    normally good enough to detect whether a file is modified or not. If this is
+    insufficient for whatever reasons, you can tell the build system to use one
+    of the SHA algorithms supported by the Perl module <a href=
+    "http://perldoc.perl.org/Digest/SHA.html">Digest::SHA</a>, by setting the
+    value of <a href=
+    "annex_cfg.html#make.build.prop.checksum-method">build.prop{checksum-method}</a>.</p>
+  </div>
+
+  <h3 id="build.inherit">Build Inheritance</h3>
+
+  <p>If a previous build with a similar configuration exists in another
+  location, it can be more efficient to inherit from this previous build in
+  your current build. This works like a normal incremental build, except that
+  your build will only contain the changes you have specified (compared with
+  the inherited build) instead of the full set of targets.</p>
+
+  <p>The current build inherits all properties and target settings, as well as
+  sources and targets from the inherited build. While properties and target
+  settings can be overridden with a corresponding declaration, source
+  inheritance can only be prevented by using a <code><a href=
+  "annex_cfg.html#make.build.prop.no-inherit-source">build.prop{no-inherit-source}</a></code>
+  declaration. E.g.:</p>
+  <pre>
+# Prevents inheritance from some name-spaces:
+build.prop{no-inherit-source} = food/mint drink/soft/cola.c
+</pre>
+
+  <p>For multiple inheritance, the last one takes precedence, and any search
+  for source files or targets are recursive and depth first. For instance, if
+  we have the following declarations in the current FCM make configuration:</p>
+  <pre>
+use = /path/to/a /path/to/b /path/to/c
+</pre>
+
+  <p>and the following in the FCM make configuration of
+  <samp>/path/to/b</samp>:</p>
+  <pre>
+use /path/to/d
+</pre>
+
+  <p>The relationship looks like:</p>
+  <pre>
+/path/to/current
+    /path/to/c
+    /path/to/b
+        /path/to/d
+    /path/to/a
+</pre>
+
+  <p>Therefore, we would expect the search path to follow the order:</p>
+  <pre>
+/path/to/current
+/path/to/c
+/path/to/b
+/path/to/d
+/path/to/a
+</pre>
+
+  <p>In its normal setting, the system does not inherit targets in the
+  <samp>bin</samp>, <samp>etc</samp> and <samp>lib</samp> categories. A target
+  in one of these categories is rebuilt in the current destination, whether the
+  inherited target is up to date or not. This allows someone to use the
+  executables of the build by setting the <var>PATH</var> environment variable
+  to point only to <samp>$DEST/build/bin/</samp> (where <var>$DEST</var> is the
+  destination of the current make). If this behaviour is undesirable for
+  whatever reason, it can be altered using the <code><a href=
+  "annex_cfg.html#make.build.prop.no-inherit-target-category">build.prop{no-inherit-target-category}</a></code>
+  declaration.</p>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>
+      <p>The build system uses the compiler's <code>-I</code> option to specify
+      the search path for include files. E.g. it uses this option to specify
+      the <samp>inc/</samp> sub-directories of the current build and its
+      inherited build.</p>
+
+      <p>However, some compilers (e.g. <code>cpp</code>) search for include
+      files from the container directory of the source file before searching
+      for the paths specified by the <code>-I</code> options. This behaviour
+      may cause the build to behave incorrectly.</p>
+
+      <p>Consider a source file <samp>egg/hen.c</samp> that includes
+      <samp>fried.h</samp>. If the directory structure looks like:</p>
+      <pre>
+# Sources in inherited build:
+egg/hen.c
+egg/fried.h
+
+# Sources in current build:
+egg/fried.h
+</pre>
+
+      <p>The system will correctly identify that <samp>fried.h</samp> is out of
+      date, and trigger a re-compilation of <samp>egg/hen.c</samp>. However, if
+      the compiler searches for the include files from the container directory
+      of the source file first, it will wrongly use the include file in the
+      inherited build instead of the current one.</p>
+
+      <p>If your directory structure does not have any include files in the same
+      directory as the source files that include them then you do not need to
+      worry. If it does then you need to check whether you are affected by this
+      problem before using an inherited build. The situation will vary
+      according to whether the affected code uses Fortran or preprocessor
+      include statements and also whether you are using the
+      <a href="#preprocess">preprocess system</a>. Some compilers (e.g.
+      <code>gfortran</code>) work fine for Fortran includes but not preprocessor
+      includes. Others (e.g. <code>ifort</code>) have options which can be used
+      (e.g. <code>-assume nosource_include</code>) to get the desired behaviour.
+      The FCM distribution includes some simple test code to help you test how
+      your chosen compilers behave. If you cannot ensure the correct behaviour
+      then it is safer not to use inherited builds.</p>
+    </dd>
+  </dl>
+
+  <h3 id="build.diagnostic">Build Diagnostic</h3>
+
+  <p>The amount of diagnostic messages generated by the build system is
+  dependent on the diagnostic verbosity level that can be modified by the
+  <code>-v</code> and <code>-q</code> options to the <code>fcm make</code>
+  command.</p>
+
+  <p>The following is a list of diagnostic output at each verbosity level:</p>
+
+  <dl>
+    <dt>-q</dt>
+
+    <dd>
+      <ul>
+        <li>Exceptions.</li>
+      </ul>
+    </dd>
+
+    <dt>default</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the -q level.</li>
+
+        <li>Start time of the build.</li>
+
+        <li>The summary of source analysis.</li>
+
+        <li>The summary of targets. Each row except the last reports the number
+        of modified and unchanged targets with a given type of task, and the
+        total time spent to perform the tasks. The last row reports the total
+        number of modified and unchanged targets, and the actual elapsed time.
+        It is worth noting that the elapsed time in a multi-process build
+        should be significant shorter than the sum of the total time for each
+        type of task. E.g.:
+          <pre>
+[info] compile   targets: modified=5, unchanged=0, total-time=0.5s
+[info] compile+  targets: modified=1, unchanged=0, total-time=0.0s
+[info] ext-iface targets: modified=2, unchanged=0, total-time=0.0s
+[info] install   targets: modified=1, unchanged=0, total-time=0.0s
+[info] link      targets: modified=1, unchanged=0, total-time=0.1s
+[info] TOTAL     targets: modified=10, unchanged=0, elapsed-time=0.7s
+</pre>
+        </li>
+
+        <li>Total time.</li>
+      </ul>
+    </dd>
+
+    <dt>-v</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the default level.</li>
+
+        <li>Elapsed time and name-space for each analysed source.</li>
+
+        <li>Task name, elapsed time, target status (<samp>M</samp> for modified
+        or <samp>U</samp> for unchanged), target key, and source name-space for
+        each modified target. E.g.:
+          <pre>
+[info] compile    0.0 M hello_func.o         &lt;- lib/function/hello_func.f90
+[info] ext-iface  0.0 M hello_func.interface &lt;- lib/function/hello_func.f90
+[info] install    0.0 M hello_inc.f90        &lt;- include/hello_inc.f90
+[info] compile    0.0 M hello_1.o            &lt;- bin/hello_1.f90
+[info] link       0.1 M hello_1.exe          &lt;- bin/hello_1.f90
+</pre>
+        </li>
+      </ul>
+    </dd>
+
+    <dt>-vv</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at the -v level.</li>
+
+        <li>A list of dependencies (type and name) of each analysed source.
+        E.g.
+          <pre>
+[info] analyse  0.0 bin/hello_1.f90
+[info]              -&gt; (  include) hello_inc.f90
+[info]              -&gt; (        o) hello_void.o
+[info]              -&gt; ( f.module) hello_mod
+</pre>
+        </li>
+
+        <li>A list of the available targets from the sources. Each row contains
+        the source namespace, the target task, the target category and the key
+        of the target. E.g.:
+          <pre>
+[info] source-&gt;target / -&gt; (archive) lib/ libo.a
+[info] source-&gt;target hello.f90 -&gt; (link) bin/ hello.exe
+[info] source-&gt;target hello.f90 -&gt; (install) include/ hello.f90
+[info] source-&gt;target hello.f90 -&gt; (compile) o/ hello.o
+[info] source-&gt;target world.f90 -&gt; (install) include/ world.f90
+[info] source-&gt;target world.f90 -&gt; (compile+) include/ world.mod
+[info] source-&gt;target world.f90 -&gt; (compile) o/ world.o
+</pre>
+        </li>
+
+        <li>A list of the required targets for this build. Each row contains
+        the task, the category and the key of the target. E.g.:
+          <pre>
+[info] required-target: link      bin     hello_1.exe
+</pre>
+        </li>
+
+        <li>The dependency tree of all the required targets. (N.B. A
+        <samp>(n-deps=N)</samp> at the end of a line means that the target has
+        already appeared earlier and that it has <samp>N</samp> direct
+        dependencies, which will not be reported again.) E.g.:
+          <pre>
+[info] target hello_1.exe
+[info] target  - hello_void.o
+[info] target  - hello_1.o
+[info] target  -  - hello_mod.mod
+[info] target  -  -  - hello_mod.o
+[info] target  -  - hello_void.o
+[info] target  -  - hello_inc.f90
+[info] target  -  -  - hello_sub.interface
+[info] target  -  -  -  - hello_sub.o
+[info] target  -  -  -  -  - hello_func.interface
+[info] target  -  -  -  -  -  - hello_func.o
+[info] target  - hello_2.o
+[info] target  -  - hello_mod.mod (n-deps=1)
+</pre>
+        </li>
+
+        <li>Each shell command invoked with elapsed time and return code.</li>
+
+        <li>STDOUT and STDERR from shell commands invoked by the build
+        tasks, e.g. diagnostic output from compilers and linkers.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h2 id="preprocess">Preprocess</h2>
+
+  <p>As most modern compilers can handle preprocessing, you should normally
+  leave preprocessing to the compiler. However, it is recognised that some code
+  is written with preprocessor directives that can alter the calling interface
+  of the procedure and/or their dependencies. If a source file requires
+  preprocessing in such a way, we have to preprocess it before feeding it to
+  the build system. The preprocess system can be used to do this. It is
+  typically run as a step before build.</p>
+
+  <p>However, using a separate preprocess step is not the best way of working,
+  as it adds an overhead to the build process. If your code requires
+  preprocessing, you should try to design it to avoid changes in the above.</p>
+
+  <p>In practice, the only reasonable use of a preprocessor with Fortran is for
+  code selection. For example, preprocessing is useful for isolating machine
+  specific libraries or instructions, where it may be appropriate to use inline
+  alternatives for small sections of code. Another example is when multiple
+  versions of the same procedure exist in the source tree and you need to use
+  the preprocessor to select the correct version for your build.</p>
+
+  <p>Avoid using the a preprocessor for code inclusion, as you should be able
+  to do the same via the Fortran <code>INCLUDE</code> statement. You should
+  also avoid embedding preprocessor macros within the continuations of a
+  Fortran statement, as it can make your code very confusing.</p>
+
+  <p>The preprocess system works using the same logic as the build system, but
+  is configured primarily to preprocess C/C++ and Fortran source files. We
+  shall document only the main differences to the build system in the remainder
+  of this section.</p>
+
+  <h3 id="preprocess.basic">Preprocess: Basic</h3>
+
+  <p>A typical usage of the preprocess system may look like:</p>
+  <pre>
+steps = extract preprocess build
+
+# ... some extract configuration
+
+# Switch off preprocessing for all
+preprocess.target{task} =
+# Only preprocess source files in these name-spaces
+preprocess.target{task}[foo/bar egg/fried.F90] = process
+# Specifies the macro definitions for the Fortran preprocessor
+preprocess.prop{fpp.defs} = THING=stuff HIGH=tall
+# Specifies the macro definitions for the C preprocessor
+preprocess.prop{cpp.defs} = LOWER=lower UNDER LINUX
+
+# ... some build configuration
+</pre>
+
+  <p>The result of the preprocess can be found in the sub-directories of the
+  <samp>preprocess/</samp> sub-directory. There are only 2 target
+  categories:</p>
+
+  <dl>
+    <dt id="preprocess.category.include"><samp>include</samp></dt>
+
+    <dd>e.g. include files.</dd>
+
+    <dt id="preprocess.category.src"><samp>src</samp></dt>
+
+    <dd>e.g. preprocessed source files</dd>
+  </dl>
+
+  <h3 id="preprocess.source-type">Preprocess Source Types</h3>
+
+  <p>Only files in the following types (with the given file extensions) are
+  recognised by the preprocess system:</p>
+
+  <dl>
+    <dt><a href="annex_cfg.html#make.preprocess.prop.file-ext.cpp">cpp</a>
+    (C/C++ source file)</dt>
+
+    <dd>.c .m .cc .cp .cxx .cpp .CPP .c++ .C .mm .M</dd>
+
+    <dt><a href="annex_cfg.html#make.preprocess.prop.file-ext.fpp">fpp</a>
+    (Fortran source file requiring preprocessing)</dt>
+
+    <dd>.F .FOR .FTN .F90 .F95</dd>
+
+    <dt><a href="annex_cfg.html#make.preprocess.prop.file-ext.h">h</a>
+    (Preprocessor header file)</dt>
+
+    <dd>.h</dd>
+  </dl>
+
+  <h3 id="preprocess.source-analysis">Preprocess Source Analysis</h3>
+
+  <p>The preprocess system only looks for include dependencies using the
+  pattern <samp>#include "name.h"</samp>. Macros using the angle brackets
+  syntax (e.g. <samp>#include &lt;name.h&gt;</samp>) are ignored.</p>
+
+  <h3 id="preprocess.target-source">Preprocess Targets from Source Files</h3>
+
+  <p>The preprocess system only generates targets from source files, (i.e.
+  targets are not generated by name-spaces). Targets of the preprocess system
+  perform one of the following <dfn>tasks</dfn>:</p>
+
+  <dl>
+    <dt id="preprocess.task.install">install</dt>
+
+    <dd>Copies the source file to the destination.</dd>
+
+    <dt id="preprocess.task.process">process</dt>
+
+    <dd>Creates a new source file by invoking the C/Fortran preprocessor on the
+    original source file.</dd>
+  </dl>
+
+  <p>By default, it attempts to build all targets with a <samp><a href=
+  "#preprocess.task.process">process</a></samp> task, i.e.:</p>
+  <pre>
+preprocess.target =
+preprocess.target{task} = process
+preprocess.target{category} =
+</pre>
+
+  <p>Here is a list of what targets are available for each type of file:</p>
+
+  <dl>
+    <dt>cpp -&gt; name-space</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: the preprocessed version of the original
+      file.</p>
+
+      <p><dfn>task</dfn>: <a href="#preprocess.task.process">process</a>
+      (cpp).</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#preprocess.category.src">src</a> and src/name-space</p>
+
+      <p><dfn>properties</dfn>: <a href=
+      "annex_cfg.html#make.preprocess.prop.cpp">cpp</a>, <a href=
+      "annex_cfg.html#make.preprocess.prop.cpp.flags">cpp.flags</a>, <a href=
+      "annex_cfg.html#make.preprocess.prop.cpp.defs">cpp.defs</a>, <a href=
+      "annex_cfg.html#make.preprocess.prop.cpp.flag-define">cpp.flag-define</a>,
+      <a href=
+      "annex_cfg.html#make.preprocess.prop.cpp.flag-include">cpp.flag-include</a>,
+      <a href=
+      "annex_cfg.html#make.preprocess.prop.cpp.include-paths">cpp.include-paths</a></p>
+      <p><dfn>dependencies</dfn>: include.</p>
+
+      <p><dfn>update if</dfn>: source file or any of the required properties
+      are modified, or if any include dependencies are updated.</p>
+    </dd>
+
+    <dt>fpp -&gt; name-space</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: the preprocessed version of the original
+      file.</p>
+
+      <p><dfn>task</dfn>: <a href="#preprocess.task.process">process</a>
+      (fpp).</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#preprocess.category.src">src</a> and src/name-space</p>
+
+      <p><dfn>properties</dfn>: <a href=
+      "annex_cfg.html#make.preprocess.prop.fpp">fpp</a>, <a href=
+      "annex_cfg.html#make.preprocess.prop.fpp.flags">fpp.flags</a>, <a href=
+      "annex_cfg.html#make.preprocess.prop.fpp.defs">fpp.defs</a>, <a href=
+      "annex_cfg.html#make.preprocess.prop.fpp.flag-define">fpp.flag-define</a>,
+      <a href=
+      "annex_cfg.html#make.preprocess.prop.fpp.flag-include">fpp.flag-include</a>,
+      <a href=
+      "annex_cfg.html#make.preprocess.prop.fpp.include-paths">fpp.include-paths</a></p>
+
+      <p><dfn>dependencies</dfn>: include.</p>
+
+      <p><dfn>update if</dfn>: source file or any of the required properties
+      are modified, or if any include dependencies are updated.</p>
+    </dd>
+
+    <dt>h -&gt; name</dt>
+
+    <dd>
+      <p><dfn>description</dfn>: a header (include) file.</p>
+
+      <p><dfn>task</dfn>: <a href="#preprocess.task.install">install</a>.</p>
+
+      <p><dfn>category and destination</dfn>: <a href=
+      "#preprocess.category.include">include</a> and include/name</p>
+
+      <p><dfn>dependencies</dfn>: include.</p>
+
+      <p><dfn>update if</dfn>: source file is modified.</p>
+    </dd>
+  </dl>
+
+  </div>
+  </div>
+  </div>
+
+  <hr/>
+  <div class="container-fluid text-center">
+    <div class="row"><div class="col-md-12">
+    <address><small>
+      Copyright &copy; 2006-2021 British Crown (Met Office) &amp; Contributors.
+      <a href="http://www.metoffice.gov.uk">Met Office</a>.
+      See <a href="../etc/fcm-terms-of-use.html">Terms of Use</a>.<br />
+      This document is released under the British <a href=
+      "http://www.nationalarchives.gov.uk/doc/open-government-licence/" rel=
+      "license">Open Government Licence</a>.<br />
+    </small></address>
+    </div></div>
+  </div>
+
+  <script type="text/javascript" src="../etc/jquery.min.js"></script>
+  <script type="text/javascript" src="../etc/bootstrap/js/bootstrap.min.js"></script>
+  <script type="text/javascript" src="../etc/fcm.js"></script>
+  <script type="text/javascript" src="../etc/fcm-version.js"></script>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/admin.cfg.example
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/admin.cfg.example	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/admin.cfg.example	(revision 5095)
@@ -0,0 +1,86 @@
+#-------------------------------------------------------------------------------
+# FCM Admin Configuration Example
+# See also the Perl module FCM::Admin::Config
+#-------------------------------------------------------------------------------
+# To use, copy this file to "admin.cfg".
+# Uncomment a line to activate a setting.
+# Default values are given below.
+#-------------------------------------------------------------------------------
+## Email address of FCM system admin
+#  admin_email = $USER
+## Notification email address (for the "From:" field in notification emails).
+#  notification_from =
+
+## Location for log files
+#  log_dir = /var/log/fcm
+
+## Location where FCM is installed
+#  fcm_home = $FCM_HOME
+## Location where FCM site specific items are installed
+#  fcm_site_home =
+
+## Locations (space delimited) to mirror items in "mirror_keys"
+#  mirror_dests =
+## Items (space delimited) to mirror to "mirror_dests", e.g. "fcm_site_home"
+#  mirror_keys =
+
+## Location to backup Subversion repositories
+#  svn_backup_dir = /var/svn/backups
+## Location to create dumps for commits to Subversion repositories
+#  svn_dump_dir = /var/svn/dumps
+### Name of group where Subversion repositories should be created in
+#  svn_group =
+## PATH environment variable for Subversion hooks
+#  svn_hook_path_env =
+## Location to serve Subversion repositories
+#  svn_live_dir = /srv/svn
+## Name of svnserve password file
+#  svn_passwd_file =
+## File name suffix that may be added to each Subversion repository
+#  svn_project_suffix =
+
+## List of admin users for all Trac environments
+#  trac_admin_users =
+## Location to backup Trac environments
+#  trac_backup_dir = /var/trac/backups
+### Name of group where Trac files should be created in
+#  trac_group =
+## Host name (from a user's perspective) where Trac environments are served
+#  trac_host_name = localhost
+## Name of "trac.ini" in each Trac environment
+#  trac_ini_file = trac.ini
+## Location to serve Trac environments
+#  trac_live_dir = /srv/trac
+## Template to create a Trac environment URL (from a user's perspective)
+#  trac_live_url_tmpl = https://{host}/trac/{project}
+## Name of Trac password file (under Apache)
+#  trac_passwd_file =
+
+## Specify the name of the tool for obtaining user info (ldap or passwd)
+#  user_info_tool = passwd
+
+## LDAP settings, only relevant if user_info_tool = ldap
+## File containing the password to the LDAP server, if required
+# ldappw = ~/.ldappw
+## The URI of the LDAP server
+# ldap_uri =
+## The DN in the LDAP server to bind with to search the directory
+# ldap_binddn =
+## The DN in the LDAP server that is the base for a search
+# ldap_basedn =
+## The attributes for UID, common name and email in the LDAP directory
+# ldap_attrs = uid cn mail
+## If specified, use the value as extra (AND) filters to an LDAP search
+# ldap_filter_more =
+
+## PASSWD settings, only relevant if user_info_tool = passwd
+## Domain name to suffix user IDs to create an email address
+#  passwd_email_domain =
+## Maximum GID considered to be a normal user group.
+#  passwd_gid_max =
+## Maximum UID considered to be a normal user.
+#  passwd_uid_max =
+## Minimum GID considered to be a normal user group.
+#  passwd_gid_min = 1000
+## Minimum UID considered to be a normal user.
+#  passwd_uid_min = 1000
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/external.cfg.example
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/external.cfg.example	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/external.cfg.example	(revision 5095)
@@ -0,0 +1,9 @@
+#-------------------------------------------------------------------------------
+# FCM External Configuration Example
+#-------------------------------------------------------------------------------
+# For detail, please refer to:
+# FCM User Guide Annex: FCM Configuration File > FCM External Configuration
+#-------------------------------------------------------------------------------
+
+# E.g. Web browser
+browser = firefox
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/keyword.cfg.example
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/keyword.cfg.example	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/keyword.cfg.example	(revision 5095)
@@ -0,0 +1,9 @@
+#-------------------------------------------------------------------------------
+# FCM Keyword Configuration Example
+#-------------------------------------------------------------------------------
+# For detail, please refer to:
+# FCM User Guide Annex: FCM Configuration File > FCM Keyword Configuration
+#-------------------------------------------------------------------------------
+
+# E.g. Location of FCM
+location{primary}[fcm] = svn://host/fcm
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/make.cfg.example
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/make.cfg.example	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/fcm/make.cfg.example	(revision 5095)
@@ -0,0 +1,9 @@
+#-------------------------------------------------------------------------------
+# FCM Make Configuration Example
+#-------------------------------------------------------------------------------
+# For detail, please refer to:
+# FCM User Guide Annex: FCM Configuration File > FCM Make Configuration
+#-------------------------------------------------------------------------------
+
+# E.g. C compiler for the build system
+build.prop{cc} = cc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/post-commit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/post-commit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/post-commit	(revision 5095)
@@ -0,0 +1,23 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+set -eu
+mkdir -p "$1/log"
+nohup "$FCM_HOME/sbin/post-commit-bg" "$@" \
+    </dev/null >>"$1/log/post-commit.log" 2>&1 &
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/post-revprop-change
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/post-revprop-change	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/post-revprop-change	(revision 5095)
@@ -0,0 +1,23 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+set -eu
+mkdir -p "$1/log"
+nohup "$FCM_HOME/sbin/post-revprop-change-bg" "$@" \
+    <&0 >>"$1/log/post-revprop-change.log" 2>&1 &
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/pre-commit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/pre-commit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/pre-commit	(revision 5095)
@@ -0,0 +1,22 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+set -eu
+mkdir -p "$1/log"
+exec "$FCM_HOME/sbin/pre-commit" "$@" >>"$1/log/pre-commit.log"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/pre-revprop-change
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/pre-revprop-change	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/etc/svn-hooks/pre-revprop-change	(revision 5095)
@@ -0,0 +1,22 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+set -eu
+mkdir -p "$1/log"
+exec "$FCM_HOME/sbin/pre-revprop-change" "$@" >>"$1/log/pre-revprop-change.log"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/index.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/index.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/index.html	(revision 5095)
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>FCM</title>
+  <meta http-equiv="refresh" content="0;url=doc/">
+</head>
+
+<body>
+<p>If not automatically redirected, please click
+<a href="doc/">FCM Documentation</a>.</p>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Config.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Config.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Config.pm	(revision 5095)
@@ -0,0 +1,353 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Config;
+use base qw{FCM::Class::HASH};
+
+use FCM::Context::Locator;
+use FCM::Util;
+use File::Basename qw{dirname};
+use File::Spec::Functions qw{catfile};
+use FindBin;
+
+our $UTIL = FCM::Util->new();
+
+my $TRAC_LIVE_URL_TMPL = 'https://{host}/trac/{project}';
+my $USER_ID = (getpwuid($<))[0];
+
+__PACKAGE__->class({
+    # Emails
+    admin_email         => {isa => '$', default => $USER_ID},
+    notification_from   => {isa => '$'},
+
+    # Location for log files
+    log_dir             => {isa => '$', default => '/var/log/fcm'},
+
+    # FCM installation locations
+    fcm_home            => {isa => '$', default => dirname($FindBin::Bin)},
+    fcm_site_home       => {isa => '$', default => q{}},
+
+    # FCM installation mirror locations
+    mirror_dests        => {isa => '$', default => q{}},
+    mirror_keys         => {isa => '$', default => q{}},
+
+    # Subversion repositories settings
+    svn_backup_dir      => {isa => '$', default => '/var/svn/backups'},
+    svn_dump_dir        => {isa => '$', default => '/var/svn/dumps'},
+    svn_group           => {isa => '$', default => q{}},
+    svn_hook_path_env   => {isa => '$', default => q{}},
+    svn_live_dir        => {isa => '$', default => '/srv/svn'},
+    svn_passwd_file     => {isa => '$', default => q{}},
+    svn_project_suffix  => {isa => '$', default => q{}},
+
+    # Trac environments settings
+    trac_admin_users    => {isa => '$', default => q{}},
+    trac_backup_dir     => {isa => '$', default => '/var/trac/backups'},
+    trac_group          => {isa => '$', default => q{}},
+    trac_host_name      => {isa => '$', default => 'localhost'},
+    trac_ini_file       => {isa => '$', default => 'trac.ini'},
+    trac_live_dir       => {isa => '$', default => '/srv/trac'},
+    trac_live_url_tmpl  => {isa => '$', default => $TRAC_LIVE_URL_TMPL},
+    trac_passwd_file    => {isa => '$', default => q{}},
+
+    # User information tool settings
+    user_info_tool      => {isa => '$', default => 'passwd'},
+
+    # User information tool, LDAP settings
+    ldappw              => {isa => '$', default => '~/.ldappw'},
+    ldap_uri            => {isa => '$', default => q{}},
+    ldap_binddn         => {isa => '$', default => q{}},
+    ldap_basedn         => {isa => '$', default => q{}},
+    ldap_attrs          => {isa => '$', default => q{uid cn mail}},
+    ldap_filter_more    => {isa => '$', default => q{}},
+
+    # User information tool, passwd settings
+    passwd_email_domain => {isa => '$', default => q{}},
+    passwd_gid_max      => {isa => '$'},
+    passwd_uid_max      => {isa => '$'},
+    passwd_gid_min      => {isa => '$', default => 1000},
+    passwd_uid_min      => {isa => '$', default => 1000},
+});
+
+
+# Returns a unique instance of this class.
+my $INSTANCE;
+sub instance {
+    my ($class) = @_;
+    if (!defined($INSTANCE)) {
+        $INSTANCE = $class->new();
+        # Load $FCM_HOME/etc/fcm/admin.cfg and $HOME/.metomi/fcm/admin.cfg
+        $UTIL->cfg_init(
+            'admin.cfg',
+            sub {
+                my $config_reader = shift();
+                while (defined(my $entry = $config_reader->())) {
+                    my $label = $entry->get_label();
+                    if (exists($INSTANCE->{$label})) {
+                        $INSTANCE->{$label} = $entry->get_value();
+                    }
+                }
+            },
+        );
+    }
+    return $INSTANCE;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::Config
+
+=head1 SYNOPSIS
+
+    $config = FCM::Admin::Config->instance();
+    $dir = $config->get_svn_backup_dir();
+    # ...
+
+=head1 DESCRIPTION
+
+This class is used to retrieve/store configurations required by FCM
+admininstration scripts.
+
+It is a sub-class of L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 METHODS
+
+=over 4
+
+=item FCM::Admin::Config->instance()
+
+Returns a unique instance of this class. On first call, creates the instance
+with the configurations set to their default values; and loads from the
+site/user configuration at $FCM_HOME/etc/fcm/admin.cfg and
+$HOME/.metomi/fcm/admin.cfg.
+
+=back
+
+=head1 ATTRIBUTES
+
+Email addresses.
+
+=over 4
+
+=item admin_email
+
+The e-mail address of the FCM administrator.
+
+=item notification_from
+
+Notification email address (for the "From:" field in notification emails).
+
+=back
+
+Location for log files.
+
+=over 4
+
+=item log_dir
+
+The location for log files.
+
+=back
+
+Locations of FCM installation.
+
+=over 4
+
+=item fcm_home
+
+The source path of the default FCM distribution.
+
+=item fcm_site_home
+
+The source path of the default FCM site distribution.
+
+=back
+
+Settings on how to mirror FCM installation.
+
+=over 4
+
+=item mirror_dests
+
+A space-delimited list of destinations to mirror FCM installation.
+
+=item mirror_keys
+
+A string containing a list of source keys. Each source key should point
+to a source location in this $config. The source locations will be distributed
+to the list of destinations in C<mirror_dests>.
+
+=back
+
+Subversion repositories settings.
+
+=over 4
+
+=item svn_backup_dir
+
+The path to a directory containing the backups of SVN repositories.
+
+=item svn_dump_dir
+
+The path to a directory containing the revision dumps of SVN
+repositories.
+
+=item svn_group
+
+The group name in which Subversion repositories should be created in.
+
+=item svn_hook_dir
+
+The path to a directory containing source files of SVN hook scripts.
+
+=item svn_hook_path_env
+
+The value of the PATH environment variable, in which SVN hook scripts
+should run with.
+
+=item svn_live_dir
+
+The path to a directory containing the live SVN repositories.
+
+=item svn_passwd_file
+
+The base name of the SVN password file.
+
+=item svn_project_suffix
+
+The suffix added to the name of each SVN repository.
+
+=back
+
+Trac environment settings.
+
+=over 4
+
+=item trac_admin_users
+
+A space-delimited list of admin users for all Trac environments.
+
+=item trac_backup_dir
+
+The path to a directory containing the backups of Trac environments.
+
+=item trac_group
+
+The group name in which Trac environment files should be created in.
+
+=item trac_host_name
+
+The host name of the Trac server, from the user's perspective.
+
+=item trac_ini_file
+
+The base name of the Trac INI file.
+
+=item trac_live_dir
+
+The path to a directory containing the live Trac environments.
+
+=item trac_live_url_tmpl
+
+The template string for determining the URL of the Trac environment of a
+project.
+
+=item trac_passwd_file
+
+The base name of the Trac password file.
+
+=back
+
+=over 4
+
+User information tool settings.
+
+=item user_info_tool
+
+The name of the tool for obtaining user information.
+
+=back
+
+LDAP settings, only relevant if C<user_info_tool = ldap>
+
+=over 4
+
+=item ldappw
+
+File containing the password to the LDAP server, if required.
+
+=item ldap_uri
+
+The URI of the LDAP server.
+
+=item ldap_binddn
+
+The DN in the LDAP server to bind with to search the directory.
+
+=item ldap_basedn
+
+The DN in the LDAP server that is the base for a search.
+
+=item ldap_attrs
+
+The attributes for UID, common name and email in the LDAP directory.
+
+=item ldap_filter_more
+
+If specified, use the value as extra (AND) filters to an LDAP search.
+
+=back
+
+PASSWD settings, only relevant if user_info_tool = passwd
+
+=over 4
+
+=item passwd_email_domain
+
+Domain name to suffix user IDs to create an email address.
+
+=item passwd_gid_max
+
+Maximum GID considered to be a normal user group.
+
+=item passwd_uid_max
+
+Maximum UID considered to be a normal user.
+
+=item passwd_gid_min
+
+Minimum GID considered to be a normal user group. (default=1000)
+
+=item passwd_uid_min
+
+Minimum UID considered to be a normal user. (default=1000)
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Project.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Project.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Project.pm	(revision 5095)
@@ -0,0 +1,260 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Project;
+
+use overload q{""} => \&get_name;
+use FCM::Admin::Config;
+use File::Spec;
+
+my $ARCHIVE_EXTENSION = q{.tgz};
+
+# ------------------------------------------------------------------------------
+# Creates a new instance of this class.
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+# ------------------------------------------------------------------------------
+# Returns the name of the project.
+sub get_name {
+    my ($self) = @_;
+    return $self->{name};
+}
+
+# ------------------------------------------------------------------------------
+# Returns the base name of the backup archive of the project's SVN repository.
+sub get_svn_archive_base_name {
+    my ($self) = @_;
+    return $self->get_svn_base_name() . $ARCHIVE_EXTENSION;
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path of the backup archive of the project's SVN repository.
+sub get_svn_backup_path {
+    my ($self) = @_;
+    return File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_svn_backup_dir(),
+        $self->get_svn_archive_base_name(),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns the base name of the project's Subversion repository.
+sub get_svn_base_name {
+    my ($self) = @_;
+    return
+        $self->get_name()
+        . FCM::Admin::Config->instance()->get_svn_project_suffix();
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path to the revision dumps of the project's SVN repository.
+sub get_svn_dump_path {
+    my ($self) = @_;
+    return File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_svn_dump_dir(),
+        $self->get_svn_base_name(),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path to the project's SVN live repository's hooks directory.
+sub get_svn_live_hook_path {
+    my ($self) = @_;
+    return File::Spec->catfile($self->get_svn_live_path(), q{hooks});
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path to the project's SVN live repository.
+sub get_svn_live_path {
+    my ($self) = @_;
+    return File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_svn_live_dir(),
+        $self->get_svn_base_name(),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns the file:// URI to the project's SVN live repository.
+sub get_svn_file_uri {
+    my ($self) = @_;
+    return q{file://} . $self->get_svn_live_path();
+    # Note: can use URI::file in theory, but it returns file:/path (instead of
+    #       file:///path) which Subversion does not like.
+}
+
+# ------------------------------------------------------------------------------
+# Returns the base name of the project's Trac environment backup archive.
+sub get_trac_archive_base_name {
+    my ($self) = @_;
+    return $self->get_name() . $ARCHIVE_EXTENSION;
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path to the project's Trac backup archive.
+sub get_trac_backup_path {
+    my ($self) = @_;
+    return File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_trac_backup_dir(),
+        $self->get_trac_archive_base_name(),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path to the project's Trac live environment's database.
+sub get_trac_live_db_path {
+    my ($self) = @_;
+    return File::Spec->catfile($self->get_trac_live_path(), qw{db trac.db});
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path to the project's Trac live environment's INI file.
+sub get_trac_live_ini_path {
+    my ($self) = @_;
+    return File::Spec->catfile($self->get_trac_live_path(), qw{conf trac.ini});
+}
+
+# ------------------------------------------------------------------------------
+# Returns the path to the project's Trac live environment.
+sub get_trac_live_path {
+    my ($self) = @_;
+    return File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_trac_live_dir(),
+        $self->get_name(),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns the URL to the project's Trac live environment.
+sub get_trac_live_url {
+    my ($self) = @_;
+    my $return = FCM::Admin::Config->instance()->get_trac_live_url_tmpl();
+    for (
+        ['{host}', FCM::Admin::Config->instance()->get_trac_host_name()],
+        ['{project}', $self->get_name()],
+    ) {
+        my ($key, $value) = @{$_};
+        my $index = index($return, $key);
+        if ($index > -1) {
+            substr($return, $index, length($key), $value);
+        }
+    }
+    $return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::Project
+
+=head1 SYNOPSIS
+
+    use FCM::Admin::Project;
+    $project = FCM::Admin::Project->new({name => 'foo'});
+    $path = $project->get_svn_live_path();
+
+=head1 DESCRIPTION
+
+An object of this class represents a project hosted/managed by FCM. The methods
+of this class relies on L<FCM::Admin::Config|FCM::Admin::Config> for many of the
+configurations.
+
+=head1 METHODS
+
+=over 4
+
+=item FCM::Admin::Project->new({name => $name})
+
+Returns a new instance. A name of the project must be specified.
+
+=item $project->get_name()
+
+Returns the name of the project.
+
+=item $project->get_svn_archive_base_name()
+
+Returns the base name of the backup archive of the project's Subversion
+repository.
+
+=item $project->get_svn_backup_path()
+
+Returns the path to the backup archive of the project's Subversion repository.
+
+=item $project->get_svn_base_name()
+
+Returns the base name of the project's Subversion repository.
+
+=item $project->get_svn_dump_path()
+
+Returns the path to the revision dumps of the project's Subversion repository.
+
+=item $project->get_svn_live_hook_path()
+
+Returns the path to the project's SVN live repository's hooks directory.
+
+=item $project->get_svn_live_path()
+
+Returns the path to the project's SVN live repository.
+
+=item $project->get_svn_file_uri()
+
+Returns the file:// URI to the project's SVN live repository.
+
+=item $project->get_trac_archive_base_name()
+
+Returns the base name of the project's Trac environment backup archive.
+
+=item $project->get_trac_backup_path()
+
+Returns the path to the project's Trac backup archive.
+
+=item $project->get_trac_live_db_path()
+
+Returns the path to the project's Trac live environment's database.
+
+=item $project->get_trac_live_ini_path()
+
+Returns the path to the project's Trac live environment's INI file.
+
+=item $project->get_trac_live_path()
+
+Returns the path to the project's Trac live environment.
+
+=item $project->get_trac_live_url()
+
+Returns the URL to the project's Trac live environment.
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM::Admin::Config|FCM::Admin::Config>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Runner.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Runner.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Runner.pm	(revision 5095)
@@ -0,0 +1,299 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Runner;
+
+use IO::Handle;
+use POSIX qw{strftime};
+
+# The default values of the attributes
+my %DEFAULT = (
+    exceptions     => [],
+    max_attempts   => 3,
+    retry_interval => 5,
+    stderr_handle  => \*STDERR,
+    stdout_handle  => \*STDOUT,
+);
+
+my $INSTANCE;
+
+# ------------------------------------------------------------------------------
+# Returns a unique instance of this class. Creates the instance on first call.
+sub instance {
+    my ($class) = @_;
+    if (!defined($INSTANCE)) {
+        $INSTANCE = bless({%DEFAULT}, $class);
+    }
+    return $INSTANCE;
+}
+
+# ------------------------------------------------------------------------------
+# Adds a new exception to the list of exceptions.
+sub _add_exception {
+    my ($self, $exception) = @_;
+    push(@{$self->get_exceptions()}, $exception);
+}
+
+# ------------------------------------------------------------------------------
+# Returns the list of exceptions (or a reference to the list in scalar context).
+sub get_exceptions {
+    my ($self) = @_;
+    return (wantarray() ? @{$self->{exceptions}} : $self->{exceptions});
+}
+
+# ------------------------------------------------------------------------------
+# Returns the latest exception in the exception list.
+sub get_latest_exception {
+    my ($self) = @_;
+    if (exists($self->get_exceptions()->[-1])) {
+        return $self->get_exceptions()->[-1];
+    }
+    else {
+        return;
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Returns the maximum number of attempts for the "run_with_retries" method.
+sub get_max_attempts {
+    my ($self) = @_;
+    return $self->{max_attempts};
+}
+
+# ------------------------------------------------------------------------------
+# Returns the retry interval for the "run_with_retries" method.
+sub get_retry_interval {
+    my ($self) = @_;
+    return $self->{retry_interval};
+}
+
+# ------------------------------------------------------------------------------
+# Returns the file handle for STDERR.
+sub get_stderr_handle {
+    my ($self) = @_;
+    if (!IO::Handle::opened($self->{stderr_handle})) {
+        $self->{stderr_handle} = $DEFAULT{stderr_handle};
+    }
+    return $self->{stderr_handle};
+}
+
+# ------------------------------------------------------------------------------
+# Returns the file handle for STDOUT.
+sub get_stdout_handle {
+    my ($self) = @_;
+    if (!IO::Handle::opened($self->{stdout_handle})) {
+        $self->{stdout_handle} = $DEFAULT{stdout_handle};
+    }
+    return $self->{stdout_handle};
+}
+
+# ------------------------------------------------------------------------------
+# Runs $sub_ref->(@arguments) with a diagnostic $message. Dies on error.
+sub run {
+    my ($self, $message, $sub_ref, @arguments) = @_;
+    printf(
+        {$self->get_stdout_handle()}
+        qq{%s: %s\n}, strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()), $message,
+    );
+    eval {
+        if (!$sub_ref->(@arguments)) {
+            die(qq{\n});
+        }
+    };
+    if ($@) {
+        my $e = $@;
+        chomp($e);
+        my $exception
+            = sprintf(qq{ERROR %s%s\n}, $message, ($e ? qq{ - $e} : qq{}));
+        $self->_add_exception($exception);
+        die($exception);
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Runs $sub_ref->(@arguments) with a diagnostic $message. Warns on error.
+sub run_continue {
+    my ($self, $message, $sub_ref, @arguments) = @_;
+    my $rc;
+    eval {
+        $rc = $self->run($message, $sub_ref, @arguments);
+    };
+    if ($@) {
+        print({$self->get_stderr_handle()} $@);
+        return;
+    }
+    return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# Runs $sub_ref->(@arguments) with a diagnostic $message. Retries on error.
+sub run_with_retries {
+    my ($self, $message, $sub_ref, @arguments) = @_;
+    for my $i_attempt (1 .. $self->get_max_attempts()) {
+        my $attempt_message = sprintf(
+            qq{%s, attempt %d of %d},
+            $message, $i_attempt, $self->get_max_attempts(),
+        );
+        if ($i_attempt == $self->get_max_attempts()) {
+            return $self->run($attempt_message, $sub_ref, @arguments);
+        }
+        else {
+            if ($self->run_continue($attempt_message, $sub_ref, @arguments)) {
+                return 1;
+            }
+            sleep($self->get_retry_interval());
+        }
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Sets the maximum number of attempts for the "run_with_retries" method.
+sub set_max_attempts {
+    my ($self, $value) = @_;
+    $self->{max_attempts} = $value;
+}
+
+# ------------------------------------------------------------------------------
+# Sets the retry interval for the "run_with_retries" method.
+sub set_retry_interval {
+    my ($self, $value) = @_;
+    $self->{retry_interval} = $value;
+}
+
+# ------------------------------------------------------------------------------
+# Sets the file handle for STDERR.
+sub set_stderr_handle {
+    my ($self, $value) = @_;
+    if (defined($value) && IO::Handle::opened($value)) {
+        $self->{stderr_handle} = $value;
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Sets the file handle for STDOUT.
+sub set_stdout_handle {
+    my ($self, $value) = @_;
+    if (defined($value) && IO::Handle::opened($value)) {
+        $self->{stdout_handle} = $value;
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::Runner
+
+=head1 SYNOPSIS
+
+    $runner = FCM::Admin::Runner->instance();
+    $runner->run($message, sub { ... });
+
+=head1 DESCRIPTION
+
+Provides a simple way to run a piece of code with a time-stamped diagnostic
+message.
+
+=head1 METHODS
+
+=over 4
+
+=item FCM::Admin::Runner->instance()
+
+Returns a unique instance of FCM::Admin::Runner.
+
+=item $runner->get_exceptions()
+
+Returns a list containing all the exceptions captured by the previous
+invocations of the $runner->run() method. In SCALAR context, returns a reference
+to the list.
+
+=item $runner->get_latest_exception()
+
+Returns the latest exception captured by the $runner->run() method. Returns
+undef if there is no captured exception in the list.
+
+=item $runner->get_max_attempts()
+
+Returns the number of maximum retries for the
+$runner->run_with_retries($message,$sub_ref,@arguments) method. (Default: 3)
+
+=item $runner->get_retry_interval()
+
+Returns the interval (in seconds) between retries for the
+$runner->run_with_retries($message,$sub_ref,@arguments) method. (Default: 5)
+
+=item $runner->get_stderr_handle()
+
+Returns the file handle for standard error output. (Default: \*STDERR)
+
+=item $runner->get_stdout_handle()
+
+Returns the file handle for standard output. (Default: \*STDOUT)
+
+=item $runner->run($message,$sub_ref,@arguments)
+
+Prints the diagnostic $message and runs $sub_ref (with extra @arguments).
+Returns true if $sub_ref returns true. die() with a message that looks like
+"ERROR $message\n" if $sub_ref returns false or die().
+
+=item $runner->run_continue($message,$sub_ref,@arguments)
+
+Same as $runner->run($message,$sub_ref,@arguments), but only issue a warning
+(and returns false) if $sub_ref returns false or die().
+
+=item $runner->run_with_retries($message,$sub_ref,@arguments)
+
+Attempts $runner->run($message,$sub_ref,@arguments) for a number of times up to
+$runner->get_max_attempts(), with a delay of $runner->get_retry_interval()
+between each attempt. die() if $sub_ref still returns false in the final
+attempt. Returns true on success.
+
+=item $runner->set_max_attempts($value)
+
+Sets the maximum number of attempts in the
+$runner->run_with_retries($message,$sub_ref,@arguments) method.
+
+=item $runner->set_retry_interval($value)
+
+Sets the interval (in seconds) between retries for the
+$runner->run_with_retries($message,$sub_ref,@arguments) method.
+
+=item $runner->set_stderr_handle($value)
+
+Sets the file handle for standard error output to an alternate file handle. The
+$value must be a valid file descriptor.
+
+=item $runner->set_stdout_handle($value)
+
+Sets the file handle for standard output to an alternate file handle. The $value
+must be a valid file descriptor.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/System.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/System.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/System.pm	(revision 5095)
@@ -0,0 +1,1414 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::System;
+
+use Config::IniFiles;
+use DBI; # See also: DBD::SQLite
+use Exporter qw{import};
+use FCM::Admin::Config;
+use FCM::Admin::Project;
+use FCM::Admin::Runner;
+use FCM::Admin::User;
+use FCM::Admin::Util qw{
+    read_file
+    run_copy
+    run_create_archive
+    run_extract_archive
+    run_mkpath
+    run_rename
+    run_rmtree
+    run_rsync
+    run_symlink
+    write_file
+};
+use Fcntl qw{:mode}; # for S_IRGRP, S_IWGRP, S_IROTH, etc
+use File::Basename qw{basename dirname};
+use File::Compare qw{compare};
+use File::Find qw{find};
+use File::Spec::Functions qw{catfile rel2abs};
+use File::Temp qw{tempdir tempfile};
+use IO::Compress::Gzip qw{gzip};
+use IO::Dir;
+use IO::Pipe;
+use IO::Zlib;
+use List::Util qw{first};
+use POSIX qw{strftime};
+use Text::ParseWords qw{shellwords};
+
+our @EXPORT_OK = qw{
+    add_svn_repository
+    add_trac_environment
+    backup_svn_repository
+    backup_trac_environment
+    backup_trac_files
+    distribute_wc
+    filter_projects
+    get_projects_from_svn_backup
+    get_projects_from_svn_live
+    get_projects_from_trac_backup
+    get_projects_from_trac_live
+    get_users
+    housekeep_svn_hook_logs
+    install_svn_hook
+    manage_users_in_svn_passwd
+    manage_users_in_trac_passwd
+    manage_users_in_trac_db_of
+    recover_svn_repository
+    recover_trac_environment
+    recover_trac_files
+    vacuum_trac_env_db
+    verify_users
+};
+
+our $NO_OVERWRITE = 1;
+our $BUFFER_SIZE = 4096;
+our @SVN_REPOS_ROOT_HOOK_ITEMS = qw{commit.conf svnperms.conf};
+our %USER_INFO_TOOL_OF = (
+    'ldap'   => 'FCM::Admin::Users::LDAP',
+    'passwd' => 'FCM::Admin::Users::Passwd',
+);
+our $USER_INFO_TOOL;
+
+our $UTIL = $FCM::Admin::Config::UTIL;
+my $CONFIG = FCM::Admin::Config->instance();
+my $RUNNER = FCM::Admin::Runner->instance();
+
+# ------------------------------------------------------------------------------
+# Adds a new Subversion repository.
+sub add_svn_repository {
+    my ($project_name) = @_;
+    my $project = FCM::Admin::Project->new({name => $project_name});
+    if (-e $project->get_svn_live_path()) {
+        die(sprintf(
+            "%s: Subversion repository already exists at %s.\n",
+            $project_name,
+            $project->get_svn_live_path(),
+        ));
+    }
+    my $repos_path = $project->get_svn_live_path();
+    $RUNNER->run(
+        "creating Subversion repository at $repos_path",
+        sub {!system(qw{svnadmin create}, $repos_path)},
+    );
+    my $group = $CONFIG->get_svn_group();
+    if ($group) {
+        _chgrp_and_chmod($project->get_svn_live_path(), $group);
+    }
+    install_svn_hook($project);
+    housekeep_svn_hook_logs($project);
+}
+
+# ------------------------------------------------------------------------------
+# Adds a new Trac environment.
+sub add_trac_environment {
+    my ($project_name) = @_;
+    my $project = FCM::Admin::Project->new({name => $project_name});
+    if (-e $project->get_trac_live_path()) {
+        die(sprintf(
+            "%s: Trac environment already exists at %s.\n",
+            $project_name,
+            $project->get_trac_live_path(),
+        ));
+    }
+    my $RUN = sub{$RUNNER->run(@_)};
+    my $TRAC_ADMIN = sub {
+        my ($log, @args) = @_;
+        my @command = (q{trac-admin}, $project->get_trac_live_path(), @args);
+        $RUN->($log, sub {!system(@command)});
+    };
+    $TRAC_ADMIN->(
+        "initialising Trac environment",
+        q{initenv},
+        $project_name,
+        q{sqlite:db/trac.db},
+        q{--inherit=../../trac.ini},
+    );
+    my $group = $CONFIG->get_trac_group();
+    if ($group) {
+        _chgrp_and_chmod($project->get_trac_live_path(), $group);
+    }
+    for my $item (qw{component1 component2}) {
+        $TRAC_ADMIN->(
+            "removing example component $item", qw{component remove}, $item,
+        );
+    }
+    for my $item (qw{1.0 2.0}) {
+        $TRAC_ADMIN->(
+            "removing example version $item", qw{version remove}, $item,
+        );
+    }
+    for my $item (qw{milestone1 milestone2 milestone3 milestone4}) {
+        $TRAC_ADMIN->(
+            "removing example milestone $item", qw{milestone remove}, $item,
+        );
+    }
+    for my $item (
+        ['major'    => 'normal'  ],
+        ['critical' => 'major'   ],
+        ['blocker'  => 'critical'],
+    ) {
+        my ($old, $new) = @{$item};
+        $TRAC_ADMIN->(
+            "changing priority $old to $new", qw{priority change}, $old, $new,
+        );
+    }
+    if (-d $project->get_svn_live_path()) {
+        $TRAC_ADMIN->(
+            "adding repository",
+            qw{repository add (default)},
+            $project->get_svn_live_path(),
+        );
+    }
+    $TRAC_ADMIN->(
+        "adding admin permission", qw{permission add admin TRAC_ADMIN},
+    );
+    $TRAC_ADMIN->(
+        "adding admin permission", qw{permission add owner TRAC_ADMIN},
+    );
+    my @admin_users = shellwords($CONFIG->get_trac_admin_users());
+    for my $item (@admin_users) {
+        $TRAC_ADMIN->(
+            "adding admin user $item", qw{permission add}, $item, q{admin},
+        );
+    }
+    $TRAC_ADMIN->(
+        "adding TICKET_EDIT_CC permission to authenticated",
+        qw{permission add}, 'authenticated', qw{TICKET_EDIT_CC},
+    );
+    $TRAC_ADMIN->(
+        "adding TICKET_EDIT_DESCRIPTION permission to authenticated",
+        qw{permission add}, 'authenticated', qw{TICKET_EDIT_DESCRIPTION},
+    );
+    $RUN->(
+        "adding names and emails of users",
+        sub {manage_users_in_trac_db_of($project, get_users())},
+    );
+    $RUN->(
+        "updating configuration file",
+        sub {
+            my $trac_ini_path = $project->get_trac_live_ini_path();
+            my $trac_ini = Config::IniFiles->new(q{-file} => $trac_ini_path);
+            if (!$trac_ini) {
+                die("$trac_ini_path: cannot open.\n");
+            }
+            for (
+                #section    #key        #value
+                ['inherit', 'file'    , '../../trac.ini,../../intertrac.ini'],
+                ['project', 'descr'   , $project->get_name()                ],
+                ['trac'   , 'base_url', $project->get_trac_live_url()       ],
+            ) {
+                my ($section, $key, $value) = @{$_};
+                if (!$trac_ini->SectionExists($section)) {
+                    $trac_ini->AddSection($section);
+                }
+                if (!$trac_ini->newval($section, $key, $value)) {
+                    die("$trac_ini_path: $section:$key: cannot set value.\n");
+                }
+            }
+            return $trac_ini->RewriteConfig();
+        },
+    );
+    $RUN->(
+        "updating InterTrac",
+        sub {
+            my $ini_path = catfile(
+                $CONFIG->get_trac_live_dir(),
+                'intertrac.ini',
+            );
+            if (!-e $ini_path) {
+                open(my $handle, '>', $ini_path) || die("$ini_path: $!\n");
+                close($handle) || die("$ini_path: $!\n");
+            }
+            my $trac_ini = Config::IniFiles->new(
+                q{-allowempty} => 1,
+                q{-file} => $ini_path,
+            );
+            if (!defined($trac_ini)) {
+                die("$ini_path: cannot open.\n");
+            }
+            if (!$trac_ini->SectionExists(q{intertrac})) {
+                $trac_ini->AddSection(q{intertrac});
+            }
+            my $name = $project->get_name();
+            for (
+                [q{title} , $name                        ],
+                [q{url}   , $project->get_trac_live_url()],
+                [q{compat}, 'false'                      ],
+            ) {
+                my ($key, $value) = @{$_};
+                my $option = lc($name) . q{.} . $key;
+                if (!$trac_ini->newval(q{intertrac}, $option, $value)) {
+                    die("$ini_path: intertrac:$option: cannot set value.\n");
+                }
+            }
+            return $trac_ini->RewriteConfig();
+        },
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Backup the SVN repository of a project.
+sub backup_svn_repository {
+    my ($option_hash_ref, $project) = @_;
+    my $RUN = sub {FCM::Admin::Runner->instance()->run(@_)};
+    if (!exists($option_hash_ref->{'no-pack'})) {
+        $RUN->(
+            sprintf("packing %s", $project->get_svn_live_path()),
+            sub {!system(qw{svnadmin pack}, $project->get_svn_live_path())},
+        );
+    }
+    my $base_name = $project->get_svn_base_name();
+    run_mkpath($CONFIG->get_svn_backup_dir());
+    my $work_dir = tempdir("$base_name.backup.XXXXXX", CLEANUP => 1, TMPDIR => 1);
+    my $work_path = catfile($work_dir, $base_name);
+    $RUN->(
+        sprintf(
+            "hotcopying %s to %s", $project->get_svn_live_path(), $work_path,
+        ),
+        sub {!system(
+            qw{svnadmin hotcopy}, $project->get_svn_live_path(), $work_path,
+        )},
+        # Note: "hotcopy" is not yet possible via SVN::Repos
+    );
+    if (!exists($option_hash_ref->{'no-verify-integrity'})) {
+        my $VERIFIED_REVISION_REGEX = qr{\A\*\s+Verified\s+revision\s+\d+\.}xms;
+        $RUN->(
+            "verifying integrity of SVN repository of $project",
+            sub {
+                my $pipe = IO::Pipe->new();
+                $pipe->reader(sprintf(
+                    qq{svnadmin verify %s 2>&1}, $work_path,
+                ));
+                while (my $line = $pipe->getline()) {
+                    if ($line !~ $VERIFIED_REVISION_REGEX) { # don't print
+                        print($line);
+                    }
+                }
+                $pipe->close();
+                return $? ? () : (1);
+                # Note: "verify" is not yet possible via SVN::Repos
+            },
+        );
+    }
+    _create_backup_archive(
+        $work_path,
+        $CONFIG->get_svn_backup_dir(),
+        $project->get_svn_archive_base_name(),
+    );
+    if (!exists($option_hash_ref->{'no-housekeep-dumps'})) {
+        my $base_name = $project->get_svn_base_name();
+        my $dump_path = $CONFIG->get_svn_dump_dir();
+        my $youngest = _svnlook_youngest($work_path);
+        # Note: could use SVN::Repos for "youngest"
+        $RUN->(
+            "housekeeping $dump_path/$base_name-*.gz",
+            sub {
+                my @rev_dump_paths;
+                _get_files_from(
+                    $dump_path,
+                    sub {
+                        my ($dump_base_name, $path) = @_;
+                        my ($name, $rev)
+                            = $dump_base_name =~ qr{\A(.*)-(\d+)\.gz\z}msx;
+                        if (    !$name
+                            ||  !$rev
+                            ||  $name ne $base_name
+                            ||  $rev > $youngest
+                        ) {
+                            return;
+                        }
+                        push(@rev_dump_paths, $path);
+                    },
+                );
+                for my $rev_dump_path (@rev_dump_paths) {
+                    run_rmtree($rev_dump_path);
+                }
+                return 1;
+            }
+        );
+    }
+    run_rmtree($work_dir);
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Backup the Trac environment of a project.
+sub backup_trac_environment {
+    my ($option_hash_ref, $project) = @_;
+    my $trac_live_path = $project->get_trac_live_path();
+    my $base_name = $project->get_name();
+    run_mkpath($CONFIG->get_trac_backup_dir());
+    my $work_dir = tempdir("$base_name.backup.XXXXXX", CLEANUP => 1, TMPDIR => 1);
+    my $work_path = catfile($work_dir, $base_name);
+    $RUNNER->run_with_retries(
+        sprintf(
+            qq{hotcopying %s to %s},
+            $project->get_trac_live_path(),
+            $work_path,
+        ),
+        sub {
+            return !system(
+                q{trac-admin},
+                $project->get_trac_live_path(),
+                q{hotcopy},
+                $work_path,
+            );
+        },
+    );
+    if (!exists($option_hash_ref->{'no-verify-integrity'})) {
+        my $db_path = catfile($work_path, qw{db trac.db});
+        my $db_name = catfile($project->get_name(), qw{db trac.db});
+        $RUNNER->run(
+            "checking $db_name for integrity",
+            sub {
+                my $db_handle
+                    = DBI->connect(qq{dbi:SQLite:dbname=$db_path}, q{}, q{});
+                if (!$db_handle) {
+                    return;
+                }
+                my $rc = defined($db_handle->do(q{pragma integrity_check;}));
+                $db_handle->disconnect();
+                return $rc;
+            },
+        );
+    }
+    _create_backup_archive(
+        $work_path,
+        $CONFIG->get_trac_backup_dir(),
+        $project->get_trac_archive_base_name(),
+    );
+    run_rmtree($work_dir);
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Backup misc files in the Trac live directory to the Trac backup directory.
+sub backup_trac_files {
+    # (no argument)
+    _copy_files($CONFIG->get_trac_live_dir(), $CONFIG->get_trac_backup_dir());
+}
+
+# ------------------------------------------------------------------------------
+# Distributes the central FCM working copy to standard locations.
+sub distribute_wc {
+    my $rc = 1;
+    my @RSYNC_OPTS = qw{--timeout=1800 --exclude=.*};
+    my @sources;
+    for my $source_key (shellwords($CONFIG->get_mirror_keys())) {
+        my $method = "get_$source_key";
+        if ($CONFIG->can($method)) {
+            push(@sources, $CONFIG->$method());
+        }
+    }
+    for my $dest (shellwords($CONFIG->get_mirror_dests())) {
+        $rc = $RUNNER->run_continue(
+            "distributing FCM to $dest",
+            sub {
+                run_rsync(
+                    \@sources, $dest,
+                    [@RSYNC_OPTS, qw{-a --delete-excluded}],
+                );
+            },
+        ) && $rc;
+    }
+    return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# Returns a filtered list of projects matching names in a list.
+sub filter_projects {
+    my ($project_list_ref, $filter_list_ref) = @_;
+    if (!@{$filter_list_ref}) {
+        return @{$project_list_ref};
+    }
+    my %project_of = map {($_->get_name(), $_)} @{$project_list_ref};
+    my @projects;
+    my @unmatched_names;
+    for my $name (@{$filter_list_ref}) {
+        if (exists($project_of{$name})) {
+            push(@projects, $project_of{$name});
+        }
+        else {
+            push(@unmatched_names, $name);
+        }
+    }
+    if (@unmatched_names) {
+        die("@unmatched_names: not found\n");
+    }
+    return @projects;
+}
+
+# ------------------------------------------------------------------------------
+# Returns a list of projects by searching the backup SVN directory.
+sub get_projects_from_svn_backup {
+    # (no dummy argument)
+    my $SVN_PROJECT_SUFFIX = $CONFIG->get_svn_project_suffix();
+    my @projects;
+    _get_files_from(
+        $CONFIG->get_svn_backup_dir(),
+        sub {
+            my ($base_name, $path) = @_;
+            my $name = $base_name;
+            if ($name !~ s{$SVN_PROJECT_SUFFIX\.tgz\z}{}xms) {
+                return;
+            }
+            if (!-f $path) {
+                return;
+            }
+            push(@projects, FCM::Admin::Project->new({name => $name}));
+        },
+    );
+    return @projects;
+}
+
+# ------------------------------------------------------------------------------
+# Returns a list of projects by searching the live SVN directory.
+sub get_projects_from_svn_live {
+    # (no dummy argument)
+    my $SVN_PROJECT_SUFFIX = $CONFIG->get_svn_project_suffix();
+    my @projects;
+    _get_files_from(
+        $CONFIG->get_svn_live_dir(),
+        sub {
+            my ($base_name, $path) = @_;
+            my $name = $base_name;
+            $name =~ s{$SVN_PROJECT_SUFFIX\z}{}xms;
+            if (!-d $path) {
+                return;
+            }
+            push(@projects, FCM::Admin::Project->new({name => $name}));
+        },
+    );
+    return @projects;
+}
+
+# ------------------------------------------------------------------------------
+# Returns a list of projects by searching the backup Trac directory.
+sub get_projects_from_trac_backup {
+    # (no dummy argument)
+    my @projects;
+    _get_files_from(
+        $CONFIG->get_trac_backup_dir(),
+        sub {
+            my ($base_name, $path) = @_;
+            my $name = $base_name;
+            if ($name !~ s{\.tgz\z}{}xms) {
+                return;
+            }
+            if (!-f $path) {
+                return;
+            }
+            push(@projects, FCM::Admin::Project->new({name => $name}));
+        },
+    );
+    return @projects;
+}
+
+# ------------------------------------------------------------------------------
+# Returns a list of projects by searching the live Trac directory.
+sub get_projects_from_trac_live {
+    # (no dummy argument)
+    my @projects;
+    _get_files_from(
+        $CONFIG->get_trac_live_dir(),
+        sub {
+            my ($name, $path) = @_;
+            if (!-d $path) {
+                return;
+            }
+            push(@projects, FCM::Admin::Project->new({name => $name}));
+        },
+    );
+    return @projects;
+}
+
+# ------------------------------------------------------------------------------
+# Return a HASH of valid users. If @only_users, then return only users matching
+# these IDs.
+sub get_users {
+    my @only_users = @_;
+    my $name = $CONFIG->get_user_info_tool();
+    if (!defined($USER_INFO_TOOL)) {
+        my $class = $UTIL->class_load($USER_INFO_TOOL_OF{$name});
+        $USER_INFO_TOOL = $class->new({util => $UTIL});
+    }
+    my $user_hash_ref = $USER_INFO_TOOL->get_users_info(@only_users);
+    if (!%{$user_hash_ref}) {
+        die("No user found via $name.\n");
+    }
+    return $user_hash_ref;
+}
+
+# ------------------------------------------------------------------------------
+# Housekeep logs generated by hook scripts of a SVN project.
+sub housekeep_svn_hook_logs {
+    my ($project) = @_;
+    my $project_path = $project->get_svn_live_path();
+    my $hook_source_dir = catfile($CONFIG->get_fcm_home(), 'etc', 'svn-hooks');
+    my $today = strftime("%Y%m%d", gmtime());
+    my $date_p1w = strftime("%Y%m%d", gmtime(time() - 604800)); # 1 week ago
+    my $date_p4w = strftime("%Y%m%d", gmtime(time() - 2419200)); # 4 weeks ago
+    my @hook_names = map {basename($_)} glob(catfile($hook_source_dir, q{*}));
+    for my $hook_name (sort @hook_names) {
+        my $log_path = catfile($project_path, 'log', $hook_name . '.log');
+        my $log_path_cur;
+        # Determine whether log file is more than a week old
+        if (    -l $log_path
+            &&  index(readlink($log_path), $hook_name . '.log.') == 0
+        ) {
+            my $path = readlink($log_path);
+            my ($date) = $path =~ qr{\.log\.(\d{8}\d*)\z}msx;
+            if ($date && $date > $date_p1w) {
+                $log_path_cur = catfile($project_path, 'log', $path);
+            }
+        }
+        # Create latest log, if necessary
+        if (!$log_path_cur) {
+            $log_path_cur = "$log_path.$today";
+            write_file($log_path_cur);
+        }
+        if (    !-e $log_path
+            ||  !-l $log_path
+            ||  readlink($log_path) ne basename($log_path_cur)
+        ) {
+            run_rmtree($log_path);
+            run_symlink(basename($log_path_cur), $log_path);
+        }
+        # Remove logs older than $keep_threshold
+        for my $path (
+            sort glob(catfile($project_path, 'log', $hook_name . '*.log.*'))
+        ) {
+            my ($date, $dot_gz) = $path =~ qr{\.log\.(\d{8}\d*)(\.gz)?\z}msx;
+            if (    $date && $date <= $date_p4w
+                ||  $date && $date <= $date_p1w && !-s $path
+            ) {
+                run_rmtree($path);
+            }
+            elsif ($date && $date <= $date_p1w && !$dot_gz) {
+                $RUNNER->run(
+                    "gzip $path",
+                    sub {gzip($path, "$path.gz") && unlink($path)},
+                );
+            }
+        }
+    }
+    my $group = $CONFIG->get_svn_group();
+    if ($group) {
+        _chgrp_and_chmod(catfile($project_path, 'log'), $group);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Installs hook scripts to a SVN project.
+sub install_svn_hook {
+    my ($project, $clean_mode) = @_;
+    # Write hook environment configuration
+    my $project_path = $project->get_svn_live_path();
+    my $conf_dest = catfile($project_path, qw{conf hooks-env});
+    write_file(
+        $conf_dest,
+        "[default]\n",
+        map {sprintf("%s=%s\n", @{$_});}
+        grep {$_->[1];} (
+            ['FCM_HOME', $CONFIG->get_fcm_home()],
+            ['FCM_SITE_HOME', $CONFIG->get_fcm_site_home()],
+            ['FCM_SVN_HOOK_ADMIN_EMAIL', $CONFIG->get_admin_email()],
+            ['FCM_SVN_HOOK_COMMIT_DUMP_DIR', $CONFIG->get_svn_dump_dir()],
+            ['FCM_SVN_HOOK_NOTIFICATION_FROM', $CONFIG->get_notification_from()],
+            ['FCM_SVN_HOOK_REPOS_SUFFIX', $CONFIG->get_svn_project_suffix()],
+            ['FCM_SVN_HOOK_TRAC_ROOT_DIR', $CONFIG->get_trac_live_dir()],
+            ['PATH', $CONFIG->get_svn_hook_path_env()],
+            ['TZ', 'UTC'],
+        )
+    );
+    my %path_of = ();
+    # Search for hook scripts:
+    # * default sets
+    # * selected items from top of repository, e.g. svnperms.conf
+    # * site overrides
+    _get_files_from(
+        catfile($CONFIG->get_fcm_home(), 'etc', 'svn-hooks'),
+        sub {
+            my ($base_name, $path) = @_;
+            if (index($base_name, q{.}) == 0 || -d $path) {
+                return;
+            }
+            $path_of{$base_name} = $path;
+        },
+    );
+    for my $line (qx{svnlook tree -N $project_path}) {
+        chomp($line);
+        my ($base_name) = $line =~ qr{\A\s*(.*)\z}msx;
+        if (grep {$_ eq $base_name} @SVN_REPOS_ROOT_HOOK_ITEMS) {
+            $path_of{$base_name} = "^/$base_name";
+        }
+    }
+    _get_files_from(
+        catfile(
+            $CONFIG->get_fcm_site_home(), 'svn-hooks', $project->get_name(),
+        ),
+        sub {
+            my ($base_name, $path) = @_;
+            if (index($base_name, q{.}) == 0 || -d $path) {
+                return;
+            }
+            $path_of{$base_name} = $path;
+        },
+    );
+    # Install hook scripts and associated files
+    for my $base_name (sort keys(%path_of)) {
+        my $hook_source = $path_of{$base_name};
+        my $hook_dest = catfile($project->get_svn_live_hook_path(), $base_name);
+        if (index($hook_source, '^/') == 0) {
+            $RUNNER->run(
+                "install $hook_dest <- $hook_source",
+                sub {
+                    my $source = "file://$project_path/$base_name";
+                    !system(qw{svn export -q --force}, $source, $hook_dest)
+                        || die("\n");
+                    chmod((stat($hook_dest))[2] | S_IRGRP | S_IROTH, $hook_dest);
+                },
+            );
+        }
+        else {
+            run_copy($hook_source, $hook_dest);
+        }
+    }
+    # Clean hook destination, if necessary
+    if ($clean_mode) {
+        my $hook_path = $project->get_svn_live_hook_path();
+        for my $path (sort glob(catfile($hook_path, q{*}))) {
+            if (!exists($path_of{basename($path)})) {
+                run_rmtree($path);
+            }
+        }
+    }
+    my $group = $CONFIG->get_svn_group();
+    if ($group) {
+        _chgrp_and_chmod($project->get_svn_live_hook_path(), $group);
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Updates the SVN password file.
+sub manage_users_in_svn_passwd {
+    my ($user_ref) = @_;
+    if (!$CONFIG->get_svn_passwd_file()) {
+        return 1;
+    }
+    my $svn_passwd_file = catfile(
+        $CONFIG->get_svn_live_dir(),
+        $CONFIG->get_svn_passwd_file(),
+    );
+    $RUNNER->run(
+        "updating $svn_passwd_file",
+        sub {
+            my $USERS_SECTION = q{users};
+            my $svn_passwd_ini;
+            my $is_changed;
+            if (-f $svn_passwd_file) {
+                $svn_passwd_ini
+                    = Config::IniFiles->new(q{-file} => $svn_passwd_file);
+            }
+            else {
+                $svn_passwd_ini = Config::IniFiles->new();
+                $svn_passwd_ini->SetFileName($svn_passwd_file);
+                $svn_passwd_ini->AddSection($USERS_SECTION);
+                $is_changed = 1;
+            }
+            for my $name (($svn_passwd_ini->Parameters($USERS_SECTION))) {
+                if (!exists($user_ref->{$name})) {
+                    $RUNNER->run(
+                        "removing $name from $svn_passwd_file",
+                        sub {
+                            return
+                                $svn_passwd_ini->delval($USERS_SECTION, $name);
+                        },
+                    );
+                    $is_changed = 1;
+                }
+            }
+            for my $user (values(%{$user_ref})) {
+                if (!defined($svn_passwd_ini->val($USERS_SECTION, "$user"))) {
+                    $RUNNER->run(
+                        "adding $user to $svn_passwd_file",
+                        sub {
+                            $svn_passwd_ini->newval(
+                                $USERS_SECTION, $user->get_name(), q{},
+                            ),
+                        },
+                    );
+                    $is_changed = 1;
+                }
+            }
+            return ($is_changed ? $svn_passwd_ini->RewriteConfig() : 1);
+        },
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Updates the Trac password file.
+sub manage_users_in_trac_passwd {
+    my ($user_ref) = @_;
+    if (!$CONFIG->get_trac_passwd_file()) {
+        return 1;
+    }
+    my $trac_passwd_file = catfile(
+        $CONFIG->get_trac_live_dir(),
+        $CONFIG->get_trac_passwd_file(),
+    );
+    $RUNNER->run(
+        "updating $trac_passwd_file",
+        sub {
+            my %old_names;
+            my %new_names = %{$user_ref};
+            if (-f $trac_passwd_file) {
+                read_file(
+                    $trac_passwd_file,
+                    sub {
+                        my ($line) = @_;
+                        chomp($line);
+                        if (
+                            !$line || $line =~ qr{\A\s*\z}xms # blank line
+                            || $line =~ qr{\A\s*\#}xms        # comment line
+                        ) {
+                            return;
+                        }
+                        my ($name, $passwd) = split(qr{\s*:\s*}xms, $line);
+                        if (exists($new_names{$name})) {
+                            delete($new_names{$name});
+                        }
+                        else {
+                            $old_names{$name} = 1;
+                        }
+                    },
+                ) || return;
+            }
+            else {
+                write_file($trac_passwd_file) || return;
+            }
+            if (%old_names || %new_names) {
+                for my $name (keys(%old_names)) {
+                    $RUNNER->run(
+                        "removing $name from $trac_passwd_file",
+                        sub {
+                            return !system(
+                                qw{htpasswd -D}, $trac_passwd_file, $name,
+                            );
+                        },
+                    );
+                }
+                for my $name (keys(%new_names)) {
+                    $RUNNER->run(
+                        "adding $name to $trac_passwd_file",
+                        sub {
+                            return !system(
+                                qw{htpasswd -b}, $trac_passwd_file, $name, q{},
+                            );
+                        },
+                    );
+                    sleep(1); # ensure the random seed for htpasswd is changed
+                }
+            }
+            return 1;
+        },
+        # Note: can use HTTPD::UserAdmin, if it is installed
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Manages the session* tables in the DB of a Trac environment.
+sub manage_users_in_trac_db_of {
+    my ($project, $user_ref) = @_;
+    return $RUNNER->run_with_retries(
+        sprintf(
+            qq{checking/updating %s},
+            $project->get_trac_live_db_path(),
+        ),
+        sub {return _manage_users_in_trac_db_of($project, $user_ref)},
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Recovers a SVN repository from its backup.
+sub recover_svn_repository {
+    my ($project, $recover_dumps_option, $recover_hooks_option) = @_;
+    if (-e $project->get_svn_live_path()) {
+        die(sprintf(
+            "%s: live repository exists.\n",
+            $project->get_svn_live_path(),
+        ));
+    }
+    run_mkpath($CONFIG->get_svn_live_dir());
+    my $base_name = $project->get_svn_base_name();
+    my $work_dir = tempdir(
+        qq{$base_name.XXXXXX},
+        DIR => $CONFIG->get_svn_live_dir(),
+        CLEANUP => 1,
+    );
+    my $work_path = catfile($work_dir, $base_name);
+    _extract_backup_archive($project->get_svn_backup_path(), $work_path);
+    if ($recover_dumps_option) {
+        my $youngest = _svnlook_youngest($work_path);
+        my %dump_path_of;
+        _get_files_from(
+            $CONFIG->get_svn_dump_dir(),
+            sub {
+                my ($dump_base_name, $path) = @_;
+                my ($name, $rev) = $dump_base_name =~ qr{\A(.*)-(\d+)\.gz\z}msx;
+                if (    !$name
+                    ||  !$rev
+                    ||  $name ne $base_name
+                    ||  $rev <= $youngest
+                ) {
+                    return;
+                }
+                $dump_path_of{$rev} = $path;
+            },
+        );
+        for my $rev (sort {$a <=> $b} keys(%dump_path_of)) {
+            my $dump_path = $dump_path_of{$rev};
+            $RUNNER->run(
+                "loading $dump_path into $work_path",
+                sub {
+                    my $pipe = IO::Pipe->new();
+                    $pipe->writer(qw{svnadmin load}, $work_path);
+                    my $handle = IO::Zlib->new($dump_path, 'rb');
+                    if (!$handle) {
+                        die("$dump_path: $!\n");
+                    }
+                    while ($handle->read(my $buffer, $BUFFER_SIZE)) {
+                        $pipe->print($buffer);
+                    }
+                    $handle->close();
+                    return ($pipe->close());
+                },
+            );
+        }
+    }
+    run_rename($work_path, $project->get_svn_live_path());
+    my $group = $CONFIG->get_svn_group();
+    if ($group) {
+        _chgrp_and_chmod($project->get_svn_live_path(), $group);
+    }
+    if ($recover_hooks_option) {
+        install_svn_hook($project);
+        housekeep_svn_hook_logs($project);
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Recovers a Trac environment from its backup.
+sub recover_trac_environment {
+    my ($project) = @_;
+    if (-e $project->get_trac_live_path()) {
+        die(sprintf(
+            "%s: live environment exists.\n",
+            $project->get_trac_live_path(),
+        ));
+    }
+    run_mkpath($CONFIG->get_trac_live_dir());
+    my $base_name = $project->get_name();
+    my $work_dir = tempdir(
+        qq{$base_name.XXXXXX},
+        DIR => $CONFIG->get_trac_live_dir(),
+        CLEANUP => 1,
+    );
+    my $work_path = catfile($work_dir, $base_name);
+    _extract_backup_archive($project->get_trac_backup_path(), $work_path);
+    run_rename($work_path, $project->get_trac_live_path());
+    my $group = $CONFIG->get_trac_group();
+    if ($group) {
+        _chgrp_and_chmod($project->get_trac_live_path(), $group);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Recover a file from the Trac backup directory to the Trac live directory.
+sub recover_trac_files {
+    # (no argument)
+    _copy_files(
+        $CONFIG->get_trac_backup_dir(),
+        $CONFIG->get_trac_live_dir(),
+        $NO_OVERWRITE,
+        qr{\.tgz\z}msx,
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Vacuum the database of a Trac environment.
+sub vacuum_trac_env_db {
+    my ($project) = @_;
+    $RUNNER->run(
+        "performing vacuum on database of Trac environment for $project",
+        sub {
+            my $db_handle = _get_trac_db_handle_for($project);
+            if (!$db_handle) {
+                return;
+            }
+            $db_handle->do(q{vacuum;}) && $db_handle->disconnect();
+        },
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Verify users. Return a list of bad users from @users.
+sub verify_users {
+    my @users = @_;
+    if (!defined($USER_INFO_TOOL)) {
+        my $name = $CONFIG->get_user_info_tool();
+        my $class = $UTIL->class_load($USER_INFO_TOOL_OF{$name});
+        $USER_INFO_TOOL = $class->new({util => $UTIL});
+    }
+    return $USER_INFO_TOOL->verify_users(@users);
+}
+
+# ------------------------------------------------------------------------------
+# Changes/restores ownership and permission of a given $path to a given $group.
+sub _chgrp_and_chmod {
+    my ($path, $group) = @_;
+    my $gid = $group ? scalar(getgrnam($group)) : -1;
+    find(
+        sub {
+            my $file = $File::Find::name;
+            my $old_gid = (stat($file))[5];
+            if ($old_gid != $gid) {
+                $RUNNER->run(
+                    "changing group ownership for $file",
+                    sub {return chown(-1, $gid, $file)},
+                );
+            }
+            my $old_mode = (stat($file))[2];
+            my $mode = (stat($file))[2] | S_IRGRP | S_IWGRP;
+            if ($old_mode != $mode) {
+                $RUNNER->run(
+                    "adding group write permission for $file",
+                    sub {return chmod($mode, $file)},
+                );
+            }
+        },
+        $path,
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Copies files immediately under $source to $target.
+sub _copy_files {
+    my ($source, $target, $no_overwrite, $re_skip) = @_;
+    my @bases;
+    opendir(my $handle, $source) || die("$source: $!\n");
+    while (my $base = readdir($handle)) {
+        if (-f catfile($source, $base)) {
+            if ($no_overwrite && -f catfile($target, $base)) {
+                warn("[SKIP] $base: already exists in $target.\n");
+            }
+            elsif (!$re_skip || ($base !~ $re_skip)) {
+                push(@bases, $base);
+            }
+        }
+    }
+    closedir($handle);
+    run_mkpath($target);
+    for my $base (@bases) {
+        run_copy(map {catfile($_, $base)} ($source, $target));
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Creates backup archive from a path.
+sub _create_backup_archive {
+    my ($source_path, $backup_dir, $archive_base_name) = @_;
+    my $source_dir = dirname($source_path);
+    my $source_base_name = basename($source_path);
+    run_mkpath($backup_dir);
+    my ($fh, $work_backup_path)
+        = tempfile(qq{$archive_base_name.XXXXXX}, DIR => $backup_dir);
+    close($fh);
+    run_create_archive($work_backup_path, $source_dir, $source_base_name);
+    my $backup_path = catfile($backup_dir, $archive_base_name);
+    run_rename($work_backup_path, $backup_path);
+    my $mode = (stat($backup_path))[2] | S_IRGRP | S_IROTH;
+    return chmod($mode, $backup_path);
+}
+
+# ------------------------------------------------------------------------------
+# Extracts from a backup archive to a work path.
+sub _extract_backup_archive {
+    my ($archive_path, $work_path) = @_;
+    run_extract_archive($archive_path, dirname($work_path));
+    if (! -e $work_path) {
+        my ($base_name) = basename($work_path);
+        die("$base_name: does not exist in archive $archive_path.\n");
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Searches a directory for files and invokes a callback on each file.
+sub _get_files_from {
+    my ($dir_path, $callback_ref) = @_;
+    my $dir_handle = IO::Dir->new($dir_path);
+    if (!defined($dir_handle)) {
+        return;
+    }
+    BASE_NAME:
+    while (my $base_name = $dir_handle->read()) {
+        my $path = catfile($dir_path, $base_name);
+        if (index($base_name, q{.}) == 0) {
+            next BASE_NAME;
+        }
+        $callback_ref->($base_name, $path);
+    }
+    return $dir_handle->close();
+}
+
+# ------------------------------------------------------------------------------
+# Returns a database handle for the database of a Trac environment.
+sub _get_trac_db_handle_for {
+    my ($project) = @_;
+    my $db_path = $project->get_trac_live_db_path();
+    return DBI->connect(qq{dbi:SQLite:dbname=$db_path}, q{}, q{});
+}
+
+# ------------------------------------------------------------------------------
+# Manages the session* tables in the DB of a Trac environment.
+sub _manage_users_in_trac_db_of {
+    my ($project, $user_ref) = @_;
+    my $db_handle = _get_trac_db_handle_for($project);
+    if (!$db_handle) {
+        return;
+    }
+    SESSION: {
+        my $session_select_statement = $db_handle->prepare(
+            "SELECT sid FROM session WHERE authenticated == 1",
+        );
+        my $session_insert_statement = $db_handle->prepare(
+            "INSERT INTO session VALUES (?, 1, 0)",
+        );
+        my $session_delete_statement = $db_handle->prepare(
+            "DELETE FROM session WHERE sid == ?",
+        );
+        $session_select_statement->execute();
+        my $is_changed = 0;
+        my %session_old_users;
+        while (my ($sid) = $session_select_statement->fetchrow_array()) {
+            if (exists($user_ref->{$sid})) {
+                $session_old_users{$sid} = 1;
+            }
+            else {
+                $RUNNER->run(
+                    "session: removing $sid",
+                    sub{return $session_delete_statement->execute($sid)},
+                );
+                $is_changed = 1;
+            }
+        }
+        for my $sid (keys(%{$user_ref})) {
+            if (!exists($session_old_users{$sid})) {
+                $RUNNER->run(
+                    "session: adding $sid",
+                    sub {return $session_insert_statement->execute($sid)},
+                );
+                $is_changed = 1;
+            }
+        }
+        $session_select_statement->finish();
+        $session_insert_statement->finish();
+        $session_delete_statement->finish();
+    }
+    SESSION_ATTRIBUTE: {
+        my $attribute_select_statement = $db_handle->prepare(
+            "SELECT sid,name,value FROM session_attribute "
+                . "WHERE authenticated == 1",
+        );
+        my $attribute_insert_statement = $db_handle->prepare(
+            "INSERT INTO session_attribute VALUES (?, 1, ?, ?)",
+        );
+        my $attribute_update_statement = $db_handle->prepare(
+            "UPDATE session_attribute SET value = ? "
+                . "WHERE sid = ? AND authenticated == 1 AND name == ?",
+        );
+        my $attribute_delete_statement = $db_handle->prepare(
+            "DELETE FROM session_attribute WHERE sid == ?",
+        );
+        my $attribute_delete_name_statement = $db_handle->prepare(
+            "DELETE FROM session_attribute WHERE sid == ? AND name == ?",
+        );
+        $attribute_select_statement->execute();
+        my %attribute_old_users;
+        my %deleted_users;
+        ROW:
+        while (my @row = $attribute_select_statement->fetchrow_array()) {
+            my ($sid, $name, $value) = @row;
+            my $user = exists($user_ref->{$sid})? $user_ref->{$sid} : undef;
+            if (defined($user)) {
+                my $getter
+                    = $name eq 'name'  ? 'get_display_name'
+                    : $name eq 'email' ? 'get_email'
+                    :                    undef;
+                if (!defined($getter)) {
+                    next ROW;
+                }
+                $attribute_old_users{"$sid|$name"} = 1;
+                my $new_value = $user->$getter();
+                if ($new_value && $new_value ne $value) {
+                    $RUNNER->run(
+                        "session_attribute: updating $name: $sid: $new_value",
+                        sub {return $attribute_update_statement->execute(
+                            $new_value, $sid, $name,
+                        )},
+                    );
+                }
+                elsif (!$new_value && $value) {
+                    $RUNNER->run(
+                        "session_attribute: removing $name: $sid",
+                        sub {return $attribute_delete_name_statement->execute(
+                            $sid, $name,
+                        )},
+                    );
+                }
+            }
+            elsif (!exists($deleted_users{$sid})) {
+                $deleted_users{$sid} = 1;
+                $RUNNER->run(
+                    "session_attribute: removing $sid",
+                    sub {return $attribute_delete_statement->execute($sid)},
+                );
+            }
+        }
+        for my $sid (keys(%{$user_ref})) {
+            my $user = $user_ref->{$sid};
+            ATTRIB:
+            for (
+                ['name' , $user->get_display_name()],
+                ['email', $user->get_email()       ],
+            ) {
+                my ($name, $value) = @{$_};
+                if (exists($attribute_old_users{"$sid|$name"})) {
+                    next ATTRIB;
+                }
+                if ($value) {
+                    $RUNNER->run(
+                        "session_attribute: adding $name: $sid: $value",
+                        sub {$attribute_insert_statement->execute(
+                            $sid, $name, $value,
+                        )},
+                    );
+                }
+            }
+        }
+        $attribute_select_statement->finish();
+        $attribute_insert_statement->finish();
+        $attribute_update_statement->finish();
+        $attribute_delete_statement->finish();
+    }
+    return $db_handle->disconnect();
+}
+
+# ------------------------------------------------------------------------------
+# Returns the youngest revision of a SVN repository.
+sub _svnlook_youngest {
+    my ($svn_repos_path) = @_;
+    my ($youngest) = qx{svnlook youngest $svn_repos_path};
+    chomp($youngest);
+    return $youngest;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::System
+
+=head1 SYNOPSIS
+
+    use FCM::Admin::System qw{ ... };
+    # ... see descriptions of individual functions for detail
+
+=head1 DESCRIPTION
+
+This module contains utility functions for the administration of Subversion
+repositories and Trac environments hosted by the FCM team.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item add_svn_repository($project_name)
+
+Creates a new Subversion repository.
+
+=item add_trac_environment($project_name)
+
+Creates a new Trac environment.
+
+=item backup_svn_repository(\%option,$project)
+
+Creates an archived hotcopy of $project's live SVN repository, and put it in the
+SVN backup directory. If $option{'no-verify-integrity'} does not exist, it
+verifies the integrity of the live repository before creating the hotcopy. If
+$option{'no-pack'} does not exist, it packs the live repository before creating
+the hotcopy. If $option{'no-housekeep-dumps'} does not exist, it housekeeps the
+revision dumps of $project following a successful backup.
+
+$project should be a L<FCM::Admin::Project|FCM::Admin::Project> object.
+
+=item backup_trac_environment(\%option,$project)
+
+Creates an archived hotcopy of $project's live Trac environment, and put it in
+the Trac backup directory. If $option{'no-verify-integrity'} does not exist, it
+verifies the integrity of the database of the live environment before creating
+the hotcopy.
+
+$project should be a L<FCM::Admin::Project|FCM::Admin::Project> object.
+
+=item backup_trac_files()
+
+Copies regular files immediately under the live Trac directory to the Trac
+backup directory.
+
+=item distribute_wc()
+
+Distributes the central FCM working copy to standard locations.
+
+=item filter_projects($project_list_ref,$filter_list_ref)
+
+Filters the project list in $project_list_ref using a list of names in
+$filter_list_ref. Returns a list of projects with names matching those in
+$filter_list_ref. Returns the full list if $filter_list_ref points to an empty
+list.
+
+=item get_projects_from_svn_backup()
+
+Returns a list of L<FCM::Admin::Project|FCM::Admin::Project> objects by
+searching the SVN backup directory. By default, all valid projects are returned.
+
+=item get_projects_from_svn_live()
+
+Similar to get_projects_from_svn_backup(), but it searches the SVN live
+directory.
+
+=item get_projects_from_trac_backup()
+
+Similar to get_projects_from_svn_backup(), but it searches the Trac backup
+directory.
+
+=item get_projects_from_trac_live()
+
+Similar to get_projects_from_svn_backup(), but it searches the Trac live
+directory.
+
+=item get_users(@only_users)
+
+Retrieves a list of users. Store results in a HASH, {user ID => user info, ...}
+where each user info is stored in an instance of
+L<FCM::Admin::System::User|FCM::Admin::System::User>.
+
+If no argument, return all valid users. If @only_users, return only those users
+with matching user ID in @only_users.
+
+=item housekeep_svn_hook_logs($project)
+
+Housekeep logs generated by the hook scripts of the $project's SVN live
+repository.
+
+$project should be a L<FCM::Admin::Project|FCM::Admin::Project> object.
+
+=item install_svn_hook($project, $clean_mode)
+
+Searches for hook scripts in the standard location and install them (as symbolic
+links) in the I<hooks> directory of the $project's SVN live repository.
+
+$project should be a L<FCM::Admin::Project|FCM::Admin::Project> object.
+
+If $clean_mode is specified and is true, remove any items in the I<hooks>
+directory that are not known to this install.
+
+=item manage_users_in_svn_passwd($user_ref)
+
+Using entries in the hash reference $user_ref, sets up or updates the SVN and
+Trac password files. The $user_ref argument should be a reference to a hash, as
+returned by get_users().
+
+=item manage_users_in_trac_passwd($user_ref)
+
+Using entries in the hash reference $user_ref, sets up or updates the Trac
+password files. The $user_ref argument should be a reference to a hash, as
+returned by get_users().
+
+=item manage_users_in_trac_db_of($project, $user_ref)
+
+Using entries in $user_ref, sets up or updates the session/session_attribute
+tables in the databases of the live Trac environments. The $project argument
+should be a L<FCM::Admin::Project|FCM::Admin::Project> object
+and $user_ref should be a reference to a hash, as returned by get_users().
+
+=item recover_svn_repository($project,$recover_dumps_option,$recover_hooks_option)
+
+Recovers a project's SVN repository using its backup. If $recover_dumps_option
+is set to true, it will also attempt to load the latest revision dumps following
+a successful recovery. If $recover_hooks_option is set to true, it will also
+attempt to re-install the hook scripts following a successful recovery.
+
+$project should be a L<FCM::Admin::Project|FCM::Admin::Project> object.
+
+=item recover_trac_environment($project)
+
+Recovers a project's Trac environment using its backup.
+
+$project should be a L<FCM::Admin::Project|FCM::Admin::Project> object.
+
+=item recover_trac_files()
+
+Copies files immediately under the backup Trac directory to the Trac live
+directory (if the files do not already exist).
+
+=item vacuum_trac_env_db($project)
+
+Connects to the database of a project's Trac environment, and issues the
+"VACUUM" SQL command.
+
+$project should be a L<FCM::Admin::Project|FCM::Admin::Project> object.
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM::Admin::Config|FCM::Admin::Config>,
+L<FCM::Admin::Project|FCM::Admin::Project>,
+L<FCM::Admin::Runner|FCM::Admin::Runner>,
+L<FCM::Admin::User|FCM::Admin::User>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/User.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/User.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/User.pm	(revision 5095)
@@ -0,0 +1,90 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::User;
+use base qw{FCM::Class::HASH};
+use overload q{""} => \&get_name;
+
+__PACKAGE__->class({
+    name         => '$',
+    display_name => '$',
+    email        => '$',
+});
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::User
+
+=head1 SYNOPSIS
+
+    use FCM::Admin::User;
+    $user = FCM::Admin::User->new({name => 'bob'});
+    $user->set_display_name('Robert Smith');
+    $user->set_email('robert.smith@somewhere.org');
+
+=head1 DESCRIPTION
+
+An object of this class is used to store the data model of a user.
+
+=head1 METHODS
+
+=over 4
+
+=item FCM::Admin::User->new(\%arguments)
+
+Creates a new instance. The keys of the %argument hash may contain "name",
+"display_name", and/or "email".
+
+=item $user->get_name()
+
+Returns the name/ID of the user.
+
+=item $user->get_display_name()
+
+Returns the display name of the user.
+
+=item $user->get_email()
+
+Returns the e-mail address of the user.
+
+=item $user->set_name($value)
+
+Sets the name/ID of the user.
+
+=item $user->set_display_name($value)
+
+Sets the display name of the user.
+
+=item $user->set_email($value)
+
+Sets the e-mail address of the user.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Users/LDAP.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Users/LDAP.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Users/LDAP.pm	(revision 5095)
@@ -0,0 +1,149 @@
+
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Users::LDAP;
+use base qw{FCM::Class::CODE};
+
+use FCM::Admin::Config;
+use FCM::Admin::User;
+use Net::LDAP;
+use Text::ParseWords qw{shellwords};
+
+my %ACTION_OF = (
+    get_users_info => \&_get_users_info,
+    verify_users   => \&_verify_users,
+);
+
+__PACKAGE__->class({util => '&'}, {action_of => {%ACTION_OF}});
+
+my $CONFIG = FCM::Admin::Config->instance();
+
+# Gets a HASH of users via LDAP.
+# %user_of = ($name => <FCM::Admin::User instance>, ...)
+sub _get_users_info {
+    my ($attrib_ref, @only_users) = @_;
+    my $res = _ldap_search($attrib_ref, undef, @only_users);
+    my ($uid_attr, $cn_attr, $mail_attr)
+        = shellwords($CONFIG->get_ldap_attrs());
+    my %user_of;
+    for my $entry ($res->entries()) {
+        my $name = $entry->get_value($uid_attr);
+        my $display_name = $entry->get_value($cn_attr);
+        my $email = $entry->get_value($mail_attr);
+        if ($display_name && $email) {
+            $user_of{$name} = FCM::Admin::User->new({
+                name         => $name,
+                display_name => $display_name,
+                email        => $email,
+            });
+        }
+    }
+    return \%user_of;
+}
+
+# Return a list of bad users.
+sub _verify_users {
+    my ($attrib_ref, @users) = @_;
+    my $res = _ldap_search($attrib_ref, 0, @users); # 0 == $uid_attr
+    my ($uid_attr, $cn_attr, $mail_attr)
+        = shellwords($CONFIG->get_ldap_attrs());
+    my %bad_users = map {($_ => 1)} @users;
+    for my $entry ($res->entries()) {
+        my $name = $entry->get_value($uid_attr);
+        if (exists($bad_users{$name})) {
+            delete($bad_users{$name});
+        }
+    }
+    return sort(keys(%bad_users));
+}
+
+# Bind to the LDAP server. Return a Net::LDAP instance.
+sub _ldap_search {
+    my ($attrib_ref, $attr_index, @users) = @_;
+
+    my $ldap_uri = $CONFIG->get_ldap_uri();
+    my $ldap = Net::LDAP->new($CONFIG->get_ldap_uri());
+    my $password_file = $CONFIG->get_ldappw();
+    $password_file = $attrib_ref->{util}->file_tilde_expand($password_file);
+    my $password = $password_file
+        ? $attrib_ref->{util}->file_load($password_file)
+        : undef;
+    my %ldap_options = $password ? (password => $password) : ();
+    $ldap->bind($CONFIG->get_ldap_binddn(), %ldap_options);
+
+    my @attrs = shellwords($CONFIG->get_ldap_attrs());
+    my ($uid_attr) = @attrs;
+    my $filter = @users
+        ? "(|($uid_attr=" . join(")($uid_attr=", @users) . '))'
+        : "(&($uid_attr=*))";
+    my $ldap_filter_more = $CONFIG->get_ldap_filter_more();
+    if ($ldap_filter_more) {
+        $filter = '(&' . $filter . $ldap_filter_more . ')';
+    }
+    my $res = $ldap->search(
+        base   => $CONFIG->get_ldap_basedn(),
+        filter => $filter,
+        attrs  => [$attr_index ? ($attrs[$attr_index]) : @attrs],
+    );
+    $ldap->unbind();
+    return $res;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::Users::LDAP
+
+=head1 SYNOPSIS
+
+    use FCM::Admin::Users::LDAP;
+    my $users_info_util = FCM::Admin::Users::LDAP->new();
+    $users_info_util->get_users();
+
+=head1 DESCRIPTION
+
+Utility for obtaining user information via LDAP.
+
+=head1 METHODS
+
+=over 4
+
+=item $util->get_users_info()
+
+Return a HASH (in list context) or a reference to a HASH (in scalar context)
+{name => <FCM::Admin::User instance>, ...}. The HASH should contain all entries
+in the passwd database that appear to be real users.
+
+=item $util->verify_users(@users)
+
+Return a list of bad users in @users.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Users/Passwd.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Users/Passwd.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Users/Passwd.pm	(revision 5095)
@@ -0,0 +1,145 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Users::Passwd;
+use base qw{FCM::Class::CODE};
+
+use FCM::Admin::Config;
+use FCM::Admin::User;
+use Text::ParseWords qw{shellwords};
+
+my %ACTION_OF = (
+    get_users_info => \&_get_users_info,
+    verify_users   => \&_verify_users,
+);
+
+__PACKAGE__->class({}, {action_of => {%ACTION_OF}});
+
+my $CONFIG = FCM::Admin::Config->instance();
+
+# Gets a HASH of users using the POSIX password DB.
+# %user_of = ($name => <FCM::Admin::User instance>, ...)
+sub _get_users_info {
+    my ($attrib_ref, @only_users) = @_;
+    if (@only_users) {
+        return _get_only_users_info($attrib_ref, @only_users);
+    }
+    my $gid_max = $CONFIG->get_passwd_gid_max();
+    my $uid_max = $CONFIG->get_passwd_uid_max();
+    my $gid_min = $CONFIG->get_passwd_gid_min();
+    my $uid_min = $CONFIG->get_passwd_uid_min();
+    my %user_of;
+    USER:
+    while (my ($name, $uid, $gid, $gecos) = (getpwent())[0, 2, 3, 6]) {
+        if (    exists($user_of{$name})
+            ||  defined($uid_max) && $uid > $uid_max || $uid < $uid_min
+            ||  defined($gid_max) && $gid > $gid_max || $gid < $gid_min
+            ||  !$gecos
+            ||  (@only_users && grep {$_ eq $name} @only_users)
+        ) {
+            next USER;
+        }
+        my $email = _guess_email_from_gecos($attrib_ref, $gecos);
+        if (!$email) {
+            next USER;
+        }
+        $user_of{$name} = FCM::Admin::User->new({
+            name         => $name,
+            display_name => (split(q{,}, $gecos))[0],
+            email        => $email,
+        });
+    }
+    endpwent();
+    return \%user_of;
+}
+
+# Gets a HASH of users matching @only_users using the POSIX password DB.
+# %user_of = ($name => <FCM::Admin::User instance>, ...)
+sub _get_only_users_info {
+    my ($attrib_ref, @only_users) = @_;
+    my %user_of;
+    for my $user (@only_users) {
+        my ($name, $gecos) = (getpwnam($user))[0, 6];
+        if ($name && $gecos) {
+            $user_of{$name} = FCM::Admin::User->new({
+                name         => $name,
+                display_name => (split(q{,}, $gecos))[0],
+                email        => _guess_email_from_gecos($attrib_ref, $gecos),
+            });
+        }
+    }
+    return (wantarray() ? %user_of : \%user_of);
+}
+
+# Guess a user's email from gecos information
+sub _guess_email_from_gecos {
+    my ($attrib_ref, $gecos) = @_;
+    my $domain = $CONFIG->get_passwd_email_domain();
+    my $name = index($gecos, q{,}) > 0 ? (split(q{,}, $gecos))[0] : $gecos;
+    return ($name =~ qr{\s}msx)
+        ? undef : lc($name) . ($domain ? '@' . $domain : q{});
+}
+
+# Return a list of bad users in @users.
+sub _verify_users {
+    my ($attrib_ref, @users) = @_;
+    grep {!getpwnam($_)} @users;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::Users::Passwd
+
+=head1 SYNOPSIS
+
+    use FCM::Admin::Users::Passwd;
+    my $users_info_util = FCM::Admin::Users::Passwd->new();
+    $users_info_util->get_users();
+
+=head1 DESCRIPTION
+
+Utility for obtaining user information from passwd information.
+
+=head1 METHODS
+
+=over 4
+
+=item $util->get_users_info()
+
+Return a HASH (in list context) or a reference to a HASH (in scalar context)
+{name => <FCM::Admin::User instance>, ...}. The HASH should contain all entries
+in the passwd database that appear to be real users.
+
+=item $util->verify_users(@users)
+
+Return a list of bad users in @users.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Util.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Util.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Admin/Util.pm	(revision 5095)
@@ -0,0 +1,385 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Util;
+
+use Exporter qw{import};
+use FCM::Admin::Config;
+use FCM::Admin::Runner;
+use File::Basename qw{dirname};
+use File::Copy qw{copy};
+use File::Path qw{mkpath rmtree};
+use IO::File;
+use SVN::Client;
+
+our @EXPORT_OK = qw{
+    option2config
+    read_file
+    run_copy
+    run_create_archive
+    run_extract_archive
+    run_mkpath
+    run_rename
+    run_rmtree
+    run_rsync
+    run_svn_info
+    run_svn_update
+    run_symlink
+    sed_file
+    write_file
+};
+
+my @HTML2PS = qw{html2ps -n -U -W b};
+my @PS2PDF  = qw{
+    ps2pdf
+    -dMaxSubsetPct=100
+    -dCompatibilityLevel=1.3
+    -dSubsetFonts=true
+    -dEmbedAllFonts=true
+    -dAutoFilterColorImages=false
+    -dAutoFilterGrayImages=false
+    -dColorImageFilter=/FlateEncode
+    -dGrayImageFilter=/FlateEncode
+    -dMonoImageFilter=/FlateEncode
+    -sPAPERSIZE=a4
+};
+
+# ------------------------------------------------------------------------------
+# Loads values of an option hash into the configuration.
+sub option2config {
+    my ($option_ref) = @_;
+    my $config = FCM::Admin::Config->instance();
+    for my $key (keys(%{$option_ref})) {
+        my $method = $key;
+        $method =~ s{-}{_}gxms;
+        $method = "set_$method";
+        if ($config->can($method)) {
+            $config->$method($option_ref->{$key});
+        }
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Reads lines from a file.
+sub read_file {
+    my ($path, $sub_ref) = @_;
+    my $file = IO::File->new($path);
+    if (!defined($file)) {
+        die("$path: cannot open for reading ($!).\n");
+    }
+    while (my $line = $file->getline()) {
+        $sub_ref->($line);
+    }
+    $file->close() || die("$path: cannot close for reading ($!).\n");
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Runs copy with checks and diagnostics.
+sub run_copy {
+    my ($source_path, $dest_path) = @_;
+    FCM::Admin::Runner->instance()->run(
+        "copy $source_path to $dest_path",
+        sub {
+            my $mode = (stat($source_path))[2];
+            my $rc = copy($source_path, $dest_path) && chmod($mode, $dest_path);
+            if (!$rc) {
+                die($!);
+            }
+            return $rc;
+        },
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Creates a TAR-GZIP archive.
+sub run_create_archive {
+    my ($archive_path, $work_dir, @base_names) = @_;
+    FCM::Admin::Runner->instance()->run(
+        "creating archive $archive_path",
+        sub {
+            my $command
+                = qq{tar -c -z -C '$work_dir' -f -}
+                . q{ } . join(q{ }, map {qq{'$_'}} @base_names)
+                . qq{ | dd 'conv=fsync' 'of=$archive_path'};
+            return !system($command);
+            # Note: can use Archive::Tar, but "tar" is much faster.
+        },
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Extracts from a TAR-GZIP archive.
+sub run_extract_archive {
+    my ($archive_path, $work_dir) = @_;
+    FCM::Admin::Runner->instance()->run(
+        "extracting archive $archive_path",
+        sub {
+            return !system(
+                qw{tar -x -z},
+                q{-C} => $work_dir,
+                q{-f} => $archive_path,
+            );
+            # Note: can use Archive::Tar, but "tar" is much faster.
+        },
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Runs mkpath with checks and diagnostics.
+sub run_mkpath {
+    my ($path) = @_;
+    if (!-d $path) {
+        FCM::Admin::Runner->instance()->run(
+            "creating $path",
+            sub {return mkpath($path)},
+        );
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Runs rename with checks and diagnostics.
+sub run_rename {
+    my ($source_path, $dest_path) = @_;
+    FCM::Admin::Runner->instance()->run(
+        "renaming $source_path to $dest_path",
+        sub {
+            run_mkpath(dirname($dest_path));
+            my $rc = rename($source_path, $dest_path);
+            if (!$rc) {
+                die($!);
+            }
+            return $rc;
+        },
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Runs rmtree with checks and diagnostics.
+sub run_rmtree {
+    my ($path) = @_;
+    if (-e $path) {
+        FCM::Admin::Runner->instance()->run(
+            "removing $path",
+            sub {
+                rmtree($path);
+                return !-e $path;
+            },
+        );
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Runs rsync.
+sub run_rsync {
+    my ($sources_ref, $dest_path, $option_list_ref) = @_;
+    FCM::Admin::Runner->instance()->run(
+        sprintf('mirroring %s <- %s', $dest_path, join(q{ }, @{$sources_ref})),
+        sub {return !system(
+            q{rsync},
+            ($option_list_ref ? @{$option_list_ref} : ()),
+            @{$sources_ref},
+            $dest_path,
+        )},
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Runs "svn info".
+sub run_svn_info {
+    my ($path) = @_;
+    my $return;
+    my $ctx = SVN::Client->new();
+    $ctx->info($path, undef, 'WORKING', sub {$return = $_[1]}, 0);
+    return $return;
+}
+
+# ------------------------------------------------------------------------------
+# Runs "svn update".
+sub run_svn_update {
+    my ($path) = @_;
+    my @return;
+    my $ctx = SVN::Client->new(
+        notify => sub {
+            if ($path ne $_[0]) {
+                push(@return, $_[0]);
+            }
+        }
+    );
+    $ctx->update($path, 'HEAD', 1);
+    return @return;
+}
+
+# ------------------------------------------------------------------------------
+# Runs symlink with checks and diagnostics.
+sub run_symlink {
+    my ($source_path, $dest_path) = @_;
+    FCM::Admin::Runner->instance()->run(
+        "creating symlink: $source_path -> $dest_path",
+        sub {
+            my $rc = symlink($source_path, $dest_path);
+            if (!$rc) {
+                die($!);
+            }
+            return $rc;
+        },
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Edits content of a file.
+sub sed_file {
+    my ($path, $sub_ref) = @_;
+    my @lines;
+    read_file(
+        $path,
+        sub {
+            my ($line) = @_;
+            $line = $sub_ref->($line);
+            push(@lines, $line);
+        },
+    );
+    write_file($path, @lines);
+}
+
+# ------------------------------------------------------------------------------
+# Writes content to a file.
+sub write_file {
+    my ($path, @contents) = @_;
+    mkpath(dirname($path));
+    my $file = IO::File->new($path, q{w});
+    if (!defined($file)) {
+        die("$path: cannot open for writing ($!).\n");
+    }
+    for my $content (@contents) {
+        $file->print($content);
+    }
+    $file->close() || die("$path: cannot close for writing ($!).\n");
+    return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Admin::Util
+
+=head1 SYNOPSIS
+
+    use FCM::Admin::Util qw{ ... };
+    # ... see descriptions of individual functions for detail
+
+=head1 DESCRIPTION
+
+This module contains utility functions for the administration of Subversion
+repositories and Trac environments hosted by the FCM team.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item option2config($option_ref)
+
+Loads the values of an option hash into
+L<FCM::Admin::Config|FCM::Admin::Config>.
+
+=item read_file($path,$sub_ref)
+
+Reads from $path. For each $line the file, calls $sub_ref->($line).
+
+=item run_copy($source_path,$dest_path)
+
+Copies $source_path to $dest_path, with diagnostic.
+
+=item run_create_archive($archive_path,$work_dir,@base_names)
+
+Creates a TAR-GZIP archive at $archive_path using $work_dir as the working
+directory and @base_names as members of the archive. Depends on GNU "tar" or
+compatible.
+
+=item run_extract_archive($archive_path,$work_dir)
+
+Extracts a TAR-GZIP archive at $archive_path using $work_dir as the working
+directory. Depends on GNU "tar" or compatible.
+
+=item run_mkpath($path)
+
+Creates $path if it does not already exist, with diagnostic.
+
+=item run_rename($source_path,$dest_path)
+
+Same as the core I<rename>, but with diagnostic.
+
+=item run_rmtree($path)
+
+Removes $path, with diagnostic.
+
+=item run_rsync(\@sources,$dest_path,$option_list_ref)
+
+Invokes the "rsync" shell command with diagnostics to mirror the paths in
+@sources to $dest_path. Command line options can be specified in a list with
+$option_list_ref. Depends on "rsync".
+
+=item run_svn_info($path)
+
+Wrapper of the info() method of L<SVN::Client|SVN::Client>. Expects $path to be
+a Subversion working copy. Returns the C<svn_info_t> object as described by the
+info() method of L<SVN::Client|SVN::Client>.
+
+=item run_svn_update($path)
+
+Wrapper of the update() method of L<SVN::Client|SVN::Client>. Expects $path to be
+a Subversion working copy. Returns a list of updated paths.
+
+=item run_symlink($source_path,$dest_path)
+
+Same as the core I<symlink>, but with diagnostic.
+
+=item sed_file($path,$sub_ref)
+
+For each $line in $path, runs $line = $sub_ref->($line). Writes results back to
+$path.
+
+=item write_file($path,$content)
+
+Writes $content to $path.
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM::Admin::Config|FCM::Admin::Config>,
+L<FCM::Admin::Runner|FCM::Admin::Runner>,
+L<SVN::Client|SVN::Client>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI.pm	(revision 5095)
@@ -0,0 +1,295 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::CLI;
+use base qw{FCM::Class::CODE};
+
+use FCM::CLI::Exception;
+use FCM::CLI::Parser;
+use FCM::Context::Event;
+use FCM::System;
+use FindBin;
+use File::Basename        qw{dirname};
+use File::Spec::Functions qw{catfile rel2abs};
+use Pod::Usage            qw{pod2usage};
+
+my $E = 'FCM::CLI::Exception';
+our $EVENT;
+our $S;
+our %ACTION_OF = (
+    # Commands handled by FCM
+    'add'           => _opt_func('check', sub {$S->cm_check_unknown(@_)}),
+    'branch'        => \&_branch,
+    'branch-create' => _sys_func(sub {$S->cm_branch_create(@_)}),
+    'branch-delete' => _sys_func(sub {$S->cm_branch_delete(@_)}),
+    'branch-diff'   => _sys_func(sub {$S->cm_branch_diff(@_)}),
+    'branch-info'   => _sys_func(sub {$S->cm_branch_info(@_)}),
+    'branch-list'   => _sys_func(sub {$S->cm_branch_list(@_)}),
+    'browse'        => _sys_func(sub {$S->browse(@_)}),
+    'build'         => _sys_func(sub {$S->build(@_)}),
+    'cfg-print'     => _sys_func(sub {$S->config_parse(@_)}),
+    'checkout'      => _sys_func(sub {$S->cm_checkout(@_)}),
+    'cmp-ext-cfg'   => _sys_func(sub {$S->config_compare(@_)}),
+    'commit'        => _sys_func(sub {$S->cm_commit(@_)}),
+    'conflicts'     => _sys_func(sub {$S->cm_resolve_conflicts(@_)}),
+    'delete'        => _opt_func('check', sub {$S->cm_check_missing(@_)}),
+    'diff'          => _opt_func(
+        'branch', sub {$S->cm_branch_diff(@_)}, sub {$S->cm_diff(@_)},
+    ),
+    'export-items'  => _sys_func(sub {$S->export_items(@_)}),
+    'extract'       => _sys_func(sub {$S->extract(@_)}),
+    'gui'           => \&_gui,
+    'help'          => \&_help,
+    'keyword-print' => _sys_func(sub {$S->keyword_find(@_)}),
+    'loc-layout'    => _sys_func(sub {$S->cm_loc_layout(@_)}),
+    'merge'         => _sys_func(sub {$S->cm_merge(@_)}),
+    'mkpatch'       => _sys_func(sub {$S->cm_mkpatch(@_)}),
+    'make'          => _sys_func(sub {$S->make(@_)}),
+    'project-create'=> _sys_func(sub {$S->cm_project_create(@_)}),
+    'switch'        => _opt_func(
+        'relocate', sub {$S->svn(@_)}, sub {$S->cm_switch(@_)},
+    ),
+    'test-battery'  => \&_test_battery,
+    'update'        => _sys_func(sub {$S->cm_update(@_)}),
+    'version'       => \&_version,
+    # Commands passed directly to "svn"
+    map {($_ => _sys_func())} qw{
+        blame
+        cat
+        cleanup
+        copy
+        export
+        import
+        info
+        list
+        lock
+        log
+        mergeinfo
+        mkdir
+        move
+        patch
+        propdel
+        propedit
+        propget
+        proplist
+        propset
+        relocate
+        resolve
+        resolved
+        revert
+        status
+        unlock
+        upgrade
+    },
+);
+# List of overridden subcommands that need to display "svn help"
+our %CLI_MORE_HELP_FOR = map {($_, 1)} (qw{add delete diff switch update});
+
+# Creates the class.
+__PACKAGE__->class(
+    {'gui' => '$', 'parser' => 'FCM::CLI::Parser', 'system' => 'FCM::System'},
+    {   init => sub {
+            my $attrib_ref = shift();
+            $attrib_ref->{parser} ||= FCM::CLI::Parser->new();
+            $attrib_ref->{system}
+                ||= FCM::System->new({'gui' => $attrib_ref->{'gui'}});
+        },
+        action_of => {main => \&_main},
+    },
+);
+
+# The main CLI action.
+sub _main {
+    my ($attrib_ref, @argv) = @_;
+    local($EVENT) = sub {$attrib_ref->{system}->util()->event(@_)};
+    my ($app, $option_ref, @args) = eval {$attrib_ref->{parser}->parse(@argv)};
+    if (my $e = $@) {
+        _err($attrib_ref, \@argv, $e);
+    }
+    if (!$app || $option_ref->{help}) {
+        return _help($attrib_ref, $app);
+    }
+    $option_ref ||= {};
+    my $q = $option_ref->{quiet}   || 0;
+    my $v = $option_ref->{verbose} || 0;
+    my $reporter = $attrib_ref->{system}->util()->util_of_report();
+    my $verbosity = $reporter->DEFAULT + $v - $q;
+    if (exists($ENV{FCM_DEBUG}) && $ENV{FCM_DEBUG} eq 'true') {
+        $verbosity = $reporter->DEBUG;
+    }
+    $reporter->get_ctx_of_stderr()->set_verbosity($verbosity);
+    $reporter->get_ctx_of_stdout()->set_verbosity($verbosity);
+    my @context = eval {
+        if (!exists($ACTION_OF{$app})) {
+            return $E->throw($E->APP, \@argv);
+        }
+        $ACTION_OF{$app}->($attrib_ref, $app, $option_ref, @args);
+    };
+    if (my $e = $@) {
+        return _err($attrib_ref, \@argv, $e);
+    }
+}
+
+# "fcm branch".
+sub _branch {
+    my ($attrib_ref, $app, $option_ref, @args) = @_;
+    my $method
+        = exists($option_ref->{create}) ? 'cm_branch_create'
+        : exists($option_ref->{delete}) ? 'cm_branch_delete'
+        : exists($option_ref->{list})   ? 'cm_branch_list'
+        :                                 'cm_branch_info'
+        ;
+    if ($option_ref->{create}) {
+        if (!$option_ref->{name}) {
+            return $E->throw($E->OPT, [$app, @args]);
+        }
+        my $name = delete($option_ref->{name});
+        unshift(@args, $name);
+    }
+    $attrib_ref->{system}->($method, $option_ref, @args);
+}
+
+# Handles FCM::Exception.
+sub _err {
+    my ($attrib_ref, $argv_ref, $e) = @_;
+    $EVENT->(FCM::Context::Event->E, $e) || die($e);
+    die("\n");
+}
+
+# "fcm gui".
+sub _gui {
+    my ($attrib_ref, $app, $option_ref, @args) = @_;
+    exec("$FindBin::Bin/fcm_gui", @args);
+}
+
+# Implements "fcm help" and usage.
+sub _help {
+    my ($attrib_ref, $app, $option_ref, @args) = @_;
+    $app ||= 'help';
+    my @keys = ($app eq 'help' && @args) ? @args : (q{});
+    for my $key (@keys) {
+        if (exists($FCM::CLI::Parser::PREF_NAME_OF{$key})) {
+            $key = $FCM::CLI::Parser::PREF_NAME_OF{$key};
+        }
+        my $pod
+            = $key ? catfile(dirname($INC{'FCM/CLI.pm'}), 'CLI', "fcm-$key.pod")
+            :        $0
+            ;
+        if ($pod eq $0) {
+            _version($attrib_ref, $app, $option_ref, @args);
+        }
+        my $has_pod = -f $pod;
+        if ($has_pod) {
+            my $reporter = $attrib_ref->{system}->util()->util_of_report();
+            my $verbosity = $reporter->get_ctx_of_stdout()->get_verbosity();
+            pod2usage({
+                '-exitval' => 'NOEXIT',
+                '-input'   => $pod,
+                '-verbose' => $verbosity,
+            });
+        }
+        if (!$has_pod || exists($CLI_MORE_HELP_FOR{$key})) {
+            $attrib_ref->{system}->svn('help', {}, $key ? $key : ())
+        }
+    }
+    return;
+}
+
+# "fcm test-battery".
+sub _test_battery {
+    my ($attrib_ref, $app, $option_ref, @args) = @_;
+    exec("$FindBin::Bin/fcm_test_battery", @args);
+}
+
+# Returns a function that select the alternate handler for the application. The
+# handler is either $method_id (if $opt_id is set) or "svn".
+sub _opt_func {
+    my ($opt_id, $code0_ref, $code1_ref) = @_;
+    $code0_ref = _sys_func($code0_ref);
+    $code1_ref = _sys_func($code1_ref);
+    sub {
+        my ($attrib_ref, $app, $option_ref, @args) = @_;
+        my $code_ref = exists($option_ref->{$opt_id}) ? $code0_ref : $code1_ref;
+        $code_ref->($attrib_ref, $app, $option_ref, @args);
+    };
+}
+
+# Invokes a system function.
+sub _sys_func {
+    my ($code_ref) = @_;
+    sub {
+        my ($attrib_ref, $app, @args) = @_;
+        local($S) = $attrib_ref->{system};
+        defined($code_ref) ? $code_ref->(@args) : $S->svn($app, @args);
+    };
+}
+
+# Implements "fcm version".
+sub _version {
+    my ($attrib_ref, $app, $option_ref, @args) = @_;
+    my $version = $attrib_ref->{system}->util()->version();
+    $EVENT->(FCM::Context::Event->OUT, "FCM $version\n");
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::CLI
+
+=head1 SYNOPSIS
+
+    my $cli = FCM::CLI->new();
+    $cli->(@ARGV);
+
+=head1 DESCRIPTION
+
+An implementation of the FCM command line interface.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new()
+
+Returns a new instance.
+
+=item $cli->(@ARGV)
+
+Determines the application using the first element in @ARGV, parses the options
+and arguments according to the application, and invokes the application.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=head2 FCM::CLI::Exception
+
+This exception is thrown when the CLI fails to invoke an application.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/Exception.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/Exception.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/Exception.pm	(revision 5095)
@@ -0,0 +1,57 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::CLI::Exception;
+use base qw{FCM::Exception};
+
+use constant {
+    APP => 'APP',
+    OPT => 'OPT',
+};
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::CLI::Exception
+
+=head1 SYNOPSIS
+
+    use FCM::CLI::Exception;
+    FCM::CLI::Exception->throw(FCM::CLI::Exception->APP, \@argv, $e);
+    FCM::CLI::Exception->throw(FCM::CLI::Exception->OPT, \@argv, $e);
+
+=head1 DESCRIPTION
+
+An exception associated with the FCM CLI. It is a sub-class of
+L<FCM::Exception|FCM::Exception>. The $e->get_ctx() method returns an ARRAY
+reference containing the argument list. The $e->get_code() method may return
+either $e->APP (if an unknown application is specified) or $e->OPT (if an
+unknown option is specified, i.e. the option parser returns some errors).
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/Parser.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/Parser.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/Parser.pm	(revision 5095)
@@ -0,0 +1,463 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::CLI::Parser;
+use base qw{FCM::Class::CODE};
+
+use FCM::CLI::Exception;
+use Getopt::Long qw{GetOptions :config bundling};
+
+use constant {
+    OPT_INCR => q{+},   # no argument, but incremental
+    OPT_BOOL => q{},    # no argument
+    OPT_SCAL => q{=s},  # single argument
+    OPT_LIST => q{=s@}, # multiple argument
+};
+
+# Option hash, key = preferred name of option, value = HASH reference where:
+# arg     => argument flag
+# letters => ARRAY reference of a list of option letters
+# names   => ARRAY reference of a list of names
+our %OPTION_OF = map {
+    ($_->[0][0], {arg => $_->[2], letters => $_->[1], names => $_->[0]});
+} (
+    [['archive'            ,            ], ['a'], OPT_BOOL],
+    [['auto-log'           ,            ], [   ], OPT_BOOL],
+    [['branch'             ,            ], ['b'], OPT_BOOL],
+    [['branch-of-branch'   , 'bob'      ], [   ], OPT_BOOL],
+    [['browser'            ,            ], ['b'], OPT_SCAL],
+    [['check'              ,            ], ['c'], OPT_BOOL],
+    [['clean'              ,            ], [   ], OPT_BOOL],
+    [['create'             ,            ], ['c'], OPT_BOOL],
+    [['config-file'        , 'file'     ], ['f'], OPT_LIST],
+    [['config-file-path'   ,            ], ['F'], OPT_LIST],
+    [['custom'             ,            ], [   ], OPT_BOOL],
+    [['delete'             ,            ], ['d'], OPT_BOOL],
+    [['diff-cmd'           ,            ], [   ], OPT_SCAL],
+    [['directory'          ,            ], ['C'], OPT_SCAL],
+    [['dry-run'            ,            ], [   ], OPT_BOOL],
+    [['exclude'            ,            ], [   ], OPT_LIST],
+    [['extensions'         ,            ], ['x'], OPT_SCAL],
+    [['graphical'          ,            ], ['g'], OPT_BOOL],
+    [['fcm1'               ,            ], ['1'], OPT_BOOL],
+    [['full'               ,            ], ['f'], OPT_BOOL],
+    [['help'               , 'usage'    ], ['h'], OPT_BOOL],
+    [['ignore-lock'        ,            ], [   ], OPT_BOOL],
+    [['info'               ,            ], ['i'], OPT_BOOL],
+    [['jobs'               ,            ], ['j'], OPT_SCAL],
+    [['list'               ,            ], ['l'], OPT_BOOL],
+    [['name'               ,            ], ['n'], OPT_SCAL],
+    [['new'                ,            ], ['N'], OPT_BOOL],
+    [['non-interactive'    ,            ], [   ], OPT_BOOL],
+    [['only'               ,            ], [   ], OPT_LIST],
+    [['organisation'       ,            ], [   ], OPT_SCAL],
+    [['password'           ,            ], [   ], OPT_SCAL],
+    [['quiet'              ,            ], ['q'], OPT_INCR],
+    [['relocate'           ,            ], [   ], OPT_BOOL],
+    [['reverse'            ,            ], [   ], OPT_BOOL],
+    [['revision'           ,            ], ['r'], OPT_SCAL],
+    [['rev-flag'           ,            ], [   ], OPT_SCAL],
+    [['show-all'           ,            ], ['a'], OPT_BOOL],
+    [['show-children'      ,            ], [   ], OPT_BOOL],
+    [['show-other'         ,            ], [   ], OPT_BOOL],
+    [['show-siblings'      ,            ], [   ], OPT_BOOL],
+    [['stage'              ,            ], ['s'], OPT_SCAL],
+    [['summarize'          , 'summarise'], [   ], OPT_BOOL],
+    [['svn-non-interactive',            ], [   ], OPT_BOOL],
+    [['switch'             ,            ], ['s'], OPT_BOOL],
+    [['targets'            ,            ], ['t'], OPT_LIST],
+    [['ticket'             ,            ], ['k'], OPT_LIST],
+    [['trac'               ,            ], ['t'], OPT_BOOL],
+    [['type'               ,            ], ['t'], OPT_SCAL],
+    [['url'                ,            ], [   ], OPT_BOOL],
+    [['user'               ,            ], ['u'], OPT_LIST],
+    [['verbose'            ,            ], ['v'], OPT_INCR],
+    [['verbosity'          ,            ], ['v'], OPT_SCAL],
+    [['wiki'               ,            ], ['w'], OPT_BOOL],
+    [['wiki-format'        , 'wiki'     ], ['w'], OPT_SCAL],
+    [['xml'                ,            ], [   ], OPT_BOOL],
+);
+# Hook command before parsing the options
+our %HOOK_BEFORE_FOR = (
+    'add'    => _get_code_to_match($OPTION_OF{check}),
+    'delete' => _get_code_to_match($OPTION_OF{check}),
+    'diff'   => sub {
+        _get_code_to_replace(
+            $OPTION_OF{graphical}, [qw{
+                --config-option config:working-copy:exclusive-locking-clients=
+                --diff-cmd fcm_graphic_diff
+            }]
+        )->(@_);
+        _get_code_to_replace($OPTION_OF{summarize}, ['--summarize'])->(@_);
+        _get_code_to_match($OPTION_OF{branch})->(@_);
+    },
+    'switch' => sub {!_get_code_to_match($OPTION_OF{relocate})->(@_)},
+);
+our $HELP_APP = 'help';
+# Options for known applications
+our %OPTIONS_FOR = (
+    'add'           => [$OPTION_OF{check}],
+    'branch'        => [@OPTION_OF{
+        qw{ branch-of-branch create delete info list name non-interactive
+            password quiet revision rev-flag show-all show-children
+            show-siblings svn-non-interactive ticket type user verbose
+        }
+    }],
+    'branch-create' => [@OPTION_OF{
+        qw{ branch-of-branch non-interactive password rev-flag
+            svn-non-interactive switch ticket type
+        }
+    }],
+    'branch-delete' => [@OPTION_OF{
+        qw{ non-interactive password quiet show-all show-children show-siblings
+            svn-non-interactive switch verbose
+        }
+    }],
+    'branch-diff'   => [@OPTION_OF{
+        qw{diff-cmd graphical extensions summarize trac wiki xml}
+    }],
+    'branch-info'   => [@OPTION_OF{
+        qw{quiet show-all show-children show-siblings verbose}
+    }],
+    'branch-list'   => [@OPTION_OF{
+        qw{only quiet show-all url user verbose}
+    }],
+    'browse'        => [$OPTION_OF{browser}],
+    'build'         => [@OPTION_OF{
+        qw{archive clean full ignore-lock jobs stage targets verbosity}
+    }],
+    'cfg-print'     => [$OPTION_OF{fcm1}],
+    'cmp-ext-cfg'   => [@OPTION_OF{qw{quiet verbose wiki-format}}],
+    'commit'        => [@OPTION_OF{
+        qw{dry-run password svn-non-interactive}
+    }],
+    'conflicts'     => [],
+    'delete'        => [$OPTION_OF{check}],
+    'diff'          => [@OPTION_OF{
+        qw{branch diff-cmd extensions summarize trac wiki}
+    }],
+    'export-items'  => [@OPTION_OF{qw{directory config-file new}}],
+    'extract'       => [@OPTION_OF{qw{clean full ignore-lock verbosity}}],
+    'gui'           => [],
+    $HELP_APP       => [@OPTION_OF{qw{quiet verbose}}],
+    'keyword-print' => [@OPTION_OF{qw{verbose}}],
+    'loc-layout'    => [@OPTION_OF{qw{verbose}}],
+    'make'          => [@OPTION_OF{
+        qw{ archive directory ignore-lock jobs config-file config-file-path name
+            new quiet verbose
+        }
+    }],
+    'merge'         => [@OPTION_OF{
+        qw{ auto-log custom dry-run non-interactive quiet reverse revision
+            verbose}
+    }],
+    'mkpatch'       => [@OPTION_OF{qw{exclude organisation revision}}],
+    'project-create'=> [@OPTION_OF{
+        qw{non-interactive password svn-non-interactive}
+    }],
+    'switch'        => [@OPTION_OF{qw{non-interactive revision quiet verbose}}],
+    'update'        => [@OPTION_OF{qw{non-interactive revision quiet verbose}}],
+);
+# Preferred names of known applications with aliases
+our %PREF_NAME_OF = (
+    'ann'      => 'blame',
+    'annotate' => 'blame',
+    'bcreate'  => 'branch-create',
+    'bc'       => 'branch-create',
+    'bdel'     => 'branch-delete',
+    'bdelete'  => 'branch-delete',
+    'bdi'      => 'branch-diff',
+    'bdiff'    => 'branch-diff',
+    'binfo'    => 'branch-info',
+    'bld'      => 'build',
+    'blist'    => 'branch-list',
+    'bls'      => 'branch-list',
+    'br'       => 'branch',
+    'brm'      => 'branch-delete',
+    'cfg'      => 'cfg-print',
+    'ci'       => 'commit',
+    'cf'       => 'conflicts',
+    'co'       => 'checkout',
+    'cp'       => 'copy',
+    'del'      => 'delete',
+    'di'       => 'diff',
+    'ext'      => 'extract',
+    'h'        => $HELP_APP,
+    'kp'       => 'keyword-print',
+    'ls'       => 'list',
+    'mv'       => 'move',
+    'pd'       => 'propdel',
+    'pdel'     => 'propdel',
+    'pe'       => 'propedit',
+    'pedit'    => 'propedit',
+    'pg'       => 'propget',
+    'pget'     => 'propget',
+    'pl'       => 'proplist',
+    'plist'    => 'proplist',
+    'praise'   => 'blame',
+    'ps'       => 'propset',
+    'pset'     => 'propset',
+    'ren'      => 'move',
+    'rename'   => 'move',
+    'rm'       => 'delete',
+    'remove'   => 'delete',
+    'st'       => 'status',
+    'sw'       => 'switch',
+    'stat'     => 'status',
+    'trac'     => 'browse',
+    'up'       => 'update',
+    'usage'    => $HELP_APP,
+    'www'      => 'browse',
+    '?'        => $HELP_APP,
+    '-V'       => 'version',
+    '--help'   => $HELP_APP,
+    '--usage'  => $HELP_APP,
+    '--version'=> 'version',
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   help_app        => {isa => '$', default => $HELP_APP            },
+        help_option     => {isa => '%', default => {%{$OPTION_OF{help}}}},
+        hook_before_for => {isa => '%', default => {%HOOK_BEFORE_FOR}   },
+        options_for     => {isa => '%', default => {%OPTIONS_FOR}       },
+        pref_name_of    => {isa => '%', default => {%PREF_NAME_OF}      },
+    },
+    {action_of => {parse => \&_parse}},
+);
+
+# Parses the options and arguments.
+sub _parse {
+    my ($attrib_ref, @argv) = @_;
+    my @args = @argv;
+    my $option_hash_ref = {};
+    if (!@args) {
+        return ($attrib_ref->{help_app}, $option_hash_ref);
+    }
+    my $app = shift(@args);
+    if (exists($attrib_ref->{pref_name_of}{$app})) {
+        $app = $attrib_ref->{pref_name_of}{$app};
+    }
+    if (_get_code_to_match($attrib_ref->{help_option})->(\@args)) {
+        return ($attrib_ref->{help_app}, {}, $app);
+    }
+    if (exists($attrib_ref->{hook_before_for}{$app})) {
+        if (!$attrib_ref->{hook_before_for}{$app}->(\@args)) {
+            return ($app, $option_hash_ref, @args);
+        }
+    }
+    if (!exists($attrib_ref->{options_for}{$app})) {
+        return ($app, $option_hash_ref, @args);
+    }
+    my @option_strings = map {
+        join('|', @{$_->{names}}, @{$_->{letters}}) . $_->{arg};
+    } @{$attrib_ref->{options_for}{$app}};
+    local(@ARGV) = @args;
+    my @warnings;
+    local($SIG{__WARN__}) = sub {push(@warnings, @_)};
+    if (!GetOptions($option_hash_ref, @option_strings)) {
+        my $E = 'FCM::CLI::Exception';
+        for (@warnings) {
+            chomp();
+        }
+        return $E->throw($E->OPT, \@argv, join('|', @warnings));
+    }
+    @args = @ARGV;
+    return ($app, $option_hash_ref, @args);
+}
+
+# Returns a CODE reference for matching a simple option to a string.
+sub _get_option_matcher {
+    my ($option_ref) = @_;
+    return sub {
+        grep {$_[0] eq $_} (
+            (map {"--$_"} @{$option_ref->{names}  }),
+            (map { "-$_"} @{$option_ref->{letters}}),
+        );
+    };
+}
+
+# Returns a CODE reference for matching a simple option to a string.
+sub _get_code_to_match {
+    my ($option_ref) = @_;
+    my $grepper = _get_option_matcher($option_ref);
+    return sub {grep {$grepper->($_)} @{$_[0]}};
+}
+
+# Returns a CODE reference to replace a simple option in the argument list.
+sub _get_code_to_replace {
+    my ($option_ref, $replacement) = @_;
+    my @replacements = ref($replacement) ? @{$replacement} : $replacement;
+    my $grepper = _get_option_matcher($option_ref);
+    return sub {
+        @{$_[0]} = map {($grepper->($_) ? @replacements : $_)} @{$_[0]};
+        return 1;
+    };
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::CLI::Parser
+
+=head1 SYNOPSIS
+
+    use FCM::CLI::Parser;
+    my $cli = FCM::CLI::Parser->new(\%attrib);
+    my ($app, $opt_hash_ref, @args) = $cli->(@ARGV);
+
+=head1 DESCRIPTION
+
+This class provides an option/argument parser for the FCM command line
+interface. The parser, when called with some arguments, returns a list. The 1st
+element is the name of the application, the 2nd element is a HASH reference
+containing the option names and their values. The remaining elements are the
+remaining arguments.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. The %attrib HASH may contain the following elements:
+
+=over 4
+
+=item help_app
+
+The name of the I<help> application. Default = $FCM::CLI::Parser::HELP_APP. 
+
+=item help_option
+
+An option that represents I<help>. If this option is encountered in the command
+line, the CODE reference returns (help_app, {}, $app) regardless of the other
+command line options and arguments. Default =
+$FCM::CLI::Parser::OPTIONS_FOR{help}.
+
+=item hook_before_for
+
+Hook commands for the applications, which are executed before the option parser.
+See the L</CONFIGURATIONS> section for detail. Default =
+$FCM::CLI::Parser::HOOK_BEFORE_FOR.
+
+=item options_for
+
+The options for each application. See the L</CONFIGURATIONS> section for detail.
+Default = $FCM::CLI::Parser::OPTIONS_FOR.
+
+=item pref_name_of
+
+The preferred names for the applications. See the L</CONFIGURATIONS> section for
+detail. Default = $FCM::CLI::Parser::PREF_NAME_OF.
+
+=back
+
+=item $instance->(@args)
+
+=back
+
+=head1 CONFIGURATIONS
+
+The following should only be used as read-only variables. The
+$class->new(\%attrib) method should be used to configure a parser.
+
+=over 4
+
+=item $FCM::CLI::Parser::HELP_APP
+
+The name of the I<help> application.
+
+=item %FCM::CLI::Parser::HOOK_BEFORE_FOR
+
+A hash containing the hook commands, which are invoked before calling the option
+parser. The hash keys are names of the applications, and the values are CODE
+references to invoke. If a hook exists for an application, it is called as
+$hook->(\@args) where @args is the current command line arguments (with the
+first argument, i.e. the application name removed). If the hook returns a false
+value, the parser will return immediately.
+
+=item %FCM::CLI::Parser::OPTION_OF
+
+A hash containing the known options. The key is the preferred name of the
+option, and the value is a HASH reference, where C<names> (=> ARRAY reference)
+are the long names of the option, C<letters> (=> ARRAY reference) are the
+option letters, C<arg> (=> integer) is a flag. (See L</CONSTANTS> section for
+detail.)
+
+=item %FCM::CLI::Parser::OPTIONS_FOR
+
+A hash containing the known applications. The keys are the names of the
+applications and the values are ARRAY references, each pointing to
+a list of options (as described in %FCM::CLIParser::OPTION_OF) for the
+application.
+
+=item %FCM::CLI::Parser::PREF_NAME_OF
+
+A hash containing the preferred names of an application. The keys are the
+aliases and the values are the preferred names.
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item FCM::CLI::Parser->OPT_BOOL
+
+Option flag. Option is a boolean with no argument.
+
+=item FCM::CLI::Parser->OPT_INCR
+
+Option flag. Option has no argument but is incremental.
+
+=item FCM::CLI::Parser->OPT_LIST
+
+Option flag. Option has one or more arguments.
+
+=item FCM::CLI::Parser->OPT_SCAL
+
+Option flag. Option has a single argument.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item FCM::CLI::Parser::Exception
+
+This exception is raised if an invalid command option is given. It inherits from
+L<FCM::Exception>. There is no error code associated with this exception. The
+$e->get_ctx() method returns an ARRAY reference containing the original
+arguments.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-add.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-add.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-add.pod	(revision 5095)
@@ -0,0 +1,22 @@
+=head1 NAME
+
+fcm add
+
+=head1 SYNOPSIS
+
+    1. fcm add --check [PATH ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --check, -c
+
+Check for any files or directories reported by "L<svn|svn> status" as not under
+version control and add them.
+
+=back
+
+For other options, see output of "L<svn|svn> help add".
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-create.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-create.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-create.pod	(revision 5095)
@@ -0,0 +1,86 @@
+=head1 NAME
+
+fcm branch-create (bcreate, bc)
+
+=head1 SYNOPSIS
+
+Creates a branch.
+
+    fcm branch-create [OPTIONS] NAME [SOURCE]
+
+=head1 ARGUMENTS
+
+NAME is a short name for the branch, which should contain only characters in the
+set [A-Za-z0-9_\-\.]. If the --ticket=N option is not specified and NAME
+contains only a list of positive integers separated by [_-] (an underscore or a
+hyphen), the command will assume that NAME also specifies the related ticket
+numbers.
+
+SOURCE can be an URL or a Subversion working copy. Otherwise, the current
+working directory must be a working copy. The specified URL (or the URL of the
+working copy) must be URL under a standard FCM project.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --branch-of-branch, --bob
+
+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.
+
+=item --non-interactive
+
+Do no interactive prompting. This option implies --svn-non-interactive.
+
+=item --password=PASSWORD
+
+Specify a password for write access to the repository.
+
+=item --rev-flag=NONE|NORMAL|NUMBER
+
+Specify a flag for determining the prefix of the branch name. The flag can be
+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.
+
+=item --svn-non-interactive
+
+Do no interactive prompting at commit time. This option is implied by
+--non-interactive.
+
+=item --switch
+
+C<fcm switch> the current working directory (if it contains a relevant working
+copy) to point to the newly created branch after the branch is created.
+
+=item --ticket=N, -k N
+
+Specify one (or more) Trac ticket. If specified, the ticket numbers will be
+included in the commit log message. Multiple tickets can be set by specifying
+this option multiple times, or by specifying the tickets in a comma-separated
+list.
+
+=item --type=TYPE, -t TYPE
+
+Specify the type of the branch to be created. It must be one of the following:
+
+ DEV::USER [DEV, USER] - a development branch for the user
+ DEV::SHARE [SHARE]    - a shared development branch
+ TEST::USER [TEST]     - a test branch for the user
+ TEST::SHARE           - a shared test branch
+ PKG::USER [PKG]       - a package branch for the user
+ PKG::SHARE            - a shared package branch
+ PKG::CONFIG [CONFIG]  - a configuration branch
+ PKG::REL [REL]        - a release branch
+
+If not specified, the default is to create a development branch for the current
+user, i.e.  DEV::USER.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-delete.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-delete.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-delete.pod	(revision 5095)
@@ -0,0 +1,61 @@
+=head1 NAME
+
+fcm branch-delete (bdelete bdel brm)
+
+=head1 SYNOPSIS
+
+Deletes a branch.
+
+    fcm branch-delete [OPTIONS] [TARGET]
+
+=head1 ARGUMENTS
+
+TARGET can be an URL or a Subversion working copy. Otherwise, the current
+working directory must be a working copy. The specified URL (or the URL of the
+working copy) must be a URL under a valid branch in a standard FCM project.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --verbose, -v
+
+Print extra information.
+
+=item --show-all, -a
+
+Set --show-children, --show-other and --show-siblings.
+
+=item --show-children
+
+Report children of the current branch.
+
+=item --show-other
+
+Report custom/ reverse merges into the current branch.
+
+=item --show-siblings
+
+Report merges with siblings of the current branch.
+
+=item --non-interactive
+
+Do no interactive prompting. This option implies --svn-non-interactive.
+
+=item --password=PASSWORD
+
+Specify a password for write access to the repository.
+
+=item --svn-non-interactive
+
+Do no interactive prompting at commit time. This option is implied by
+--non-interactive.
+
+=item --switch
+
+If TARGET not specified in the argument list, C<fcm switch> the current working
+copy to point to the C<trunk> after the branch deletion.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-diff.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-diff.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-diff.pod	(revision 5095)
@@ -0,0 +1,52 @@
+=head1 NAME
+
+fcm branch-diff (bdiff, bdi)
+
+=head1 SYNOPSIS
+
+Show differences relative to the base of the target branch, i.e. the changes
+available for merging from the target branch into its parent.
+
+    fcm branch-diff [OPTIONS] [TARGET]
+
+=head1 ARGUMENTS
+
+If TARGET is specified, it must either be a URL or a working copy. Otherwise,
+the target is the current directory which must be a working copy. The target
+URL must be a branch in a standard FCM project.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --diff-cmd=PATH
+
+Use PATH as I<diff> command.
+
+=item --graphical, -g
+
+Equivalent to C<--diff-cmd=fcm_graphic_diff>.
+
+=item --summarize, --summarise
+
+Show a summary of the results.
+
+=item --xml
+
+With --summarise, show a summary of the results in XML format.
+
+=item --trac, -t
+
+If TARGET is a URL, use Trac to display the diff.
+
+=item --wiki
+
+If TARGET is a URL, print Trac link for the diff.
+
+=item --extensions=ARG, -x ARG
+
+Options of the I<diff> command. See the help for "L<svn|svn> diff".
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-info.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-info.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-info.pod	(revision 5095)
@@ -0,0 +1,43 @@
+=head1 NAME
+
+fcm branch-info (binfo)
+
+=head1 SYNOPSIS
+
+Displays information of a branch.
+
+    fcm branch-info [OPTIONS] [TARGET]
+
+=head1 ARGUMENTS
+
+TARGET can be an URL or a Subversion working copy. Otherwise, the current
+working directory must be a working copy. The specified URL (or the URL of the
+working copy) must be a URL under a valid branch in a standard FCM project.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --verbose, -v
+
+Print extra information.
+
+=item --show-all, -a
+
+Set --show-children, --show-other and --show-siblings.
+
+=item --show-children
+
+Report children of the current branch.
+
+=item --show-other
+
+Report custom/ reverse merges into the current branch.
+
+=item --show-siblings
+
+Report merges with siblings of the current branch.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-list.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-list.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch-list.pod	(revision 5095)
@@ -0,0 +1,52 @@
+=head1 NAME
+
+fcm branch-list (blist, bls)
+
+=head1 SYNOPSIS
+
+Searches and lists branches in projects. By default, it lists only branches
+created by the current user.
+
+    fcm branch-list [OPTIONS] [TARGET ...]
+
+=head1 ARGUMENTS
+
+If no TARGET is specified, the current working directory is assumed to be the
+target. Each target must either be a URL[@REV] or a PATH[@REV] to a working copy
+of a standard FCM project.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --only=DEPTH:PATTERN, ...
+
+Specify a regular expression to match at various depth. E.g.  with the normal
+FCM branch naming convention, C<--only=1:dev --only=2:fred> will display only
+the development branches owned by user ID "fred". (This option is cumalative,
+and overrides the --show-all and --user=PATTERN options.)
+
+=item --quiet, -q
+
+Decreases verbosity. Only prints branches matching the search criteria.
+
+=item --show-all, -a
+
+Print branches of all users. (This option overrides the --user=USER option.)
+
+=item --url
+
+Displays Subversion URL instead of FCM location keywords.
+
+=item --user=PATTERN, -u PATTERN
+
+Equivalent to --only=2:^PATTERN$ for projects with the normal FCM branch naming
+convention. Lists branches created by the specified list of users instead of the
+current user. With the normal FCM branch naming convention, you can also list
+shared branches by specifying the user as "Share", configuration branches by
+specifying the user as "Config" and release branches by specifying the user as
+"Rel". (This option is cumalative.)
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-branch.pod	(revision 5095)
@@ -0,0 +1,37 @@
+=head1 NAME
+
+fcm branch (br)
+
+=head1 SYNOPSIS
+
+    1. fcm branch --create --name NAME [OPTIONS] [SOURCE]
+       fcm branch -c           -n NAME [OPTIONS] [SOURCE]
+    2. fcm branch --delete             [OPTIONS] [TARGET]
+       fcm branch -d                   [OPTIONS] [TARGET]
+    3. fcm branch --info               [OPTIONS] [TARGET]
+       fcm branch                      [OPTIONS] [TARGET]
+    4. fcm branch --list               [OPTIONS] [TARGET]
+       fcm branch -l                   [OPTIONS] [TARGET]
+
+This command is deprecated. The 4 usages are replaced by:
+
+=over 4
+
+=item 1.
+
+C<fcm branch-create>: Type C<fcm help branch-create> for detail.
+
+=item 2.
+
+C<fcm branch-delete>: Type C<fcm help branch-delete> for detail.
+
+=item 3.
+
+C<fcm branch-info>: Type C<fcm help branch-info> for detail.
+
+=item 4.
+
+C<fcm branch-list>: Type C<fcm help branch-list> for detail.
+
+=back
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-browse.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-browse.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-browse.pod	(revision 5095)
@@ -0,0 +1,30 @@
+=head1 NAME
+
+fcm browse (trac www)
+
+=head1 SYNOPSIS
+
+Invokes the browser for a version controlled target.
+
+    fcm browse [OPTIONS] [TARGET ...]
+
+=head1 ARGUMENTS
+
+If TARGET 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.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --browser=COMMAND
+
+Specify the web browser command.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-build.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-build.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-build.pod	(revision 5095)
@@ -0,0 +1,91 @@
+=head1 NAME
+
+fcm build (bld)
+
+=head1 SYNOPSIS
+
+Invokes the deprecated FCM 1 build system.
+
+    fcm build [OPTIONS] [CFGFILE]
+
+=head1 ARGUMENTS
+
+The path to a CFGFILE may be provided. Otherwise, the build system searches the
+default locations for a bld cfg file.
+
+=head1 OPTIONS
+
+If no option is specified, the options "-s 5 -t all -j 1 -v 1" are assumed.
+
+=over 4
+
+=item --archive, -a
+
+Archive sub-directories on success. If archive mode is switched on, build
+sub-directories that are only used in the build process will be archived to
+TAR-GZIP files.
+
+=item --clean
+
+Clean the destination.
+
+=item --full, -f
+
+Run in full mode. 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.
+
+=item --ignore-lock
+
+Ignore lock file. 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, the 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 this option.
+
+=item --jobs=N, -j N
+
+Specify the number of parallel jobs.
+
+=item --stage=N, -s N
+
+Run command up to a named stage. The stages are:
+
+=over 4
+
+=item "1", "s" or "setup"
+
+Stage 1, setup.
+
+=item "2", "pp" or "pre_process"
+
+Stage 2, pre-process.
+
+=item "3", "gd" or "generate_dependency"
+
+Stage 3, generate dependency.
+
+=item "4", "gi" or "generate_interface"
+
+Stage 4, generate Fortran 9X interface.
+
+=item "5", "m", "make"
+
+Stage 5, make.
+
+=back
+
+=item --targets=TARGETS, -t TARGETS
+
+List of build targets, delimited by (:). If specified, the default targets
+declared in the configuration file will not be used.
+
+=item --verbose=N, -v N
+
+Specify the verbosity level. If specified, the verbosity level must be an
+integer greater than 0. Verbosity level 0 is the quiet mode. Increasing the
+verbosity level will increase the amount of diagnostic output.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-cfg-print.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-cfg-print.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-cfg-print.pod	(revision 5095)
@@ -0,0 +1,23 @@
+=head1 NAME
+
+fcm cfg-print (cfg)
+
+=head1 SYNOPSIS
+
+Parses each FCM configuration file specified in the argument list, and prints
+the result to STDOUT.
+
+    fcm cfg-print [OPTIONS] [TARGET ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --fcm1, -1
+
+If specified, targets should be in FCM 1 format. Otherwise, they should be in
+FCM 2 format.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-changelist.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-changelist.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-changelist.pod	(revision 5095)
@@ -0,0 +1,9 @@
+=head1 NAME
+
+fcm changelist
+
+=head1 SYNOPSIS
+
+    svn changelist is not supported under fcm.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-cmp-ext-cfg.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-cmp-ext-cfg.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-cmp-ext-cfg.pod	(revision 5095)
@@ -0,0 +1,30 @@
+=head1 NAME
+
+fcm cmp-ext-cfg
+
+=head1 SYNOPSIS
+
+Compares the extract configurations of two similar FCM 1 extract configuration
+files CFGFILE1 and CFGFILE2. This application is deprecated.
+
+    fcm cmp-ext-cfg [OPTIONS] CFFILE1 CFGFILE2
+
+=head1 OPTIONS
+
+=over 4
+
+=item --verbose=N, -v N
+
+Specify the verbosity level. In normal mode with verbosity level 2 or above,
+displays the change log of each revision.
+
+=item --wiki-format=TARGET
+
+Print revision tables in wiki format. In wiki mode, 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. The --verbose
+option has no effect in wiki mode.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-commit.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-commit.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-commit.pod	(revision 5095)
@@ -0,0 +1,31 @@
+=head1 NAME
+
+fcm commit (ci)
+
+=head1 SYNOPSIS
+
+Send changes from your working copy to the repository. Invoke your favourite
+editor to prompt you for a commit log message. Update your working copy
+following the commit.
+
+    fcm commit [OPTIONS] [PATH ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --dry-run
+
+Allows you to add to the commit message without committing.
+
+=item --svn-non-interactive
+
+Do no interactive prompting at commit time.
+
+=item --password=PASSWORD
+
+Specify a password ARG.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-conflicts.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-conflicts.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-conflicts.pod	(revision 5095)
@@ -0,0 +1,17 @@
+=head1 NAME
+
+fcm conflicts (cf)
+
+=head1 SYNOPSIS
+
+Use graphical tool to resolve any conflicts within your working copy.
+
+    fcm conflicts [PATH]
+
+=head1 ARGUMENTS
+
+Invoke a graphical merge tool to help you resolve conflicts in your working copy
+at PATH. It prompts you to run "L<svn|svn> resolved" each time you have resolved
+the conflicts in a text file.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-delete.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-delete.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-delete.pod	(revision 5095)
@@ -0,0 +1,22 @@
+=head1 NAME
+
+fcm delete (del, remove, rm)
+
+=head1 SYNOPSIS
+
+    fcm delete [OPTIONS] [TARGETS]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --check, -c
+
+Check for any files or directories reported by "L<svn|svn> status" as missing
+and schedule them for removal.
+
+=back
+
+For other options, see output of "L<svn|svn> help delete".
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-diff.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-diff.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-diff.pod	(revision 5095)
@@ -0,0 +1,45 @@
+=head1 NAME
+
+fcm diff (di)
+
+=head1 SYNOPSIS
+
+Display the differences between two revisions or paths.
+
+    1. fcm diff --branch [OPTIONS] [TARGET]
+       fcm diff -b                 [TARGET]
+    2. fcm diff [OPTIONS] [ARGS]
+
+=over 4
+
+=item 1.
+
+This usage is deprecated. It is replaced by C<fcm branch-diff>. Type
+C<fcm help branch-diff> for detail.
+
+=item 2.
+
+See the output of "L<svn|svn> help diff".
+
+=back
+
+=head1 OPTIONS
+
+The following are additional options supported by C<fcm diff>.
+
+=over 4
+
+=item --graphical, -g
+
+If this option is specified, the command uses a graphical tool to display the
+differences. This option can be used in combination with all other valid
+options except --diff-cmd and --extensions.
+
+=item --summarise
+
+This option is implemented in FCM as a wrapper to the Subversion --summarize
+option. It prints only a summary of the results.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-export-items.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-export-items.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-export-items.pod	(revision 5095)
@@ -0,0 +1,57 @@
+=head1 NAME
+
+fcm export-items
+
+=head1 SYNOPSIS
+
+Exports directories in SOURCE as a list of versioned items.
+
+This command is used to support a legacy working practice, in which directories
+in a source tree are regarded as individual versioned items.
+
+    fcm export-items [OPTIONS] SOURCE
+
+=head1 ARGUMENTS
+
+The SOURCE should be the URL of a branch in a Subversion repository with the
+standard FCM layout.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --config-file=PATH, --file=PATH, -f PATH
+
+Specify the path to the configuration file.
+(default=$PWD/fcm-export-items.cfg)
+
+=item --directory=PATH, -C PATH
+
+Specify the path to the destination. (default=$PWD)
+
+=item --new, -N
+
+Specify the new mode. In this mode, everything is re-exported. Otherwise, the
+system runs in incremental mode, in which the version directories are only
+updated if they do not already exist.
+
+=back
+
+=head1 CONFIGURATION
+
+The configuration file should be in the deprecated FCM 1 configuration format.
+The label in each entry should be a path relative to the source URL. If the path
+ends in * then the path is expanded recursively and any sub-directories
+containing regular files are added to the list of relative paths to export. The
+value may be empty, or it may be a list of space separated "conditions". Each
+condition is a conditional operator (>, >=, <, <=, == or !=) followed by a
+revision number. The command uses the revision log to determine the revisions at
+which the relative path has been updated in the source URL. If these revisions
+also satisfy the "conditions" set by the user, they will be considered in the
+export.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-extract.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-extract.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-extract.pod	(revision 5095)
@@ -0,0 +1,46 @@
+=head1 NAME
+
+fcm extract (ext)
+
+=head1 SYNOPSIS
+
+Invokes the deprecated FCM 1 extract system.
+
+    fcm extract [OPTIONS] [CFGFILE]
+
+=head1 ARGUMENTS
+
+The path to a CFG file may be provided. Otherwise, the extract system searches
+the default locations for an ext cfg file.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --clean
+
+Clean the destination.
+
+=item --full, -f
+
+Run in full mode. If the option for full extract is specified, the
+sub-directories created by previous extracts will be removed, so that the
+current extract can start cleanly.
+
+=item --ignore-lock
+
+Ignore lock file. When an extract is invoked, it sets up a lock file in the
+extract root directory.  The lock is normally removed at the end of the extract.
+While the lock file is in place, the extract commands invoked in the same root
+directory will fail. If you need to bypass this check for whatever reason, you
+can invoke the extract system with this option.
+
+=item --verbose=N, -v N
+
+Specify the verbosity level. If specified, the verbosity level must be an
+integer greater than 0. Verbosity level 0 is the quiet mode. Increasing the
+verbosity level will increase the amount of diagnostic output.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-gui.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-gui.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-gui.pod	(revision 5095)
@@ -0,0 +1,12 @@
+=head1 NAME
+
+fcm gui
+
+=head1 SYNOPSIS
+
+Invokes the GUI wrapper for code management commands. The optional argument PATH
+modifies the initial working directory of the GUI.
+
+    fcm gui [PATH]
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-help.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-help.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-help.pod	(revision 5095)
@@ -0,0 +1,25 @@
+=head1 NAME
+
+fcm help (h, usage, --help, --usage)
+
+=head1 SYNOPSIS
+
+Describes the usage of this program or its subcommands.
+
+    fcm help [OPTIONS] [SUBCOMMAND...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --quiet, -q
+
+Decrease the verbosity level.
+
+=item --verbose, -v
+
+Increase the verbosity level.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-keyword-print.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-keyword-print.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-keyword-print.pod	(revision 5095)
@@ -0,0 +1,27 @@
+=head1 NAME
+
+fcm keyword-print (kp)
+
+=head1 SYNOPSIS
+
+Displays registered location and/or revision keywords.
+
+    fcm keyword-print [OPTIONS] [TARGET ...]
+
+=head1 ARGUMENTS
+
+If no argument is specified, prints registered location keywords. Otherwise,
+prints the location keyword and revision keywords for the specified target. If
+--verbose is specified, prints the implied location keywords as well.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --verbose, -v
+
+Increase the verbosity level.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-loc-layout.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-loc-layout.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-loc-layout.pod	(revision 5095)
@@ -0,0 +1,25 @@
+=head1 NAME
+
+fcm loc-layout
+
+=head1 SYNOPSIS
+
+Parse the URL of a FCM/Subversion TARGET, and print its FCM layout information.
+
+    fcm keyword-print [OPTIONS] [TARGET ...]
+
+=head1 ARGUMENTS
+
+If no argument is specified, TARGET is the current working directory.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --verbose, -v
+
+Increase the verbosity level.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-make.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-make.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-make.pod	(revision 5095)
@@ -0,0 +1,75 @@
+=head1 NAME
+
+fcm make
+
+=head1 SYNOPSIS
+
+Invokes the FCM make system. See the user guide for detail.
+
+    fcm make [OPTIONS] [DECLARATION ...]
+
+=head1 ARGUMENTS
+
+Each argument is considered to be a declaration line to append to the
+configuration file.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --archive, -a
+
+Switch on archive mode. In archive mode, intermediate files will be put into
+TAR-GZIP archives on completion, e.g. extract system:
+C<.fcm-make/cache/extract/>, and build system: C<build/include/> and
+C<build/o/>.
+
+The archive mode is not suitable for a make that will be inherited or used by
+other makes.
+
+=item --config-file-path=PATH, -F PATH
+
+Specify paths for searching configuration files specified in relative paths.
+
+=item --config-file=PATH, --file=PATH, -f PATH
+
+Specify paths to the configuration files. (default = fcm-make.cfg in the
+current working directory)
+
+=item --directory=PATH, -C PATH
+
+Change directory to C<PATH> before doing anything. (default = $PWD)
+
+=item --ignore-lock
+
+Ignore lock file. When the system is invoked, it sets up a lock file in the
+destination.  The lock is normally removed when the system completes the make.
+While the lock file is in place, another make invoked in the same destination
+will fail. This option can be used to bypass this check.
+
+=item --jobs=N, -j N
+
+Specify the number of (child) processes that can be run simultaneously.
+
+=item --name=NAME, -n NAME
+
+Specify a name for the make, so that the command will search for
+fcm-makeC<NAME>.cfg instead of fcm-make.cfg, and will write context files in
+.fcm-makeC<NAME>/ and log files as fcm-makeC<NAME>.log, etc.
+
+=item --new, -N
+
+Remove items in the destination created by the previous make, and starts a new
+make.
+
+=item --quiet, -q
+
+Decrease the verbosity level.
+
+=item --verbose, -v
+
+Increase the verbosity level.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-merge.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-merge.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-merge.pod	(revision 5095)
@@ -0,0 +1,78 @@
+=head1 NAME
+
+fcm merge
+
+=head1 SYNOPSIS
+
+Merge changes from a source into your working copy.
+
+    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
+
+=over 4
+
+=item 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.
+
+=item 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.
+
+=item 3.
+
+If --reverse is specified, the command performs a reverse merge of the
+changeset(s) specified by the --revision=M:N option. If a revision is not
+specified with --rev=M:N, it attempts to merge the delta COMMITTED:(COMMITTED -
+1). If a single revision N 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.
+  
+=back
+
+The command provide a commit log message template following the merge.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --auto-log
+
+In automatic mode, adds the log messages of the merged revisions in the commit
+log. Has no effect in other merge modes.
+
+=item --dry-run
+
+Try operation but make no changes.
+
+=item --non-interactive
+
+Do no interactive prompting.
+
+=item --revision=REV, -r REV
+
+Specify a (range of) revision number(s).
+
+=item --verbose, -v
+
+Print extra information.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-mkpatch.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-mkpatch.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-mkpatch.pod	(revision 5095)
@@ -0,0 +1,61 @@
+=head1 NAME
+
+fcm mkpatch
+
+=head1 SYNOPSIS
+
+Create patches from specified revisions of a URL
+
+    fcm mkpatch [OPTIONS] URL [OUTDIR]
+
+=head1 ARGUMENTS
+
+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.
+
+A warning is given if the URL is not of a branch in a FCM project or if it is a
+sub-directory of a branch.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --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.
+
+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.
+
+=item --organisation arg
+
+This 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 Trac links with a
+modified string, and add information about the original changeset. If you
+specify the name of your organisation, it will replace Trac links such as
+"ticket:123" with "$organisation_ticket:123", and report the original changeset
+with a message such as "$organisation_changeset:1000".  If the organisation
+name is not specified then it defaults to "original".
+
+=item --revision=arg, -r arg
+
+Specify a revision number or a revision number range.
+
+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).
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-project-create.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-project-create.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-project-create.pod	(revision 5095)
@@ -0,0 +1,34 @@
+=head1 NAME
+
+fcm project-create
+
+=head1 SYNOPSIS
+
+Creates a new project in a repository.
+
+    fcm project-create [OPTIONS] PROJECT-NAME REPOS-ROOT-URL
+
+=head1 ARGUMENTS
+
+PROJECT-NAME is a valid name of a project for the given repository.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --non-interactive
+
+Do no interactive prompting. This option implies --svn-non-interactive.
+
+=item --password=PASSWORD
+
+Specify a password for write access to the repository.
+
+=item --svn-non-interactive
+
+Do no interactive prompting at commit time. This option is implied by
+--non-interactive.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-switch.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-switch.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-switch.pod	(revision 5095)
@@ -0,0 +1,16 @@
+=head1 NAME
+
+fcm switch (sw)
+
+=head1 SYNOPSIS
+
+Update the working copy to a different URL.
+
+    1. fcm switch URL[@REV1] [PATH]
+    2. fcm switch --relocate FROM TO [PATH...]
+
+Note: if --relocate is not specified, "fcm switch" will only support the options
+--non-interactive, --revision (-r) and --quiet (-q). For detail, see the output
+of "L<svn|svn> help switch".
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-test-battery.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-test-battery.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-test-battery.pod	(revision 5095)
@@ -0,0 +1,56 @@
+=head1 NAME
+
+fcm test-battery
+
+=head1 SYNOPSIS
+
+Run FCM self tests.
+
+    fcm test-battery [...]
+
+=head1 ARGUMENTS
+
+Change directory to the FCM source tree, and runs this shell commmand:
+
+    exec prove -j "${NPROC:-9}" -s -r "${@:-t}"
+
+where NPROC is the number of processors on your computer. You can override the
+L<prove|prove> command line by specifying extra arguments. E.g. If you do not
+want to run the full test suite, you can specify the names of individual test
+files or their containing directories as extra arguments. For example:
+
+Run the full test suite with the default options.
+
+    fcm test-battery
+
+Run the full test suite with 12 processes.
+
+    fcm test-battery -j 12
+
+Run only tests under C<t/fcm-make/> with 12 processes.
+
+    fcm test-battery -j 12 t/fcm-make
+
+Run only C<t/fcm-make/10-log.t> in verbose mode.
+
+    fcm test-battery -v t/fcm-make/10-log.t
+
+=head1 OPTIONS
+
+Environment variables:
+
+=over 4
+
+=item TEST_PROJECT
+
+If this is set, run CM tests using a project sub-hierarchy in the test
+repositories.
+
+=item TEST_REMOTE_HOST
+
+If this is set, run CM tests using an auto-generated Subversion server on the
+host specified.
+
+=back
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-update.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-update.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-update.pod	(revision 5095)
@@ -0,0 +1,14 @@
+=head1 NAME
+
+fcm update (up)
+
+=head1 SYNOPSIS
+
+Bring changes from the repository into the working copy.
+
+    fcm update [PATH...]
+
+Note: "fcm update" only supports --non-interactive, --revision=REV (-r REV) and
+--quiet (-q). For detail, see the output of "L<svn|svn> help update".
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-version.pod
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-version.pod	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/CLI/fcm-version.pod	(revision 5095)
@@ -0,0 +1,11 @@
+=head1 NAME
+
+fcm version (--version, -V)
+
+=head1 SYNOPSIS
+
+Print FCM version.
+
+    fcm version
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/CODE.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/CODE.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/CODE.pm	(revision 5095)
@@ -0,0 +1,307 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+package FCM::Class::CODE;
+use FCM::Class::Exception;
+use Scalar::Util qw{reftype};
+
+# Methods for working out the default value of an attribute.
+my %ATTRIB_DEFAULT_BY = (
+    default => sub {
+        my $opt_ref = shift();
+        my $ret = $opt_ref->{default};
+        return (ref($ret) && reftype($ret) eq 'CODE' ? $ret->() : $ret);
+    },
+    isa     => sub {
+        my $opt_ref = shift();
+        return
+              $opt_ref->{isa} eq 'ARRAY' ? []
+            : $opt_ref->{isa} eq 'HASH'  ? {}
+            : $opt_ref->{isa} eq 'CODE'  ? sub {}
+            :                              undef
+            ;
+    },
+);
+
+# Checks the value of an attribute.
+my $ATTRIB_CHECK = sub {
+    my ($class, $opt_ref, $key, $value, $caller_ref) = @_;
+    # Note: undef is always OK?
+    if (!defined($value)) {
+        return;
+    }
+    my $expected_isa = $opt_ref->{isa};
+    if (!$expected_isa || $expected_isa eq 'SCALAR' && !ref($value)) {
+        return;
+    }
+    if (!UNIVERSAL::isa($value, $expected_isa)) {
+        return FCM::Class::Exception->throw({
+            'code'    => FCM::Class::Exception->CODE_TYPE,
+            'caller'  => $caller_ref,
+            'package' => $class,
+            'key'     => $key,
+            'type'    => $expected_isa,
+            'value'   => $value,
+        });
+    }
+};
+
+# Creates the methods of the class.
+sub class {
+    my ($class, $attrib_opt_ref, $class_opt_ref) = @_;
+    my %class_opt = (
+        init        => sub {},
+        init_attrib => sub {@_},
+        action_of   => {},
+        (defined($class_opt_ref) ? %{$class_opt_ref} : ()),
+    );
+    if (!defined($attrib_opt_ref)) {
+        $attrib_opt_ref = {};
+    }
+    my %attrib_opt;
+    while (my ($key, $item) = each(%{$attrib_opt_ref})) {
+        my %option = (
+            r       => 1,     # readable?
+            w       => 1,     # writable?
+            default => undef, # default value or CODE to return it
+            isa     => undef, # attribute isa 
+            (     defined($item) && ref($item) ? %{$item}
+                : defined($item)               ? (isa => $item)
+                :                                ()
+            ),
+        );
+        if (defined($option{isa})) {
+            $option{isa}
+                = $option{isa} eq '$' ? 'SCALAR'
+                : $option{isa} eq '@' ? 'ARRAY'
+                : $option{isa} eq '%' ? 'HASH'
+                : $option{isa} eq '&' ? 'CODE'
+                : $option{isa} eq '*' ? 'GLOB'
+                :                       $option{isa}
+                ;
+        }
+        $attrib_opt{$key} = \%option;
+    }
+    my $main_ref = sub {
+        my ($attrib_ref, $key, @args) = @_;
+        if (!exists($class_opt{action_of}{$key})) {
+            return;
+        }
+        $class_opt{action_of}{$key}->($attrib_ref, @args);
+    };
+    no strict qw{refs};
+    # $class->new(\%attrib)
+    *{$class . '::new'} = sub {
+        my $class = shift();
+        my ($attrib_ref) = $class_opt{init_attrib}->(@_);
+        my $caller_ref = [caller()];
+        my %attrib = (defined($attrib_ref) ? %{$attrib_ref} : ());
+        while (my ($key, $value) = each(%attrib)) {
+            if (exists($attrib_opt{$key})) {
+                $ATTRIB_CHECK->(
+                    $class, $attrib_opt{$key}, $key, $value, $caller_ref,
+                );
+            }
+            #else {
+            #    delete($attrib{$key});
+            #}
+        }
+        my $self = bless(sub {$main_ref->(\%attrib, @_)}, $class);
+        KEY:
+        while (my ($key, $opt_ref) = each(%attrib_opt)) {
+            if (exists($attrib{$key})) {
+                next KEY;
+            }
+            for my $opt_name (qw{default isa}) {
+                if (defined($opt_ref->{$opt_name})) {
+                    $attrib{$key} = $ATTRIB_DEFAULT_BY{$opt_name}->($opt_ref);
+                    next KEY;
+                }
+            }
+        }
+        $class_opt{init}->(\%attrib, $self);
+        return $self;
+    };
+    # $instance->$key()
+    for my $key (keys(%{$class_opt{action_of}})) {
+        *{$class . '::' . $key}
+            = sub {my $self = shift(); $self->($key, @_)};
+    }
+    return 1;
+}
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Class::CODE
+
+=head1 SYNOPSIS
+
+    # Example
+    package Bar;
+    use base qw{FCM::Class::CODE};
+    __PACKAGE__->class(
+        {
+            # ...
+        },
+        {
+            action_of => {
+                bend => sub {
+                    my ($attrib_ref, @args) = @_;
+                    # ...
+                },
+                stretch => sub {
+                    my ($attrib_ref, @args) = @_;
+                    # ...
+                },
+            },
+        },
+    );
+    # Some time later...
+    $bar = Bar->new(\%attrib);
+    $bar->bend(@args);
+    $bar->stretch(@args);
+
+=head1 DESCRIPTION
+
+Provides a simple method to create CODE-based classes.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->class(\%attrib_opt,\%class_opt)
+
+Creates common methods for a CODE-based class.
+
+The %attrib_opt is used to configure the attributes of an instance of the class.
+The key of each element is the name of the attribute, and the value is a HASH
+containing the options of the attribute, or a SCALAR. (If a SCALAR is specified,
+it is equivalent to {isa => value}.) The options may contain:
+
+=over 4
+
+=item r
+
+(Default=true) If true, the attribute is readable.
+
+=item w
+
+(Default=true) If true, the attribute is writable.
+
+=item default
+
+(Default=undef) The default value of the attribute.
+
+If this option is defined, the attribute will be initialised to the specified
+value when the new() method is called. In the special case where the value of
+this option is a CODE reference, it will be invoked as $code->(\%attrib), and
+the default value will be the returned value of the CODE reference. This is
+useful, for example, if the default value needs to be a new instance of a class.
+If a genuine CODE reference is required as the default, this option should be
+set to a CODE reference that returns the required CODE reference itself.
+
+For example:
+
+    Foo->class({
+        foo => {default => 'foo'},          # 'foo'
+        bar => {default => sub {get_id()}}, # the next id
+        baz => {default => sub {\&code}},   # &code
+    });
+    {
+        my $id = 0;
+        sub get_id {$id++}
+    }
+
+If a default option is not defined, and if the attribute "isa" is ARRAY, HASH or
+CODE, then the default value is [], {} and sub {} respectively.
+
+=item isa
+
+(Default=undef) The expected type of the attribute. If this optioin is defined
+as $type, a new $value of the attribute is only accepted if $value is undef,
+UNIVERSAL::isa($value,$type) returns true or if $type is C<SCALAR> and the new
+value is not a reference.
+
+The attribute accepts $, @, %, & and * as aliases to SCALAR, ARRAY, HASH, CODE
+and GLOB.
+
+=back
+
+The %class_opt is used to configure what methods are created for the class, as
+well as other options for the $class->new() method. It may contain the
+following:
+
+=over 4
+
+=item init
+
+If $class_opt{init} is defined, it should be a CODE reference. If specified, it
+will be called once when $instance->new() is called, with the interface
+$init->(\%attrib,$self).
+
+=item init_attrib
+
+The value of this option must be a CODE. The $class->new() normally expects a
+single HASH reference argument. If an alternate interface to the $class->new()
+is required, this CODE can be used to turn the input argument list to the
+expected HASH reference.
+
+=item action_of
+
+This provides the actions of the class. It should be a HASH. Each $key in the
+HASH will be turned into a method implemented by the CODE reference in the
+corresponding $value: $instance->$key(@args) will call $instance->($key,@args),
+which will call $value->(\%attrib,@args).
+
+=back
+
+=item $class->new(\%attrib)
+
+Creates a new instance with %attrib. Initial values of the attributes can be
+specified using %attrib. Otherwise, the method will attempt to assign the
+default values, as specified in the class() method, to the newly created
+instance.
+
+=item $instance->$key(@args)
+
+A method is created for each $key of the %{$attrib{action_of}}.
+
+=back
+
+=head1 DIAGNOSTICS
+
+L<FCM::Class::Exception|FCM::Class::Exception> is thrown on error.
+
+=head1 SEE ALSO
+
+Inspired by the standard module L<Class::Struct|Class::Struct> and CPAN modules
+such as L<Class::Accessor|Class::Accessor>.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/Exception.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/Exception.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/Exception.pm	(revision 5095)
@@ -0,0 +1,134 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+package FCM::Class::Exception;
+
+use constant {
+    CODE_TYPE => 'CODE_TYPE',
+};
+
+sub caught {
+    my ($class, $e) = @_;
+    blessed($e) && $e->isa($class);
+}
+
+sub throw {
+    my ($class, $attrib_ref) = @_;
+    my %e = (
+        'caller'  => [],
+        'code'    => undef,
+        'key'     => undef,
+        'package' => undef,
+        'type'    => undef,
+        'value'   => undef,
+        (defined($attrib_ref) ? %{$attrib_ref} : ()),
+    );
+    die(bless(\%e, $class));
+}
+
+for my $key (qw{caller code key package type value}) {
+    no strict qw{refs};
+    *{"get_$key"} = sub {$_[0]->{$key}};
+}
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Class::Exception
+
+=head1 SYNOPSIS
+
+    eval {
+        FCM::Class::Exception->throw({
+            'caller'  => [caller()],
+            'code'    => $code,
+            'key'     => $key,
+            'package' => $package,
+            'type'    => $type,
+            'value'   => $value,
+        });
+    };
+    if (my $e = $@) {
+        if (FCM::Class::Exception->caught($e)) {
+            # ... handle this exception class
+        }
+        else {
+            # ... do something else
+        }
+    }
+
+=head1 DESCRIPTION
+
+This exception is thrown on incorrect usage of an instance of a sub-class. An
+instance of this exception has the following attributes, which can be accessed
+via $e->get_$attrib():
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item caller
+
+Returns an ARRAY reference containing the caller (as returned by the built-in
+function in ARRAY context) that triggers the exception. Note: for a CODE-based
+class, this is always the caller when the instance is created.
+
+=item code
+
+The error code, which can be one of the following:
+
+=over 4
+
+=item $e->CODE_TYPE
+
+Attempt to set the value of an attribute to an incorrect type.
+
+=back
+
+=item key
+
+The key of the attribute that triggers this exception.
+
+=item type
+
+The expected data type (for an attempt to set the value of an attribute to an
+incorrect type).
+
+=item value
+
+The value of the attribute that triggers this exception.
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM::Class::CODE|FCM::Class::CODE>
+L<FCM::Class::HASH|FCM::Class::HASH>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/HASH.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/HASH.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Class/HASH.pm	(revision 5095)
@@ -0,0 +1,340 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+package FCM::Class::HASH;
+use FCM::Class::Exception;
+use Scalar::Util qw{reftype};
+
+# Methods for working out the default value of an attribute.
+my %ATTRIB_DEFAULT_BY = (
+    default => sub {
+        my $opt_ref = shift();
+        my $ret = $opt_ref->{default};
+        return (ref($ret) && reftype($ret) eq 'CODE' ? $ret->() : $ret);
+    },
+    isa     => sub {
+        my $opt_ref = shift();
+        return
+              $opt_ref->{isa} eq 'ARRAY' ? []
+            : $opt_ref->{isa} eq 'HASH'  ? {}
+            : $opt_ref->{isa} eq 'CODE'  ? sub {}
+            :                              undef
+            ;
+    },
+);
+
+# Checks the value of an attribute.
+my $ATTRIB_CHECK = sub {
+    my ($class, $opt_ref, $key, $value, $caller_ref) = @_;
+    # Note: undef is always OK?
+    if (!defined($value)) {
+        return;
+    }
+    my $expected_isa = $opt_ref->{isa};
+    if (!$expected_isa || $expected_isa eq 'SCALAR' && !ref($value)) {
+        return;
+    }
+    if (!UNIVERSAL::isa($value, $expected_isa)) {
+        return FCM::Class::Exception->throw({
+            'code'    => FCM::Class::Exception->CODE_TYPE,
+            'caller'  => $caller_ref,
+            'package' => $class,
+            'key'     => $key,
+            'type'    => $expected_isa,
+            'value'   => $value,
+        });
+    }
+};
+
+# Creates the methods of the class.
+sub class {
+    my ($class, $attrib_opt_ref, $class_opt_ref) = @_;
+    my %class_opt = (
+        init        => sub {},
+        init_attrib => sub {@_},
+        (defined($class_opt_ref) ? %{$class_opt_ref} : ()),
+    );
+    if (!defined($attrib_opt_ref)) {
+        $attrib_opt_ref = {};
+    }
+    my %attrib_opt;
+    while (my ($key, $item) = each(%{$attrib_opt_ref})) {
+        my %option = (
+            r       => 1,     # readable?
+            w       => 1,     # writable?
+            add     => undef, # isa eq 'HASH' only, class of HASH element
+            default => undef, # default value or CODE to return it
+            isa     => undef, # attribute type
+            (     defined($item) && ref($item) ? %{$item}
+                : defined($item)               ? (isa => $item)
+                :                                ()
+            ),
+        );
+        if (defined($option{isa})) {
+            $option{isa}
+                = $option{isa} eq '$' ? 'SCALAR'
+                : $option{isa} eq '@' ? 'ARRAY'
+                : $option{isa} eq '%' ? 'HASH'
+                : $option{isa} eq '&' ? 'CODE'
+                : $option{isa} eq '*' ? 'GLOB'
+                :                       $option{isa}
+                ;
+        }
+        $attrib_opt{$key} = \%option;
+    }
+    no strict qw{refs};
+    # $class->new(\%attrib)
+    *{$class . '::new'} = sub {
+        my $class = shift();
+        my ($attrib_ref) = $class_opt{init_attrib}->(@_);
+        my $caller_ref = [caller()];
+        my %attrib = (defined($attrib_ref) ? %{$attrib_ref} : ());
+        while (my ($key, $value) = each(%attrib)) {
+            $ATTRIB_CHECK->($class, $attrib_opt{$key}, $key, $value, $caller_ref);
+        }
+        my $self = bless(\%attrib, $class);
+        KEY:
+        while (my ($key, $opt_ref) = each(%attrib_opt)) {
+            if (exists($self->{$key})) {
+                next KEY;
+            }
+            for my $opt_name (qw{default isa}) {
+                if (defined($opt_ref->{$opt_name})) {
+                    $self->{$key} = $ATTRIB_DEFAULT_BY{$opt_name}->($opt_ref);
+                    next KEY;
+                }
+            }
+        }
+        $class_opt{init}->($self);
+        return $self;
+    };
+    # $instance->$methods()
+    while (my ($key, $opt_ref) = each(%attrib_opt)) {
+        # $instance->get_$attrib()
+        # $instance->get_$attrib($name)
+        if ($opt_ref->{r}) {
+            *{$class . '::get_' . $key}
+                = defined($opt_ref->{isa}) && $opt_ref->{isa} eq 'HASH'
+                ? sub {
+                    my ($self, $name) = @_;
+                    if (!defined($name)) {
+                        return $self->{$key};
+                    }
+                    if (exists($self->{$key}{$name})) {
+                        return $self->{$key}{$name};
+                    }
+                    return;
+                }
+                : sub {$_[0]->{$key}}
+                ;
+        }
+        # $instance->set_$attrib($value)
+        if ($opt_ref->{w}) {
+            *{$class . '::set_' . $key} = sub {
+                my ($self, $value) = @_;
+                $ATTRIB_CHECK->(
+                    $class, $attrib_opt{$key}, $key, $value, [caller()],
+                );
+                $self->{$key} = $value;
+                return $self;
+            };
+        }
+        # $instance->add_$attrib($name,\%option)
+        if (   defined($opt_ref->{isa}) && $opt_ref->{isa} eq 'HASH'
+            && defined($opt_ref->{add})
+        ) {
+            *{$class . '::add_' . $key} = sub {
+                my ($self, $name, @args) = @_;
+                if (defined($self->{$key}{$name})) {
+                    return $self->{$key}{$name};
+                }
+                $self->{$key}{$name} = $opt_ref->{add}->new(@args);
+            };
+        }
+    }
+    return 1;
+}
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Class::HASH
+
+=head1 SYNOPSIS
+
+    package Breakfast;
+    use base qw{FCM::Class::HASH};
+    __PACKAGE__->class(
+        {
+            eggs  => {isa => '@'},
+            ham   => {isa => '%'},
+            bacon => '$',
+            # ...
+        },
+    );
+    # Some time later...
+    $breakfast = Breakfast->new(\%attrib);
+    @eggs = @{$breakfast->get_eggs()};
+    $breakfast->set_ham(\%ham);
+
+=head1 DESCRIPTION
+
+Provides a simple method to create HASH-based classes.
+
+The class() method creates the new() method for initiating a new instance. It
+also provides a get_$attrib() and set_$attrib() accessors for each attribute.
+Basic type checkings are performed on writing to the attributes to ensure
+correct usage.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->class(\%attrib_opt,\%class_opt)
+
+Creates the class, using the attribute options in %attrib_opt and %class_opt.
+
+The %attrib_opt is used to configure the attributes of an instance of the class.
+The key of each element is the name of the attribute, and the value is a HASH
+containing the options of the attribute, or a SCALAR. (If a SCALAR is specified,
+it is equivalent to {isa => value}.). The options may contain:
+
+=over 4
+
+=item r
+
+(Default=true) If true, the attribute is readable.
+
+=item w
+
+(Default=true) If true, the attribute is writable.
+
+=item add
+
+(Default=undef) This is only useful for a HASH attribute. If defined, it should
+be the name of a class (e.g. $attrib_class). The HASH attribute will receive an
+extra method $instance->add_$attrib($key,@args). The method will assign the
+$name element of the HASH attribute to the result of $attrib_class->new(@args).
+
+=item default
+
+(Default=undef) The default value of the attribute.
+
+If this option is defined, the attribute will be initialised to the specified
+value when the new() method is called. In the special case where the value of
+this option is a CODE reference, it will be invoked as $code->(\%attrib), and
+the default value will be the returned value of the CODE reference. This is
+useful, for example, if the default value needs to be a new instance of a class.
+If a genuine CODE reference is required as the default, this option should be
+set to a CODE reference that returns the required CODE reference itself.
+
+For example:
+
+    Foo->class({
+        foo => {default => 'foo'},          # 'foo'
+        bar => {default => sub {get_id()}}, # the next id
+        baz => {default => sub {\&code}},   # &code
+    });
+    {
+        my $id = 0;
+        sub get_id {$id++}
+    }
+
+If the default options is not defined, and if the attribute "isa" is ARRAY, HASH
+or CODE, then the default value is [], {} and sub {} respectively.
+
+=item isa
+
+(Default=undef) The expected type of the attribute. If this optioin is defined
+as $type, a new $value of the attribute is only accepted if $value is undef,
+UNIVERSAL::isa($value,$type) returns true or if $type is C<SCALAR> and the new
+value is not a reference.
+
+The attribute accepts $, @, %, & and * as aliases to SCALAR, ARRAY, HASH, CODE
+and GLOB.
+
+=back
+
+The argument %class_opt can have the following elements:
+
+=over 4
+
+=item init
+
+If $class_opt{init} is defined, it should be a CODE reference. If specified, it
+will be called just after the instance is blessed in the $class->new() method,
+with an interface $f->($instance) where $instance is the new instance.
+
+=item init_attrib
+
+The value of this option must be a CODE. The $class->new() normally expects a
+single HASH reference argument. If an alternate interface to the $class->new()
+is required, this CODE can be used to turn the input argument list to the
+expected HASH reference.
+
+=back
+
+=item $class->new(\%attrib)
+
+Creates a new instance with %attrib. Initial values of the attributes can be
+specified using %attrib. Otherwise, the method will attempt to assign the
+default values, as specified in the class() method, to the newly created
+instance.
+
+=item $instance->get_$attrib()
+
+Returns a readable attribute.
+
+=item $instance->get_$attrib($key)
+
+These are available for HASH attributes only. Returns the value of an element in
+a readable attribute.
+
+=item $instance->set_$attrib($value)
+
+Sets the value of a writable attribute. Returns $instance.
+
+=item $instance->add_$attrib($key,@args)
+
+These are available for HASH attributes (with the C<add> attribute option
+defined) only. Adds a new $key element to the HASH attribute. Returns the newly
+added element.
+
+=back
+
+=head1 DIAGNOSTICS
+
+L<FCM::Class::Exception|FCM::Class::Exception> is thrown on error.
+
+=head1 SEE ALSO
+
+Inspired by the standard module L<Class::Struct|Class::Struct> and CPAN modules
+such as L<Class::Accessor|Class::Accessor>.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/ConfigEntry.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/ConfigEntry.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/ConfigEntry.pm	(revision 5095)
@@ -0,0 +1,160 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Context::ConfigEntry;
+use base qw{FCM::Class::HASH};
+
+use Text::ParseWords qw{shellwords};
+
+__PACKAGE__->class({
+    label       => '$',
+    modifier_of => '%',
+    ns_list     => '@',
+    stack       => '@',
+    value       => '$',
+});
+
+# A shorthand for shellwords($entry->get_value()).
+sub get_values {
+    shellwords($_[0]->get_value());
+}
+
+# The config entry's left hand side of the equal sign.
+sub get_lhs {
+    my ($self) = @_;
+    my $modifier = join(
+        q{, },
+        (   map
+            {   my $value = $self->{modifier_of}{$_};
+                join(q{:}, $_, (($value && $value eq 1) ? () : $value));
+            }
+            sort keys(%{$self->{modifier_of}})
+        ),
+    );
+    my $ns = join(
+        q{ },
+        (map {my $s = $_; $s =~ s{(["'\s])}{\\$1}gxms; $s} @{$self->{ns_list}}),
+    );
+    sprintf(
+        '%s%s%s',
+        $self->{label},
+        ($modifier ? "{$modifier}" : q{}),
+        ($ns ? "[$ns]" : q{}),
+    );
+}
+
+# The config entry, as a string.
+sub as_string {
+    my ($self, $in_fcm1) = @_;
+    my $value = $self->{value};
+    $value ||= q{};
+    $value =~ s{(\\)+(\$)}{$1$1\\$2}gxms;
+    sprintf(($in_fcm1 ? '%s %s' : '%s = %s'), $self->get_lhs(), $value);
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::ConfigEntry;
+
+=head1 SYNOPSIS
+
+    my $c_entry = FCM::Context::ConfigEntry->new({
+        label       => 'egg',
+        modifier_of => {fried => 1},
+        ns_list     => [qw{all day breakfast}],
+        stack       => [[$breakfast_menu, 10], [$menu, 20]],
+        value       => 2,
+    });
+
+    # ... some time later
+    $label       = $c_entry->get_label();
+    %modifier_of = %{$c_entry->get_modifier_of()};
+    @ns_list     = @{$c_entry->get_ns_list()};
+    @stack       = @{$c_entry->get_stack()};
+    $value       = $c_entry->get_value();
+
+    print($c_entry->as_string());
+    # should print: egg{fried: 1}[all day breakfast] = 2
+
+=head1 DESCRIPTION
+
+This class is based on L<FCM::Class::HASH|FCM::Class::HASH> for representing an
+entry in a FCM configuration file. All attributes can be read using the
+$instance->get_$attrib() methods.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item label
+
+The label of the entry.
+
+=item modifier_of
+
+A HASH containing the modifiers of this entry.
+
+=item ns_list
+
+An ARRAY containing the namespaces of this entry.
+
+=item stack
+
+An ARRAY containing the locator stack that provides this entry. The first
+element represents the top of the stack. Each element should be a reference to a
+2-element array [RESOURCE, LINE_NUMBER].
+
+=item value
+
+The value of this entry.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item $instance->as_string($in_fcm1)
+
+Returns a string representation of the config entry. If the optional argument
+$in_fcm1 is specified, it will return the config entry in FCM 1 format.
+
+=item $instance->get_lhs()
+
+Returns a string representation of the left hand side of the config entry.
+
+=item $instance->get_values()
+
+A shorthand for shellwords($instance->get_value()).
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Event.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Event.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Event.pm	(revision 5095)
@@ -0,0 +1,383 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+#-------------------------------------------------------------------------------
+
+package FCM::Context::Event;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    CM_ABORT                      => 'CM_ABORT',
+    CM_BRANCH_CREATE_SOURCE       => 'CM_BRANCH_CREATE_SOURCE',
+    CM_BRANCH_LIST                => 'CM_BRANCH_LIST',
+    CM_COMMIT_MESSAGE             => 'CM_COMMIT_MESSAGE',
+    CM_CONFLICT_TEXT              => 'CM_CONFLICT_TEXT',
+    CM_CONFLICT_TEXT_SKIP         => 'CM_CONFLICT_TEXT_SKIP',
+    CM_CONFLICT_TREE              => 'CM_CONFLICT_TREE',
+    CM_CONFLICT_TREE_SKIP         => 'CM_CONFLICT_TREE_SKIP',
+    CM_CONFLICT_TREE_TIME_WARN    => 'CM_CONFLICT_TREE_TIME_WARN',
+    CM_CREATE_TARGET              => 'CM_CREATE_TARGET',
+    CM_LOG_EDIT                   => 'CM_LOG_EDIT',
+    #CM_WC_STATUS                  => 'CM_WC_STATUS',
+    #CM_WC_STATUS_PATH             => 'CM_WC_STATUS_PATH',
+    CONFIG_OPEN                   => 'CONFIG_OPEN',
+    CONFIG_ENTRY                  => 'CONFIG_ENTRY',
+    CONFIG_VAR_UNDEF              => 'CONFIG_VAR_UNDEF',
+    E                             => 'E',
+    EXPORT_ITEM_CREATE            => 'EXPORT_ITEM_CREATE',
+    EXPORT_ITEM_DELETE            => 'EXPORT_ITEM_DELETE',
+    FCM_VERSION                   => 'FCM_VERSION',
+    KEYWORD_ENTRY                 => 'KEYWORD_ENTRY',
+    OUT                           => 'OUT',
+    MAKE_BUILD_SHELL_OUT          => 'MAKE_BUILD_SHELL_OUT',
+    MAKE_BUILD_SOURCE_ANALYSE     => 'MAKE_BUILD_SOURCE_ANALYSE',
+    MAKE_BUILD_SOURCE_SUMMARY     => 'MAKE_BUILD_SOURCE_SUMMARY',
+    MAKE_BUILD_TARGET_DONE        => 'MAKE_BUILD_TARGET_DONE',
+    MAKE_BUILD_TARGET_FAIL        => 'MAKE_BUILD_TARGET_FAIL',
+    MAKE_BUILD_TARGET_FROM_NS     => 'MAKE_BUILD_TARGET_FROM_NS',
+    MAKE_BUILD_TARGET_MISSING_DEP => 'MAKE_BUILD_TARGET_MISSING_DEP',
+    MAKE_BUILD_TARGET_SELECT      => 'MAKE_BUILD_TARGET_SELECT',
+    MAKE_BUILD_TARGET_SELECT_TIMER=> 'MAKE_BUILD_TARGET_SELECT_TIMER',
+    MAKE_BUILD_TARGET_STACK       => 'MAKE_BUILD_TARGET_STACK',
+    MAKE_BUILD_TARGET_SUMMARY     => 'MAKE_BUILD_TARGET_SUMMARY',
+    MAKE_BUILD_TARGET_TASK_SUMMARY=> 'MAKE_BUILD_TARGET_TASK_SUMMARY',
+    MAKE_BUILD_TARGETS_FAIL       => 'MAKE_BUILD_TARGETS_FAIL',
+    MAKE_DEST                     => 'MAKE_DEST',
+    MAKE_EXTRACT_PROJECT_TREE     => 'MAKE_EXTRACT_PROJECT_TREE',
+    MAKE_EXTRACT_RUNNER_SUMMARY   => 'MAKE_EXTRACT_RUNNER_SUMMARY',
+    MAKE_EXTRACT_SYMLINK          => 'MAKE_EXTRACT_SYMLINK',
+    MAKE_EXTRACT_TARGET           => 'MAKE_EXTRACT_TARGET',
+    MAKE_EXTRACT_TARGET_SUMMARY   => 'MAKE_EXTRACT_TARGET_SUMMARY',
+    MAKE_MIRROR                   => 'MAKE_MIRROR',
+    SHELL                         => 'SHELL',
+    TASK_WORKERS                  => 'TASK_WORKERS',
+    TIMER                         => 'TIMER',
+};
+
+__PACKAGE__->class({args => '@', code => '$'});
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Event
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Event;
+    my $event_ctx = FCM::Context::Event->new($code, @args);
+
+=head1 DESCRIPTION
+
+An instance of this class represents the context of an event. This class is a
+sub-class of L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item args
+
+An ARRAY reference that represents the additional arguments/contexts of the
+event.
+
+=item code
+
+The event code. See below
+
+=back
+
+=head1 EVENTS
+
+The following is a list of event codes.
+
+=over 4
+
+=item FCM::Context::Event->CM_ABORT
+
+This event is raised when a code management command aborts. The 1st argument
+should be either "user" (user abort) or "null" (null command).
+
+=item FCM::Context::Event->CM_BRANCH_CREATE_SOURCE
+
+This event is raised to notify the source of a branch create. The 1st argument
+should be the expected source URL, and the 2nd argument is the specified peg
+revision.
+
+=item FCM::Context::Event->CM_BRANCH_LIST
+
+This event is raised when doing a branch listing. The 1st argument should be the
+project location and the rest of the arguments are the branches discovered.
+
+=item FCM::Context::Event->CM_COMMIT_MESSAGE
+
+This event is raised to notify the user the log message to be used for a commit.
+The 1st argument of the event should be an instance of
+FCM::System::CM::CommitMessage::State.
+
+=item FCM::Context::Event->CM_CONFLICT_TEXT
+
+This event is raised to notify the path of a file with a text conflict.
+
+=item FCM::Context::Event->CM_CONFLICT_TEXT_SKIP
+
+This event is raised to notify the path of a file with a text conflict that
+cannot be resolved using a merge tool. E.g. it may be a binary file.
+
+=item FCM::Context::Event->CM_CONFLICT_TREE
+
+This event is raised to notify the path of a node with a tree conflict.
+
+=item FCM::Context::Event->CM_CONFLICT_TREE_SKIP
+
+This event is raised to notify the path of a node with a tree conflict that
+cannot be resolved automatically under current functionality. For example, it
+may be a directory containing multiple conflicts.
+
+=item FCM::Context::Event->CM_CREATE_TARGET
+
+This event is raised to notify the target of a newly created URL. The 1st argument
+should be the target URL.
+
+=item FCM::Context::Event->CM_LOG_EDIT
+
+This event is raised before the system launches an editor to edit a commit log
+message. The 1st argument of the event should be the editor command.
+
+=item FCM::Context::Event->CONFIG_ENTRY
+
+This entry is raised to notify the reading of a configuration file entry. The
+1st argument should be a blessed reference of a
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry>. The second argument
+should be a boolean flag to indicate whether this entry is in FCM 1 format or
+not.
+
+=item FCM::Context::Event->CONFIG_OPEN
+
+This event is raised when a new configuration file is opened for reading. The
+1st argument of this event is an ARRAY that represents the include file stack,
+where the last element is the top of the stack. Each element of the stack is a
+2-element ARRAY reference, where the first element is a
+L<FCM::Context::Locator|FCM::Context::Locator> object and the second element is
+the line number. (At the top of the stack, the line number is set to 0.) The 2nd
+optional argument of this event is a number to adjust the verbosity level of the
+event.
+
+=item FCM::Context::Event->CONFIG_VAR_UNDEF
+
+This event is raised when a variable is undefined. The arguments of this event
+contain 2 elements. The 1st element is the configuration entry as a blessed
+reference of L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry>. The 2nd
+element is the name of the variable.
+
+=item FCM::Context::Event->E
+
+This event is raised when to notify an exception. The 1st argument of this event
+should be the exception.
+
+=item FCM::Context::Event->EXPORT_ITEM_CREATE
+
+This event is raised when the export-items system creates a link to an item.
+The 1st argument is the namespace of the item, the 2nd argument is the revision
+of the item and the 3rd argument is the name of the link.
+
+=item FCM::Context::Event->EXPORT_ITEM_DELETE
+
+This event is raised when the export-items system deletes a link to an item.
+The 1st argument is the namespace of the item, the 2nd argument is the revision
+of the item and the 3rd argument is the name of the link.
+
+=item FCM::Context::Event->FCM_VERSION
+
+This event is raised to notify the FCM version.
+
+=item FCM::Context::Event->KEYWORD_ENTRY
+
+This event is raised to notify a keyword entry. The 1st argument is the keyword
+entry as a blessed reference of FCM::Context::Keyword::Entry as described in
+L<FCM::Context::Keyword|FCM::Context::Keyword>.
+
+=item FCM::Context::Event->MAKE_BUILD_SHELL_OUT
+
+This event is raised to notify (shell command) output from make/build. The 1st
+argument should be the STDOUT, and the 2nd argument should be the STDERR.
+
+=item FCM::Context::Event->MAKE_BUILD_SOURCE_ANALYSE
+
+This event is raised when the make/build system has analysed a source file. The
+1st argument should be a blessed reference of FCM::Context::Make::Build::Source
+as described in L<FCM::Context::Make::Build|FCM::Context::Make::Build>. The 2nd
+argument should be the time it takes for the analysis.
+
+=item FCM::Context::Event->MAKE_BUILD_SOURCE_SUMMARY
+
+This event is raised when the make/build system has analysed all its source
+files. The 1st argument should be the total number of files. The 2nd argument
+should be the number analysed. The 3rd argument should be the elapsed time. The
+4th argument should be the total time, which may differ from the elapsed time if
+the analysis is run on more than 1 process.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_DONE
+
+This event is raised when the make/build system has successfully updated a
+target or an update is unnecessary. The 1st argument is the target (as an
+instance of FCM::Context::Make::Build::Target), the 2nd argument is the elapsed
+time of the update, if relevant.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_FAIL
+
+This event is raised when the make/build system failed to update a target or the
+target is failed by its dependencies. The 1st argument is the target (as an
+instance of FCM::Context::Make::Build::Target), the 2nd argument is the elapsed
+time, if the target is failed by its update.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_FROM_NS
+
+This event is raised when the make/build system has generated a build target
+from a source or a source name-space. The arguments are: source/target
+namespace, target task, target category, and the target key.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_MISSING_DEP
+
+This event is raised when the make/build system has discarded a missing
+dependency from a target. The 1st argument is the target ID, the 2nd argument is
+the dependency ID, and the 3rd argument is the dependency type.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_SELECT
+
+This event is raised when the make/build system has selected a set of targets to
+build. The 1st argument is a HASH reference of the target set.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_SELECT_TIMER
+
+This event is raised when the make/build system has completed the target select
+and dependency tree analysis. The only argument is the elapsed time.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_STACK
+
+This event is raised when make/build system checks a target for cyclic
+dependency.  The 1st argument is the key of the task. The 2nd argument is rank
+of the task in the dependency hierarchy. The 3rd argument is the number of
+dependencies the task has if the task has already been checked, or undef if this
+is the first check for the task.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_SUMMARY
+
+This event is raised when the make/build system has finished updating its
+targets, and is ready to give a total summary. The 1st argument is the number of
+modified targets, the 2nd argument is the number unchanged, the 3rd argument is
+the number failed, the 4th argument is the elapsed time.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGET_TASK_SUMMARY
+
+This event is raised when the make/build system has finished updating its
+targets, and is ready to give a summary of each type of task. The 1st argument
+is the task type name, the 2nd argument is the number of modified targets, the
+3rd argument is the number unchanged, the 4th argument is the number failed, and
+the 5th argument is the total time spent on this task type.
+
+=item FCM::Context::Event->MAKE_BUILD_TARGETS_FAIL
+
+This event is raised when the make/build system has finished updating its
+targets, but some targets failed to update. The 1st argument is an ARRAY of
+FCM::Context::Make::Build::Target objects representing the failed targets.
+
+=item FCM::Context::Event->MAKE_DEST
+
+This event is raised when the make system sets up the destination. The 1st
+argument of this event is the make system context.
+
+=item FCM::Context::Event->MAKE_EXTRACT_PROJECT_TREE
+
+This event is raised after the make/extract system has finished gathering
+information for the source trees of each project. The 1st argument is a HASH of
+the (keys) project name-spaces and the (values) list (ARRAY) of source tree
+locators L<FCM::Context::Locator|FCM::Context::Locator> in the project.
+
+=item FCM::Context::Event->MAKE_EXTRACT_RUNNER_SUMMARY
+
+This event is raised after the make/extract system has finished using the task
+runner to perform some tasks. The 1st argument is an identifier for the tasks
+performed. The 2nd argument is the number of tasks. The 2nd argument is the
+elapsed time. The 3rd argument is the total time in all processes.
+
+=item FCM::Context::Event->MAKE_EXTRACT_SYMLINK
+
+This event is raised as the make/extract system ignores a source that is a
+symbolic link. The 1st argument of this event should be a blessed reference of
+FCM::Context::Make::Extract::Source as described in
+L<FCM::Context::Make::Extract|FCM::Context::Make::Extract>.
+
+=item FCM::Context::Event->MAKE_EXTRACT_TARGET
+
+This event is raised as the make/extract system updates a target destination. The
+1st argument of this event should be a blessed reference of
+FCM::Context::Make::Extract::Target as described in
+L<FCM::Context::Make::Extract|FCM::Context::Make::Extract>.
+
+=item FCM::Context::Event->MAKE_EXTRACT_TARGET_SUMMARY
+
+This event is raised after the make/extract system has updated all target
+destinations. The 1st argument of this event is a HASH reference, which contains
+2 keys: status and status_of_source, i.e. the destination status of the targets
+and the source status of the targets respectively. The values of both are HASH
+references. The keys are the names of the status, and the values are the number
+of targets with the corresponding status.
+
+=item FCM::Context::Event->MAKE_MIRROR
+
+This event is raised as the make/mirror system updates a target destination. The
+1st argument of this event should be the target URI. The remaining arguments
+should be the source paths.
+
+=item FCM::Context::Event->OUT
+
+This event is raised to notify (shell command) output. The 1st argument should
+be the STDOUT, and the 2nd argument should be the STDERR.
+
+=item FCM::Context::Event->SHELL
+
+This event is raised to notify the completion of a shell command. The 1st
+argument is an ARRAY reference of the shell command. The 2nd argument is an
+integer to override the verbosity level. The 3rd argument is the return code and
+the 4th argument is the elapsed time.
+
+=item FCM::Context::Event->TASK_WORKERS
+
+This event is raised on initialisation and destruction of worker processes for
+the utility task runner. The 1st argument should either be "init" or "destroy".
+The 2nd argument should be the number of workers initialised/destroyed.
+
+=item FCM::Context::Event->TIMER
+
+This event is raised at the start and end of the utility timer. The 1st
+argument is the name of the piece of code to time. The 2nd argument is the start
+the timer. The 3rd argument is the elapsed time at the end. If the 3rd argument
+is not specified, it is the start of the timer.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Keyword.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Keyword.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Keyword.pm	(revision 5095)
@@ -0,0 +1,221 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::Context::Keyword;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    BROWSER_CONFIG    => 'FCM::Context::Keyword::BrowserConfig',
+    ENTRY             => 'FCM::Context::Keyword::Entry',
+    ENTRY_OF_LOCATION => 'FCM::Context::Keyword::Entry::Location',
+};
+
+use Scalar::Util qw{blessed};
+
+__PACKAGE__->class({
+    entry_class    => {w => 0, isa => '$', default => ENTRY_OF_LOCATION},
+    entry_by_key   => {w => 0, isa => '%'},
+    entry_by_value => {w => 0, isa => '%'},
+});
+
+sub add_entry {
+    my $self = shift();
+    my ($key, $value, $entry);
+    if (blessed($_[0])) {
+        $entry = $_[0];
+        $key   = $entry->get_key();
+        $value = $entry->get_value();
+    }
+    else {
+        ($key, $value, my $attrib_ref) = @_;
+        $attrib_ref ||= {};
+        $entry = $self->get_entry_class()->new({
+            key => lc($key), value => $value, %{$attrib_ref},
+        });
+    }
+    $self->{entry_by_key}{lc($key)} = $entry;
+    $self->{entry_by_value}{$value} = $entry;
+    return $entry;
+}
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Keyword::BrowserConfig;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({comp_pat => undef, loc_tmpl => '$', rev_tmpl => '$'});
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Keyword::Entry;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({
+    key   => {w => 0, isa => '$'},
+    value => {w => 0, isa => '$'},
+});
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Keyword::Entry::Location;
+use base qw{FCM::Context::Keyword::Entry};
+
+my $CTX = 'FCM::Context::Keyword';
+
+__PACKAGE__->class(
+    {   browser_config  => $CTX->BROWSER_CONFIG,
+        ctx_of_implied  => $CTX,
+        ctx_of_rev      => $CTX,
+        implied         => {isa => '$', default => 0},
+        key             => {isa => '$', w => 0},
+        loaded_rev_prop => '$',
+        type            => '$',
+        value           => {isa => '$', w => 0},
+    },
+    {   init => sub {
+            my ($self) = @_;
+            if (!$self->get_implied()) {
+                $self->{browser_config} = $CTX->BROWSER_CONFIG->new();
+                $self->{ctx_of_implied} = $CTX->new();
+                $self->{ctx_of_rev} = $CTX->new({entry_class => $CTX->ENTRY});
+            }
+        },
+    },
+);
+
+# Returns true if this is an implied entry
+sub is_implied {
+    $_[0]->{implied};
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Keyword
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Keyword;
+
+=head1 DESCRIPTION
+
+Provides a context object for the FCM keyword utility. All the classes described
+below are sub-classes of L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 OBJECTS
+
+=head2 FCM::Context::Keyword
+
+An object of this class is used to store a list of keyword entries. It has the
+following methods:
+
+=over 4
+
+=item $instance->add_entry($key,$value,\%attrib)
+
+Creates and adds a new entry.
+
+=item $instance->add_entry($entry)
+
+Adds a new entry.
+
+=item $instance->get_entry_class()
+
+Returns the class of the entry stored by this context.
+
+=item $instance->get_entry_by_key()
+
+Returns a HASH reference to map the entry keys with the entry objects.
+
+=item $instance->get_entry_by_value()
+
+Returns a HASH reference to map the entry values with the entry objects.
+
+=back
+
+=head2 FCM::Context::Keyword::BrowserConfig
+
+An object of this class is used to store the configuration for mapping a
+location to a browser URL. It has the following attributes:
+
+=over 4
+
+=item comp_pat
+
+The pattern for extracting components from a locator, for putting into the
+browser location template.
+
+=item loc_tmpl
+
+The browser location template.
+
+=item rev_tmpl
+
+The browser revision template.
+
+=back
+
+=head2 FCM::Context::Keyword::Entry
+
+This is used to store a simple keyword entry (e.g. for revision keywords). It
+has 2 attributes, the I<key> and the I<value>.
+
+=head2 FCM::Context::Keyword::Entry::Location
+
+This is a sub-class of FCM::Context::Keyword::Entry, and is used to store a
+location keyword entry. It has the following additional attributes:
+
+=over 4
+
+=item browser_config
+
+The configuration L</FCM::Context::Keyword::BrowserConfig> for mapping this
+location to a browser URL.
+
+=item ctx_of_implied
+
+The context </FCM::Context::Keyword> object of the implied entries, if this
+entry is a primary location.
+
+=item ctx_of_rev
+
+The context </FCM::Context::Keyword> object of the revision keywords.
+
+=item implied
+
+A flag to indicate that this is an entry implied by a primary location.
+
+=item loaded_rev_prop
+
+A flag to indicate that a previous attempt is made to load revision keywords
+from the I<property> of the location.
+
+=item type
+
+The location type.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Locator.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Locator.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Locator.pm	(revision 5095)
@@ -0,0 +1,144 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::Context::Locator;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    L_INIT       => -1,
+    L_PARSED     =>  0,
+    L_NORMALISED =>  1,
+    L_INVARIANT  =>  2,
+};
+
+__PACKAGE__->class(
+    {   last_mod_rev  => '$',
+        last_mod_time => '$',
+        type          => '$',
+        value         => '$',
+        value_at_init => {isa => '$', i => 1, w => 0},
+        value_level   => {isa => '$', default => L_INIT},
+    },
+    {   init_attrib => sub {
+            my ($value, $attrib_ref) = @_;
+            return {
+                (defined($attrib_ref) ? %{$attrib_ref} : ()),
+                value         => $value,
+                value_at_init => $value,
+            };
+        },
+    }
+);
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Locator
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Locator;
+    $locator = FCM::Context::Locator->new($value_at_init, {type => $type});
+    $locator->set_value($value);
+    $locator->set_value_level($locator->L_INVARIANT);
+    print($locator->get_value(), "\n");
+
+=head1 DESCRIPTION
+
+A simple structure for storing the values of a FCM locator. It is based on
+L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 ATTRIBUTES
+
+An instance has the following attributes, all of which can be initialised and
+accessed via an $instance->get_$attrib() method:
+
+=over 4
+
+=item last_mod_rev
+
+The last modified revision.
+
+=item last_mod_time
+
+The last modified time (seconds since epoch).
+
+=item type
+
+The locator type.
+
+=item value
+
+The current value of the locator.
+
+=item value_at_init
+
+The value of the locator when the object is initialised.
+
+=item value_level
+
+The value level of the locator. It can be one of the L_* constants. A higher
+level indicates that the value is more processed.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new($value,\%attrib)
+
+Returns a new instance.
+
+=head1 CONSTANTS
+
+=over 4
+
+=item FCM::Context::Locator->L_INIT
+
+The lowest value level, i.e. the value has not been processed. (default)
+
+=item FCM::Context::Locator->L_PARSED
+
+The value level is between L_INIT and L_NORMALISED, i.e. where necessary, the
+FCM location keyword is substituted.
+
+=item FCM::Context::Locator->L_NORMALISED
+
+The value level is between L_PARSED and L_INVARIANT, i.e. where necessary, the
+FCM location and revision keywords are substituted and the value has been tidied
+(e.g. extra slashes in the path removed).
+
+=item FCM::Context::Locator->L_INVARIANT
+
+The highest value level, i.e. if the locator points to a version control
+resource, the value is expected to be tagged with a specific revision.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make.pm	(revision 5095)
@@ -0,0 +1,176 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    ST_UNKNOWN =>  0,
+    ST_INIT    =>  1,
+    ST_OK      =>  2,
+    ST_FAILED  => -1,
+};
+
+__PACKAGE__->class({
+    ctx_of            => '%',
+    description       => '$',
+    dest              => '$',
+    dest_lock         => '$',
+    error             => {},
+    inherit_ctx_list  => '@',
+    name              => {isa => '$', default => q{}},
+    option_of         => '%',
+    prev_ctx          => __PACKAGE__,
+    status            => {isa => '$', default => ST_UNKNOWN},
+    steps             => '@',
+});
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Make
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Make;
+    my $ctx = FCM::Context::Make->new();
+
+=head1 DESCRIPTION
+
+Provides a context object for the FCM make system. It is a sub-class of
+L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 OBJECTS
+
+=head2 FCM::Context::Make
+
+An instance of this class represents a make. It has the following
+attributes:
+
+=over 4
+
+=item ctx_of
+
+A HASH containing the (keys) IDs and the (values) context objects of the make.
+
+=item description
+
+A description string for this make.
+
+=item dest
+
+The destination of this make.
+
+=item dest_lock
+
+The destination lock of this make.
+
+=item error
+
+This should be set to the value of the exception, if this make ends in one.
+
+=item inherit_ctx_list
+
+An ARRAY of contexts inherited by this make.
+
+=item name
+
+The name of the context. This is useful if we need to have multiple (but
+non-overlapping) "fcm make" runs in the same destination. (Default is a null
+string.)
+
+=item option_of
+
+A HASH to store the options of this make. See L</OPTION> for detail.
+
+=item status
+
+The status of the make.
+
+=item steps
+
+The names of the steps to make.
+
+=back
+
+=head1 OPTION
+
+The C<option_of> attribute of a FCM::Context::Make object may contain the
+following elements:
+
+=over 4
+
+=item config-file
+
+An ARRAY of configuration file names.
+
+=item directory
+
+The working directory of the make.
+
+=item ignore-lock
+
+Ignores lock file in the destination.
+
+=item jobs
+
+The number of (child) threads that can be run simultaneously.
+
+=item new
+
+Performs a make in "new" mode (as opposed to the "incremental" mode).
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item FCM::Context::Make->ST_UNKNOWN
+
+The status of a make context or the context of a subsystem. Status is unknown.
+
+=item FCM::Context::Make->ST_INIT
+
+The status of a make context or the context of a subsystem. The make or the
+subsystem has initialised, but not completed.
+
+=item FCM::Context::Make->ST_OK
+
+The status of a make context or the context of a subsystem. The make or the
+subsystem has completed successfully.
+
+=item FCM::Context::Make->ST_FAILED
+
+The status of a make context or the context of a subsystem. The make or the
+subsystem has failed.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Build.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Build.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Build.pm	(revision 5095)
@@ -0,0 +1,481 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Build;
+use base qw{FCM::Class::HASH};
+
+use FCM::Context::Make;
+
+use constant {
+    CTX_SOURCE  => 'FCM::Context::Make::Build::Source',
+    CTX_TARGET  => 'FCM::Context::Make::Build::Target',
+    ID_OF_CLASS => 'build',
+};
+
+my $ST_UNKNOWN = FCM::Context::Make->ST_UNKNOWN;
+
+__PACKAGE__->class(
+    {   dest             => '$',
+        dests            => '@',
+        id               => {isa => '$' , default => ID_OF_CLASS},
+        id_of_class      => {isa => '$' , default => ID_OF_CLASS},
+        input_ns_excl    => '@',
+        input_ns_incl    => '@',
+        input_source_of  => '%',
+        prop_of          => '%',
+        source_of        => '%',
+        status           => {isa => '$' , default => $ST_UNKNOWN},
+        target_of        => '%',
+        target_key_of    => '%',
+        target_select_by => '%',
+    },
+);
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Build::Source;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({
+    checksum   => '$',
+    deps       => '@',
+    info_of    => '%',
+    ns         => '$',
+    path       => '$',
+    prop_of    => '%',
+    type       => '$',
+    up_to_date => '$',
+});
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Build::Target;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    CT_BIN                  => 'bin',
+    CT_ETC                  => 'etc',
+    CT_INCLUDE              => 'include',
+    CT_LIB                  => 'lib',
+    CT_O                    => 'o',
+    CT_SRC                  => 'src',
+    POLICY_CAPTURE          => 'POLICY_CAPTURE',
+    POLICY_FILTER           => 'POLICY_FILTER',
+    POLICY_FILTER_IMMEDIATE => 'POLICY_FILTER_IMMEDIATE',
+    ST_FAILED               => 'ST_FAILED',
+    ST_MODIFIED             => 'ST_MODIFIED',
+    ST_OOD                  => 'ST_OOD',
+    ST_UNCHANGED            => 'ST_UNCHANGED',
+    ST_UNKNOWN              => 'ST_UNKNOWN',
+};
+
+__PACKAGE__->class(
+    {   category        => '$',
+        checksum        => '$',
+        deps            => '@',
+        dep_policy_of   => '%',
+        failed_by       => '@',
+        info_of         => '%',
+        key             => '$',
+        ns              => '$',
+        path            => '$',
+        path_of_prev    => '$',
+        path_of_source  => '$',
+        prop_of         => '%',
+        prop_of_prev_of => '%',
+        status          => {isa => '$', default => ST_UNKNOWN},
+        status_of       => '%',
+        task            => '$',
+        triggers        => '@',
+        type            => '$',
+    },
+);
+
+# Returns true if target has a usable dest status.
+sub can_be_source {
+    $_[0]->get_category() && $_[0]->get_category() eq CT_SRC;
+}
+
+# Returns true if target has an OK status.
+sub is_ok {
+    $_[0]->get_status() eq ST_MODIFIED || $_[0]->get_status() eq ST_UNCHANGED;
+}
+
+# Returns true if target has a failed status.
+sub is_failed {
+    $_[0]->get_status() eq ST_FAILED;
+}
+
+# Shorthand for $target->get_status() eq $target->ST_MODIFIED.
+sub is_modified {
+    $_[0]->get_status() eq ST_MODIFIED;
+}
+
+# Shorthand for $target->get_status() eq $target->ST_UNCHANGED.
+sub is_unchanged {
+    $_[0]->get_status() eq ST_UNCHANGED;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Make::Build
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Make::Build;
+    my $ctx = FCM::Context::Make::Build->new();
+
+=head1 DESCRIPTION
+
+Provides a context object for the FCM build system. All the classes described
+below are sub-classes of L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 OBJECTS
+
+=head2 FCM::Context::Make::Build
+
+An instance of this class represents a build. It has the following
+attributes:
+
+=over 4
+
+=item dest
+
+The destination of the build.
+
+=item dests
+
+An ARRAY containing the path for searching items in the current build.
+
+=item id
+
+The ID of the context. (default="build")
+
+=item id_of_class
+
+The class ID of the context. (default="build")
+
+=item input_source_of
+
+A HASH to map a name space to its ARRAY of input sources.
+
+=item input_ns_excl
+
+An ARRAY of source name-spaces to exclude.
+
+=item input_ns_incl
+
+An ARRAY of source name-spaces to include.
+
+=item prop_of
+
+A HASH containing the named properties (i.e. options and settings of named
+external tools). Expects a value to be an instance of
+L<FCM::Context::Make::Share::Property|FCM::Context::Make::Share::Property>.
+
+=item prop_of_prev_of
+
+A HASH containing the named properties (i.e. options and settings of named
+external tools) in the latest successful update of this target. Expects a value
+to be an instance of
+L<FCM::Context::Make::Share::Property|FCM::Context::Make::Share::Property>.
+
+=item source_of
+
+A HASH to map the namespace to the source contexts. Each element is expected to
+be an L</FCM::Context::Make::Build::Source> object.
+
+=item status
+
+The status of this context. See L<FCM::Context::Make|FCM::Context::Make> for the
+status constants.
+
+=item target_of
+
+A HASH to map the namespace to the target contexts. Each element is expected to
+be an L</FCM::Context::Make::Build::Target> object.
+
+=item target_key_of
+
+A HASH to map the automatic key of targets to their desired key.
+
+=item target_select_by
+
+A HASH to allow users to specify how to select from all the targets. The key can
+be "category", "key", "ns" or "task". Each value should be a HASH that
+represents the set of criteria.
+
+=back
+
+=head2 FCM::Context::Make::Build::Source
+
+An instance of this class represents an actual source of the build. It has the
+following attributes:
+
+=over 4
+
+=item checksum
+
+The checksum of the source file.
+
+=item deps
+
+An ARRAY to contain the dependencies of the source file. Each element of the
+ARRAY is expected to be a reference to a two-element ARRAY [$name, $type] where
+$name is the name of the dependency and $type is its type.
+
+=item info_of
+
+A HASH to contain the extra information of the source file. E.g. If the {main}
+element is true, the source contains a main program. If the {symbols} element
+is defined, it contains a reference to an ARRAY of program unit symbols that has
+been found in the source file.
+
+=item ns
+
+The name-space of the source file.
+
+=item path
+
+The path in the file system pointing to the source file.
+
+=item prop_of
+
+A HASH containing the keys and the values of the build properties (mainly on
+dependency settings) of the source.
+
+=item type
+
+The file type of the source file.
+
+=item up_to_date
+
+A flag to indicate whether the source file is up to date, compared with a
+previous build or the nearest inherited build.
+
+=back
+
+=head2 FCM::Context::Make::Build::Target
+
+An instance of this class represents a target of the build. It has the following
+attributes:
+
+=over 4
+
+=item category
+
+The target category, e.g. bin, etc, include, lib, o, src
+
+=item checksum
+
+The checksum of the target.
+
+=item deps
+
+An ARRAY containing the dependencies of the target. Each element of the
+ARRAY is expected to be a reference to a two-element ARRAY [$name, $type] where
+$name is the name of the dependency and $type is its type.
+
+=item dep_policy_of
+
+A HASH to contain a map between each relevant dependency type of this target and
+its policy to apply to the dependency type. The policy should take the value of
+POLICY_CAPTURE, POLICY_FILTER or POLICY_FILTER_IMMEDIATE.
+
+=item failed_by
+
+On failure, set to an ARRAY containing the names of the targets that cause the
+failure of this target.
+
+=item info_of
+
+A HASH to contain the extra information of the target. E.g. The {paths} => ARRAY
+reference of include/object search paths for the compile, link and preprocess
+tasks; and {deps}{o} => ARRAY reference and {deps}{o.special} => ARRAY
+reference of object dependency for the link tasks.
+
+=item key
+
+The key (i.e. the base name) of the target.
+
+=item ns
+
+The name-space (of the source file) associated with this target.
+
+=item path
+
+The path in the file system where the target can be located.
+
+=item path_of_prev
+
+The path in the file system where the target in a previous or inherited build
+can be located.
+
+=item path_of_source
+
+The path in the file system where the source file associated with the target can
+be located.
+
+=item prop_of
+
+A HASH containing the keys and the values of the build properties of the target.
+
+=item status
+
+The status of the target.
+
+=item status_of
+
+A HASH containing the status of dependency types that may be relevant to targets
+higher up in the dependency tree.
+
+=item task
+
+The target type, (i.e. the name of the task to update with the target).
+
+=item triggers
+
+An ARRAY reference of the keys of targets that should be automatically triggered
+by this target.
+
+=item type
+
+The type of the source that gives this target.
+
+=back
+
+In addition, an instance of FCM::Context::Make::Extract::Target has the
+following methods:
+
+=over 4
+
+=item $target->can_be_source()
+
+Returns true if the destination status indicates that the target is usable as a
+source file of a subsequent a make (step).
+
+=item $target->is_ok()
+
+Returns true if the target has a OK destination status.
+
+=item $target->is_failed()
+
+Returns true if the target has a failed destination status.
+
+=item $target->is_modified()
+
+Shorthand for $target->get_status() eq $target->ST_MODIFIED.
+
+=item $target->is_unchanged()
+
+Shorthand for $target->get_status() eq $target->ST_UNCHANGED.
+
+=back
+
+=head1 CONSTANTS
+
+The following is a list of constants:
+
+=over 4
+
+=item FCM::Context::Make::Build->CTX_INPUT
+
+Alias of FCM::Context::Make::Build::Input.
+
+=item FCM::Context::Make::Build->CTX_SOURCE
+
+Alias of FCM::Context::Make::Build::Source.
+
+=item FCM::Context::Make::Build->ID_OF_CLASS
+
+The default value of the "id" attribute (of an instance), and the ID of the
+functional class. ("build")
+
+=item FCM::Context::Make::Build::Target->CT_BIN
+
+Target category, "bin", executable.
+
+=item FCM::Context::Make::Build::Target->CT_ETC
+
+Target category, "etc", data and misc file.
+
+=item FCM::Context::Make::Build::Target->CT_INCLUDE
+
+Target category, "include", include file.
+
+=item FCM::Context::Make::Build::Target->CT_LIB
+
+Target category, "lib", program library.
+
+=item FCM::Context::Make::Build::Target->CT_O
+
+Target category, "o", compiled object file.
+
+=item FCM::Context::Make::Build::Target->CT_SRC
+
+Target category, "src", generated source file.
+
+=item FCM::Context::Make::Build::Target->POLICY_CAPTURE
+
+Indicates that the dependency type is relevant to the target, and the build
+engine should stop floating the dependency target up the dependency tree.
+
+=item FCM::Context::Make::Build::Target->POLICY_FILTER
+
+Indicates that the dependency type is relevant to the target, and the build
+engine may float the dependency target up the dependency tree as well.
+
+=item FCM::Context::Make::Build::Target->POLICY_FILTER_IMMEDIATE
+
+Indicates that the dependency type is relevant to the target but only if the
+dependency target is an immediate dependency of this target, and the build
+engine may float the dependency target up the dependency tree as well.
+
+=item FCM::Context::Make::Build::Target->ST_FAILED
+
+Indicates that the build has failed to update the target.
+
+=item FCM::Context::Make::Build::Target->ST_MODIFIED
+
+Indicates that the target is out of date and has been modified by the build.
+
+=item FCM::Context::Make::Build::Target->ST_OOD
+
+Indicates that the target is out of date.
+
+=item FCM::Context::Make::Build::Target->ST_UNCHANGED
+
+Indicates that the target is up to date and unchanged by the build.
+
+=item FCM::Context::Make::Build::Target->ST_UNKNOWN
+
+Indicates an unknown target status.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Extract.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Extract.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Extract.pm	(revision 5095)
@@ -0,0 +1,494 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Extract;
+use base qw{FCM::Class::HASH};
+
+use FCM::Context::Make;
+
+use constant {
+    CTX_PROJECT => 'FCM::Context::Make::Extract::Project',
+    CTX_SOURCE  => 'FCM::Context::Make::Extract::Source',
+    CTX_TARGET  => 'FCM::Context::Make::Extract::Target',
+    CTX_TREE    => 'FCM::Context::Make::Extract::Tree',
+    ID_OF_CLASS => 'extract',
+    MIRROR      => 1,
+};
+
+__PACKAGE__->class({
+    dest         => '$',
+    id           => {isa => '$', default => ID_OF_CLASS},
+    id_of_class  => {isa => '$', default => ID_OF_CLASS},
+    ns_list      => '@',
+    project_of   => '%',
+    status       => {isa => '$', default => FCM::Context::Make->ST_UNKNOWN},
+    target_of    => '%',
+    prop_of      => '%',
+});
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Extract::Project;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({
+    cache     => '$',
+    inherited => '$',
+    locator   => 'FCM::Context::Locator',
+    ns        => '$',
+    path_excl => '@',
+    path_incl => '@',
+    path_root => {isa => '$', default => q{}},
+    trees     => '@',
+});
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Extract::Tree;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({
+    cache     => '$',
+    inherited => '$',
+    key       => '$',
+    locator   => 'FCM::Context::Locator',
+    ns        => '$',
+    sources   => '@',
+});
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Extract::Source;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    ST_NORMAL    => 'ST_NORMAL',
+    ST_UNCHANGED => 'ST_UNCHANGED',
+    ST_MISSING   => 'ST_MISSING',
+};
+
+__PACKAGE__->class({
+    cache       => '$',
+    key_of_tree => '$',
+    locator     => 'FCM::Context::Locator',
+    ns          => '$',
+    ns_in_tree  => '$',
+    status      => {isa => '$', default => ST_NORMAL},
+});
+
+# Shorthand for $source->get_status() eq $source->ST_MISSING.
+sub is_missing {
+    $_[0]->get_status() eq ST_MISSING;
+}
+
+# Shorthand for $source->get_status() eq $source->ST_UNCHANGED.
+sub is_unchanged {
+    $_[0]->get_status() eq ST_UNCHANGED;
+}
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Extract::Target;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    ST_ADDED      => 'ST_ADDED',
+    ST_DELETED    => 'ST_DELETED',
+    ST_MERGED     => 'ST_MERGED',
+    ST_MODIFIED   => 'ST_MODIFIED',
+    ST_O_ADDED    => 'ST_O_ADDED',
+    ST_O_DELETED  => 'ST_O_DELETED',
+    ST_UNCHANGED  => 'ST_UNCHANGED',
+    ST_UNKNOWN    => 'ST_UNKNOWN',
+    can_be_source => 1,
+};
+
+__PACKAGE__->class({
+    dests            => '@',
+    ns               => '$',
+    path             => '$',
+    source_of        => '%',
+    status           => {isa => '$', default => ST_UNKNOWN},
+    status_of_source => {isa => '$', default => ST_UNKNOWN},
+});
+
+# Returns true if target has an OK status.
+sub is_ok {
+    my ($self) = @_;
+    my $status = $self->get_status();
+    grep {$_ eq $status}
+        (ST_ADDED, ST_MERGED, ST_MODIFIED, ST_O_ADDED, ST_UNCHANGED);
+}
+
+# Shorthand for $target->get_status() eq $target->ST_UNCHANGED.
+sub is_unchanged {
+    $_[0]->get_status() eq ST_UNCHANGED;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Make::Extract
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Make::Extract;
+    my $ctx = FCM::Context::Make::Extract->new();
+
+=head1 DESCRIPTION
+
+Provides a context object for the FCM extract system. All the classes described
+below are sub-classes of L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 OBJECTS
+
+=head2 FCM::Context::Make::Extract
+
+An instance of this class represents an extract. It has the following
+attributes:
+
+=over 4
+
+=item dest
+
+The destination of the extract.
+
+=item id
+
+The ID of the current context. (default="extract")
+
+=item id_of_class
+
+The class ID of the current context. (default="extract")
+
+=item ns_list
+
+An ARRAY of name-spaces of the projects to extract.
+
+=item project_of
+
+A HASH to map (key) the name-spaces of the projects in this extract to (value)
+their corresponding contexts.
+
+=item prop_of
+
+A HASH containing the named properties (i.e. options and settings of named
+external tools). Expects a value to be an instance of
+L<FCM::Context::Make::Share::Property|FCM::Context::Make::Share::Property>.
+
+=item status
+
+The status of the extract. See L<FCM::Context::Make|FCM::Context::Make> for the
+status constants.
+
+=item target_of
+
+A HASH to map (key) the name-spaces of the targets in this extract to (value)
+their corresponding contexts.
+
+=back
+
+=head2 FCM::Context::Make::Extract::Project
+
+An instance of this class represents a project in an extract. It has the
+following attributes:
+
+=over 4
+
+=item cache
+
+The file system location (cache) of this project.
+
+=item inherited
+
+This project is inherited?
+
+=item locator
+
+An instance of L<FCM::Context::Locator|FCM::Context::Locator> that represents
+the locator of this project.
+
+=item ns
+
+The name-space of this project.
+
+=item path_excl
+
+An ARRAY of patterns to match the names of the paths that will be excluded in
+this project.
+
+=item path_incl
+
+An ARRAY of patterns to match the names of the paths that will always be
+included in this project.
+
+=item path_root
+
+The relative path in a project tree for the root name-space. If this is
+specified, the system will extract only files under this path, and their
+name-spaces will be adjusted to be relative to this path.
+
+=item trees
+
+An ARRAY of the tree contexts in this project. By convention, the 0th element is
+the base tree.
+
+=back
+
+=head2 FCM::Context::Make::Extract::Tree
+
+An instance of this class represents a tree in a project. It has the following
+attributes:
+
+=over 4
+
+=item cache
+
+The file system location (cache) of this tree.
+
+=item inherited
+
+A flag to indicate whether this tree is provided by an inherited extract.
+
+=item key
+
+The key of this tree. By convention, the base tree is the 0th key.
+
+=item locator
+
+An instance of L<FCM::Context::Locator|FCM::Context::Locator> that represents
+the locator of this tree.
+
+=item ns
+
+The name-space of the project in which this tree belongs.
+
+=item sources
+
+An ARRAY of source file contexts provided by this tree.
+
+=back
+
+=head2 FCM::Context::Make::Extract::Source
+
+An instance of this class represents a source file provided by a project tree.
+It has the following attributes:
+
+=over 4
+
+=item cache
+
+The file system location (cache) of this source file.
+
+=item key_of_tree
+
+The key of the tree that provides this source file.
+
+=item locator
+
+An instance of L<FCM::Context::Locator|FCM::Context::Locator> that represents
+the locator of the source file.
+
+=item ns
+
+The full (mapped) name-space of the source file, (including the leading project
+name-space).
+
+=item ns_in_tree
+
+The original name-space of the source file, relative to its path in the tree.
+
+=item status
+
+The status of the source file. It can take the value of one of the
+FCM::Context::Make::Extract::Source->ST_* constants. See </CONSTANTS> for detail.
+
+=back
+
+In addition, an instance of FCM::Context::Make::Extract::Source has the
+following methods:
+
+=over 4
+
+=item $source->is_missing()
+
+Shorthand for $source->get_status() eq $source->ST_MISSING.
+
+=item $source->is_unchanged()
+
+Shorthand for $source->get_status() eq $source->ST_UNCHANGED.
+
+=back
+
+=head2 FCM::Context::Make::Extract::Target
+
+An instance of this class represents an extract target. It has the following
+attributes:
+
+=over 4
+
+=item dests
+
+An ARRAY containing the destination search path of this target. The first
+element is the path to the destination of the current extract, and the rest are
+destinations to inherited extracts.
+
+=item ns
+
+The full name-space of this target.
+
+=item path
+
+Returns the actual destination path of this target.
+
+=item source_of
+
+A HASH for mapping (key) the keys of the trees to (value) the corresponding
+contexts of the source files provided by the trees to this target.
+
+=item status
+
+The status of the target destination. It can take the value of one of the
+FCM::Context::Make::Extract::Target->ST_* constants. See </CONSTANTS> for detail.
+
+=item status_of_source
+
+The status of the target, with respect to its sources. It can take the value of
+one of the FCM::Context::Make::Extract::Target->ST_* constants. See </CONSTANTS>
+for detail.
+
+=back
+
+In addition, an instance of FCM::Context::Make::Extract::Target has the
+following methods:
+
+=over 4
+
+=item $target->can_be_source()
+
+Returns true if the destination status indicates that the target is usable as a
+source file of a subsequent a make (step).
+
+=item $target->is_ok()
+
+Returns true if the target has a OK destination status.
+
+=item $target->is_unchanged()
+
+Shorthand for $target->get_status() eq $target->ST_UNCHANGED.
+
+=back
+
+=head1 CONSTANTS
+
+The following is a list of constants:
+
+=over 4
+
+=item FCM::Context::Make::Extract->CTX_PROJECT
+
+An alias to FCM::Context::Make::Extract::Project.
+
+=item FCM::Context::Make::Extract->CTX_SOURCE
+
+An alias to FCM::Context::Make::Extract::Source.
+
+=item FCM::Context::Make::Extract->CTX_TARGET
+
+An alias to FCM::Context::Make::Extract::Target.
+
+=item FCM::Context::Make::Extract->CTX_TREE
+
+An alias to FCM::Context::Make::Extract::Tree.
+
+=item FCM::Context::Make::Extract->ID_OF_CLASS
+
+The default value of the "id" attribute (of an instance), and the ID of the
+functional class. ("extract")
+
+=item FCM::Context::Make::Extract->MIRROR
+
+A flag to tell the mirror sub-system that the targets of this context can be
+used as inputs sources to subsequent steps for the configuration file in the
+mirror destination.
+
+=item FCM::Context::Make::Extract::Source->ST_NORMAL
+
+Source status: normal.
+
+=item FCM::Context::Make::Extract::Source->ST_UNCHANGED
+
+Source status: source is unchanged (against base).
+
+=item FCM::Context::Make::Extract::Source->ST_MISSING
+
+Source status: source is a placeholder in a target. It does not actually exist
+in the source tree.
+
+=item FCM::Context::Make::Extract::Target->ST_ADDED
+
+As destination status: new file in the target destination. As source status:
+added by a source in a diff tree.
+
+=item FCM::Context::Make::Extract::Target->ST_DELETED
+
+As destination status: file removed from the target destination. As source
+status: target removed by a diff tree.
+
+=item FCM::Context::Make::Extract::Target->ST_MERGED
+
+As source status: modified by 2 or more diff trees.
+
+=item FCM::Context::Make::Extract::Target->ST_MODIFIED
+
+As destination status: target destination is modified. As source status:
+modified by 1 diff tree.
+
+=item FCM::Context::Make::Extract::Target->ST_O_ADDED
+
+As destination status: new file in the target destination, overriding a file in
+an inherited destination.
+
+=item FCM::Context::Make::Extract::Target->ST_O_DELETED
+
+As destination status: target destination should be removed, but there is still
+a file in an inherited destination.
+
+=item FCM::Context::Make::Extract::Target->ST_UNCHANGED
+
+As destination status: target destination is unchanged. As source status:
+unchanged by a diff tree.
+
+=item FCM::Context::Make::Extract::Target->ST_UNKNOWN
+
+Status is unknown.
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM::System::Make::Extract|FCM::System::Make::Extract>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Mirror.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Mirror.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Mirror.pm	(revision 5095)
@@ -0,0 +1,119 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Mirror;
+use base qw{FCM::Class::HASH};
+
+use FCM::Context::Make;
+
+use constant {ID_OF_CLASS => 'mirror'};
+
+__PACKAGE__->class({
+    dest           => '$',
+    id             => {isa => '$', default => ID_OF_CLASS},
+    id_of_class    => {isa => '$', default => ID_OF_CLASS},
+    prop_of        => '%',
+    status         => {isa => '$', default => FCM::Context::Make->ST_UNKNOWN},
+    target_logname => '$',
+    target_machine => '$',
+    target_path    => '$',
+});
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Make::Mirror
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Make::Mirror;
+    my $ctx = FCM::Context::Make::Mirror->new();
+    $ctx->set_dest_path($dest_path);
+    $ctx->set_source_path($source_path);
+    # ...
+
+=head1 DESCRIPTION
+
+Provides a context object for the mirror sub-system.
+
+=head1 ATTRIBUTES
+
+This class is based on L<FCM::Class::HASH|FCM::Class::HASH>. All attributes are
+accessible via $ctx->get_$attrib() and $ctx->set_$attrib($value) methods.
+
+=over 4
+
+=item dest
+
+The local working directory for the mirror sub-system.
+
+=item id
+
+The ID of the context. (default="mirror")
+
+=item id_of_class
+
+The class ID of the context. (default="mirror")
+
+=item prop_of
+
+A HASH containing the named properties (i.e. options and settings of named
+external tools). Expects a value to be an instance of
+L<FCM::Context::Make::Share::Property|FCM::Context::Make::Share::Property>.
+
+=item status
+
+The status of the context. See L<FCM::Context::Make|FCM::Context::Make> for the
+status constants.
+
+=item target_logname
+
+The logname part of the authority of the mirror destination.
+
+=item target_machine
+
+The machine part of the authority of the mirror destination.
+
+=item target_path
+
+The container path of the mirror destination (without the authority).
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item ID_OF_CLASS
+
+The default value of the "id" attribute (of an instance), and the ID of the
+functional class. ("mirror")
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Share/Property.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Share/Property.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Make/Share/Property.pm	(revision 5095)
@@ -0,0 +1,138 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Share::Property;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    CTX_VALUE  => 'FCM::Context::Make::Share::Property::Value',
+    NS_OF_ROOT => q{},
+};
+
+__PACKAGE__->class({ctx_of => '%', id => '$'});
+
+sub get_ctx {
+    $_[0]->get_ctx_of(NS_OF_ROOT);
+}
+
+sub set_ctx {
+    $_[0]->get_ctx_of()->{$_[0]->NS_OF_ROOT} = $_[1];
+}
+
+# ------------------------------------------------------------------------------
+package FCM::Context::Make::Share::Property::Value;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({inherited => '$', value => '$'});
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Make::Share::Property
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Make::Share::Property;
+    $prop = FCM::Context::Make::Share::Property->new(\%attrib);
+
+=head1 DESCRIPTION
+
+Provides a context object to store the property of a named shell command.
+
+=head1 OBJECTS
+
+The classes described below are all sub-classes of
+L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head2 FCM::Context::Make::Share::Property
+
+This class represents a property. It has the following attributes:
+
+=over 4
+
+=item ctx_of
+
+A HASH to map (keys) the name-spaces to (values) the contexts of this property.
+Expects each context to be an instance of
+L</FCM::Context::Make::Share::Property::Value>.
+
+The context of a simple property is stored in the root (i.e. the empty string)
+name-space.
+
+=item id
+
+The ID of this property.
+
+=back
+
+An instance of FCM::Context::Make::Share::Property has 2 additional methods:
+
+=over 4
+
+=item $instance->get_ctx()
+
+Shorthand for:
+
+    $instance->get_ctx_of(q{}).
+
+=item $instance->set_ctx($ctx)
+
+Shorthand for:
+
+    $instance->get_ctx_of()->{q{}} = $ctx.
+
+=back
+
+=head2 FCM::Context::Make::Share::Property::Value
+
+This class represents a property value (associated with a name-space). It has
+the following attributes:
+
+=over 4
+
+=item inherited
+
+A flag, if true, indicates that this value is inherited.
+
+=item value
+
+The value.
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item FCM::Context::Make::Share::Property->NS_OF_ROOT
+
+The root name-space, an empty string.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Task.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Task.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Context/Task.pm	(revision 5095)
@@ -0,0 +1,105 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::Context::Task;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    ST_FAILED  => 'ST_FAILED',
+    ST_OK      => 'ST_OK',
+    ST_WORKING => 'ST_WORKING',
+};
+
+__PACKAGE__->class({
+    ctx      => {},
+    error    => {},
+    id       => '$',
+    elapse   => {isa => '$', default => 0},
+    state    => {isa => '$', default => ST_WORKING},
+});
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Context::Task
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Task;
+    my $task = FCM::Context::Task->new(\%attrib);
+
+=head1 DESCRIPTION
+
+An instance of this class represents the generic context for a task for the
+L<FCM::Util->task_runner()|FCM::Util>. This class is a sub-class of
+L<FCM::Class::HASH|FCM::Class::HASH>.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item ctx
+
+The specific context of the task, such as the inputs and the outputs.
+
+=item error
+
+If the task failed, the error/exception will be returned in this attribute.
+
+=item id
+
+The ID of the task.
+
+=item elapse
+
+The amount of time (in seconds) taken to run the task.
+
+=item state
+
+The state of the task. See L</CONSTANTS> for possible variables.
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item FCM::Context::Task->ST_FAILED
+
+A status to indicate that the task has failed.
+
+=item FCM::Context::Task->ST_OK
+
+A status to indicate that the task is completed successfully.
+
+=item FCM::Context::Task->ST_WORKING
+
+A status to indicate that the task is bing worked on.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Exception.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Exception.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Exception.pm	(revision 5095)
@@ -0,0 +1,113 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Exception;
+
+use Data::Dumper qw{Dumper};
+use Scalar::Util qw{blessed};
+use overload (q{""} => \&Dumper);
+
+use constant {
+    DEFAULT => 'DEFAULT',
+};
+
+# Returns true if $e is a blessed instance of $class.
+sub caught {
+    my ($class, $e) = @_;
+    return (blessed($e) && $e->isa($class));
+}
+
+# Throws the exception.
+sub throw {
+    my ($class, $code, $ctx, $e) = @_;
+    if (defined($e) && !ref($e) && $e =~ qr{\A\s*\z}msx) {
+        $e = undef;
+    }
+    die(bless({code => $code, ctx => $ctx, exception => $e}, $class));
+}
+
+# Attribute accessors.
+for my $name (qw{code ctx exception}) {
+    no strict qw{refs};
+    *{"get_$name"} = sub {$_[0]->{$name}};
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Exception
+
+=head1 SYNOPSIS
+
+    use FCM::Exception;
+    my $E = 'FCM::Exception';
+    eval {
+        # ...
+        if ($some_error_condition) {
+            return $E->throw($code, $ctx);
+        }
+        # ...
+    };
+    if (my $e = $@) {
+        if ($E->caught($e)) {
+            # ...
+        }
+    }
+
+=head1 DESCRIPTION
+
+Exception associated with an FCM operation.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->caught($e)
+
+Returns true if $e is a blessed object of $class.
+
+=item $class->throw($code,$ctx,$e)
+
+Creates an instance and die() with it.
+
+=item $e->get_code()
+
+Returns the code associated with this exception.
+
+=item $e->get_ctx()
+
+Returns the context associated with this exception.
+
+=item $e->get_exception()
+
+Returns the exception that generates this exception.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System.pm	(revision 5095)
@@ -0,0 +1,286 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+
+package FCM::System;
+use base qw{FCM::Class::CODE};
+
+use FCM::Util;
+use Scalar::Util qw{reftype};
+
+# Alias
+our $S;
+
+# The (keys) named actions of this class and (values) their implementations.
+our %ACTION_OF = (
+    browse               => _func('misc', sub {$S->browse(@_)}),
+    build                => _func('old' , sub {$S->build(@_)}),
+    config_compare       => _func('old' , sub {$S->config_compare(@_)}),
+    config_parse         => _func('misc', sub {$S->config_parse(@_)}),
+    cm_branch_create     => _func('cm'  , sub {$S->cm_branch_create(@_)}),
+    cm_branch_delete     => _func('cm'  , sub {$S->cm_branch_delete(@_)}),
+    cm_branch_diff       => _func('cm'  , sub {$S->cm_branch_diff(@_)}),
+    cm_branch_info       => _func('cm'  , sub {$S->cm_branch_info(@_)}),
+    cm_branch_list       => _func('cm'  , sub {$S->cm_branch_list(@_)}),
+    cm_commit            => _func('cm'  , sub {$S->cm_commit(@_)}),
+    cm_checkout          => _func('cm'  , sub {$S->cm_checkout(@_)}),
+    cm_check_missing     => _func('cm'  , sub {$S->cm_check_missing(@_)}),
+    cm_check_unknown     => _func('cm'  , sub {$S->cm_check_unknown(@_)}),
+    cm_diff              => _func('cm'  , sub {$S->cm_diff(@_)}),
+    cm_loc_layout        => _func('cm'  , sub {$S->cm_loc_layout(@_)}),
+    cm_merge             => _func('cm'  , sub {$S->cm_merge(@_)}),
+    cm_mkpatch           => _func('cm'  , sub {$S->cm_mkpatch(@_)}),
+    cm_project_create    => _func('cm'  , sub {$S->cm_project_create(@_)}),
+    cm_resolve_conflicts => _func('cm'  , sub {$S->cm_resolve_conflicts(@_)}),
+    cm_switch            => _func('cm'  , sub {$S->cm_switch(@_)}),
+    cm_update            => _func('cm'  , sub {$S->cm_update(@_)}),
+    export_items         => _func('misc', sub {$S->export_items(@_)}),
+    extract              => _func('old' , sub {$S->extract(@_)}),
+    keyword_find         => _func('misc', sub {$S->keyword_find(@_)}),
+    make                 => _func('make', sub {$S->main(@_)}),
+    svn                  => _func('cm'  , sub {$S->svn(@_)}),
+    util                 => sub {$_[0]->{util}},
+);
+# The (keys) named system and their implementation classes.
+our %SYSTEM_CLASS_OF = (
+    cm   => 'FCM::System::CM',
+    old  => 'FCM::System::Old',
+    make => 'FCM::System::Make',
+    misc => 'FCM::System::Misc',
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   gui             => '$',
+        system_class_of => {isa => '%', default => {%SYSTEM_CLASS_OF}},
+        system_of       => '%',
+        util            => '&',
+    },
+    {init => \&_init, action_of => {%ACTION_OF}},
+);
+
+# Initialises attributes.
+sub _init {
+    my $attrib_ref = shift();
+    $attrib_ref->{util} = FCM::Util->new();
+}
+
+# Generates main functions.
+sub _func  {
+    my ($name, $code_ref) = @_;
+    sub {
+        my ($attrib_ref, @args) = @_;
+        if (!defined($attrib_ref->{system_of}{$name})) {
+            my $class_name = $attrib_ref->{system_class_of}{$name};
+            $attrib_ref->{util}->class_load($class_name);
+            $attrib_ref->{system_of}{$name} = $class_name->new({
+                gui  => $attrib_ref->{gui},
+                util => $attrib_ref->{util},
+            });
+        }
+        local($S) = $attrib_ref->{system_of}{$name};
+        $code_ref->(@args);
+    };
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System
+
+=head1 SYNOPSIS
+
+    use FCM::System;
+    $fcm = FCM::System->new();
+    # ...
+    $fcm->make(\%option, @args);
+
+=head1 DESCRIPTION
+
+Provides a top level interface to access the functionalities of the FCM system.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. It also initialises the utility and sub-system classes.
+The %attrib hash can be used configure the behaviour of the instance:
+
+=over 4
+
+=item event
+
+A CODE to handle event.
+
+=item gui
+
+The GUI geometry of "fcm gui-internal".
+
+=item system_class_of
+
+A HASH to map (keys) sub-system names to (values) their implementation classes.
+See %FCM::System::SYSTEM_CLASS_OF.
+
+=item system_of
+
+A HASH to map (keys) sub-system names to (values) their implementation instances.
+
+=item util
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $fcm->browse(\%option,@args)
+
+Invokes a browser to browse the sources in @args.
+
+=item $fcm->build(\%option,@args)
+
+(Obsolete) Invokes the FCM 1 build system.
+
+=item $fcm->config_compare(\%option,@args)
+
+(Obsolete) Compares 2 FCM 1 extract configuration files.
+
+=item $fcm->config_parse(\%option,@args)
+
+Parses a configuration file.
+
+=item $fcm->cm_branch_create(\%option,@args)
+
+Creates of a branch in a project in a Subversion repository with a standard FCM
+layout.
+
+=item $fcm->cm_branch_delete(\%option,@args)
+
+Deletes of a branch in a project in a Subversion repository with a standard FCM
+layout.
+
+=item $fcm->cm_branch_diff(\%option,@args)
+
+Displays the changes between a branch and its parent in a project in a
+Subversion repository with a standard FCM layout.
+
+=item $fcm->cm_branch_info(\%option,@args)
+
+Displays information of a branch in a project in a Subversion repository with a
+standard FCM layout.
+
+=item $fcm->cm_branch_list(\%option,@args)
+
+Lists branches in a project in a Subversion repository with a standard FCM
+layout.
+
+=item $fcm->cm_commit(\%option,@args)
+
+Wraps C<svn commit>.
+
+=item $fcm->cm_checkout(\%option,@args)
+
+Wraps C<svn checkout>.
+
+=item $fcm->cm_check_missing(\%option,@args)
+
+Checks for missing status in a Subversion working copy.
+
+=item $fcm->cm_check_unknown(\%option,@args)
+
+Checks for unknown status in a Subversion working copy.
+
+=item $fcm->cm_diff(\%option,@args)
+
+Wraps C<svn diff>.
+
+=item $fcm->cm_loc_layout(\%option,@args)
+
+Parse and print layout information of each target in @args.
+
+=item $fcm->cm_merge(\%option,@args)
+
+Wraps C<svn merge>.
+
+=item $fcm->cm_mkpatch(\%option,@args)
+
+Creates FCM patches.
+
+=item $fcm->cm_project_create(\%option,@args)
+
+Create a new project in a Subversion repository.
+
+=item $fcm->cm_resolve_conflicts(\%option,@args)
+
+Invokes a graphic merge tool to resolve conflicts.
+
+=item $fcm->cm_switch(\%option,@args)
+
+Wraps C<svn switch>.
+
+=item $fcm->cm_update(\%option,@args)
+
+Wraps C<svn update>.
+
+=item $fcm->export_items(\%option,@args)
+
+Exports directories as versioned items in a branch of a project in a Subversion
+repository with the standard FCM layout.
+
+=item $fcm->extract(\%option,@args)
+
+(Obsolete) Invokes the FCM 1 extract system.
+
+=item $fcm->keyword_find(\%option,@args)
+
+If @args is empty, search for all known FCM location keyword entries. Otherwise,
+search for FCM location keyword entries matching the locations specified in
+@args.
+
+=item $fcm->make(\%option,@args)
+
+Invokes the FCM make system.
+
+=item $fcm->svn(\%option,@args)
+
+Invokes C<svn> with @args. %option is ignored.
+
+=item $fcm->util()
+
+Returns the L<FCM::Util|FCM::Util> object.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=head2 FCM::System::Exception
+
+This exception is a sub-class of L<FCM::Exception|FCM::Exception> and is thrown
+by methods of this class on error.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM.pm	(revision 5095)
@@ -0,0 +1,715 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::CM;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd};
+use FCM1::Cm;
+use FCM1::Interactive;
+use FCM::Context::Event;
+use FCM::Context::Locator;
+use FCM::System::CM::CommitMessage;
+use FCM::System::CM::Prompt;
+use FCM::System::CM::ResolveConflicts qw{_cm_resolve_conflicts};
+use FCM::System::CM::SVN;
+use FCM::System::Exception;
+use FCM::Util::Exception;
+use File::Spec::Functions qw{catfile};
+use List::Util qw{first};
+use Storable qw{dclone};
+
+# The (keys) named actions of this class and (values) their implementations.
+our %ACTION_OF = (
+    cm_branch_create     => \&_cm_branch_create,
+    cm_branch_delete     => _fcm1_func(\&FCM1::Cm::cm_branch_delete),
+    cm_branch_diff       => _fcm1_func(\&FCM1::Cm::cm_branch_diff),
+    cm_branch_info       => _fcm1_func(\&FCM1::Cm::cm_branch_info),
+    cm_branch_list       => \&_cm_branch_list,
+    cm_commit            => _fcm1_func(\&FCM1::Cm::cm_commit),
+    cm_checkout          => \&_cm_checkout,
+    cm_check_missing     => _fcm1_func(
+        \&FCM1::Cm::cm_check_missing,
+        _opt_mod_st_check_handler_func('WC_STATUS_PATH'),
+    ),
+    cm_check_unknown     => _fcm1_func(
+        \&FCM1::Cm::cm_check_unknown,
+        _opt_mod_st_check_handler_func('WC_STATUS_PATH'),
+    ),
+    cm_diff              => \&_cm_diff,
+    cm_loc_layout        => \&_cm_loc_layout,
+    cm_merge             => _fcm1_func(\&FCM1::Cm::cm_merge),
+    cm_mkpatch           => _fcm1_func(\&FCM1::Cm::cm_mkpatch),
+    cm_project_create    => \&_cm_project_create,
+    cm_resolve_conflicts => \&_cm_resolve_conflicts,
+    cm_switch            => _fcm1_func(
+        \&FCM1::Cm::cm_switch, _opt_mod_st_check_handler_func('WC_STATUS'),
+    ),
+    cm_update            => _fcm1_func(
+        \&FCM1::Cm::cm_update, _opt_mod_st_check_handler_func('WC_STATUS'),
+    ),
+    svn                  => \&_svn,
+);
+
+# Alias
+my $E = 'FCM::System::Exception';
+
+# Creates the class.
+__PACKAGE__->class(
+    {   commit_message_util => '&',
+        gui                 => '$',
+        prompt              => '&',
+        svn                 => '&',
+        util                => '&',
+    },
+    {init => \&_init, action_of => \%ACTION_OF},
+);
+
+sub _init {
+    my ($attrib_ref) = @_;
+    if (!defined(FCM1::Keyword::get_util())) {
+        FCM1::Keyword::set_util($attrib_ref->{util});
+    }
+    if ($attrib_ref->{'gui'}) {
+        FCM1::Interactive::set_impl(
+            'FCM1::Interactive::InputGetter::GUI',
+            {geometry => $attrib_ref->{gui}},
+        );
+    }
+    $attrib_ref->{prompt} = FCM::System::CM::Prompt->new({
+        gui => $attrib_ref->{gui}, util => $attrib_ref->{util},
+    });
+    $attrib_ref->{commit_message_util} = FCM::System::CM::CommitMessage->new({
+        gui  => $attrib_ref->{gui},
+        util => $attrib_ref->{util},
+    });
+    $attrib_ref->{svn} = FCM::System::CM::SVN->new({util => $attrib_ref->{util}});
+    FCM1::Cm::set_util($attrib_ref->{util});
+    FCM1::Cm::set_commit_message_util($attrib_ref->{commit_message_util});
+    FCM1::Cm::set_svn_util($attrib_ref->{svn});
+}
+
+# Create a branch in a project.
+sub _cm_branch_create {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    _parse_args($attrib_ref, $option_ref, \@args);
+    my ($name, $source) = @args;
+    # Check branch name
+    if (!$name || $name !~ qr{\A[\w\.\-/]+\z}msx) {
+        return $E->throw($E->CM_BRANCH_NAME, $name ? $name : q{});
+    }
+    # Determine ticket list with name
+    if (!$option_ref->{ticket} && $name =~ qr{\A[1-9]\d*([_\-][1-9]\d*)*\z}msx) {
+        $option_ref->{ticket} = [split(qr{[_\-]}msx, $name)];
+    }
+    # Check source
+    $source ||= cwd() . '@HEAD';
+    my $layout = $attrib_ref->{svn}->get_layout($source);
+    my $root = $layout->get_root();
+    my $source_rev = $layout->get_peg_rev();
+    my $project = $layout->get_project();
+    my $source_branch = $layout->get_branch();
+    if (!defined($project)) {
+        return $E->throw($E->CM_BRANCH_SOURCE, $source);
+    }
+    my @project_paths = split(qr{/}msx, $project);
+
+    # Determine whether to create a branch of a branch
+    if (!$option_ref->{'branch-of-branch'} || !$source_branch) {
+        $source_branch = 'trunk';
+    }
+    $source = join('/', $root, @project_paths, $source_branch)
+        . '@' . $source_rev;
+    my $source_commit_rev
+        = $attrib_ref->{svn}->get_info($source)->[0]->{'commit:revision'};
+    $source = join('/', $root, @project_paths, $source_branch)
+        . '@' . $source_commit_rev;
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->CM_BRANCH_CREATE_SOURCE, $source, $source_rev,
+    );
+
+    # Handle multiple tickets
+    $option_ref->{ticket} ||= [];
+    $option_ref->{ticket} = [
+        sort
+            {$a <=> $b}
+        map
+            {s{\A#}{}msx; $_}
+        split(qr{,}msx, join(q{,}, @{$option_ref->{ticket}}))
+    ];
+
+    # Determine the sub-directory names of the branch
+    # FIXME: hard coded legacy!
+    my %layout_config = %{$layout->get_config()};
+    my @names;
+    if ($layout_config{'template-branch'}) {
+        my $template = $layout_config{'template-branch'};
+        if (    index($template, '{category}') >= 0
+            ||  index($template, '{owner}') >= 0
+        ) {
+            $option_ref->{type} ||= 'dev::user';
+            $option_ref->{type} = lc($option_ref->{type});
+            $option_ref->{type}
+                = $option_ref->{type} eq 'user'   ? 'dev::user'
+                : $option_ref->{type} eq 'share'  ? 'dev::share'
+                : $option_ref->{type} eq 'config' ? 'pkg::config'
+                : $option_ref->{type} eq 'rel'    ? 'pkg::rel'
+                : $option_ref->{type} eq 'dev'    ? 'dev::user'
+                : $option_ref->{type} eq 'test'   ? 'test::user'
+                : $option_ref->{type} eq 'pkg'    ? 'pkg::user'
+                :                                   $option_ref->{type}
+                ;
+            if (!grep {$option_ref->{type} eq $_} qw{
+                dev::share dev::user test::share test::user
+                pkg::config pkg::rel  pkg::share  pkg::user
+            }) {
+                return $E->throw($E->CM_OPT_ARG, ['type', $option_ref->{type}]);
+            }
+            my %set = map {$_ => 1} split('::', $option_ref->{type});
+            if (index($template, '{category}') >= 0) {
+                my $index = index($template, '{category}');
+                my $category = first {exists($set{$_})} qw{dev test pkg};
+                substr($template, $index, length('{category}'), $category);
+            }
+            if (index($template, '{owner}') >= 0) {
+                my $index = index($template, '{owner}');
+                my $owner = exists($set{user})
+                    ? $attrib_ref->{svn}->get_username($root)
+                    : first {exists($set{lc($_)})} qw{Share Config Rel};
+                substr($template, $index, length('{owner}'), $owner);
+            }
+        }
+        if (index($template, '{name_prefix}') >= 0) {
+            my $index = index($template, '{name_prefix}');
+            # Check revision flag is valid
+            $option_ref->{'rev-flag'} ||= 'normal';
+            $option_ref->{'rev-flag'} = lc($option_ref->{'rev-flag'});
+            if (!grep {$_ eq $option_ref->{'rev-flag'}} qw{normal number none}) {
+                return $E->throw(
+                    $E->CM_OPT_ARG, ['rev-flag', $option_ref->{'rev-flag'}]);
+            }
+            my $name_prefix = q{};
+            if ($option_ref->{'rev-flag'} ne 'none') {
+                $name_prefix = 'r' . $source_commit_rev;
+                if ($option_ref->{'rev-flag'} eq 'normal') {
+                    # Attempt to replace revision number with a keyword
+                    my $locator = FCM::Context::Locator->new($source);
+                    my $as_keyword = $attrib_ref->{util}->loc_as_keyword($locator);
+                    my ($u, $r) = $attrib_ref->{svn}->split_by_peg($as_keyword);
+                    if ($source_commit_rev ne $r) {
+                        $name_prefix = $r;
+                    }
+                }
+
+                # Add an underscore
+                $name_prefix .= '_';
+            }
+            substr($template, $index, length('{name_prefix}'), $name_prefix);
+        }
+        if (index($template, '{name}') >= 0) {
+            my $index = index($template, '{name}');
+            substr($template, $index, length('{name}'), $name);
+        }
+        push(@names, split(qr{/+}msx, $template));
+    }
+    else {
+        push(@names, split(qr{/+}msx, $name));
+    }
+    if ($layout_config{'depth-branch'} != scalar(@names)) {
+        return $E->throw($E->CM_BRANCH_NAME, join('/', @names));
+    }
+    if ($layout_config{'dir-branch'}) {
+        unshift(@names, $layout_config{'dir-branch'});
+    }
+    # Check whether the branch already exists
+    my $target = join('/', $root, @project_paths, @names);
+    my $target_url = eval {$attrib_ref->{svn}->get_info($target)->[0]->{url}};
+    $@ = undef;
+    if ($target_url) {
+        return $E->throw($E->CM_ALREADY_EXIST, $target_url);
+    }
+
+    # Message for the commit log
+    my @tickets = @{$option_ref->{ticket}};
+    my @message = sprintf('%sCreated %s from %s@%d.' . "\n",
+        (@tickets ? join(q{,}, map {'#' . $_} @tickets) . q{: } : q{}),
+        join('/', q{}, @project_paths, @names),
+        join('/', q{}, @project_paths, $source_branch), $source_commit_rev,
+    );
+
+    # Create a temporary file for the commit log message
+    my $commit_message_ctx = $attrib_ref->{commit_message_util}->ctx();
+    $commit_message_ctx->set_auto_part(join(q{}, @message));
+    $commit_message_ctx->set_info_part(sprintf("%s    %s\n", 'A', $target));
+    if (!$option_ref->{'non-interactive'}) {
+        $attrib_ref->{commit_message_util}->edit($commit_message_ctx);
+    }
+    $attrib_ref->{commit_message_util}->notify($commit_message_ctx);
+    my $temp_handle
+        = $attrib_ref->{commit_message_util}->temp($commit_message_ctx);
+
+    # Check with the user to see if he/she wants to go ahead
+    if (    !$option_ref->{'non-interactive'}
+        &&  !$attrib_ref->{prompt}->question('BRANCH_CREATE')
+    ) {
+        return;
+    }
+
+    # Create the branch
+    $attrib_ref->{svn}->call(
+        'copy',
+        '--file', $temp_handle->filename(),
+        '--parents',
+        ($option_ref->{'svn-non-interactive'} ? '--non-interactive' : ()),
+        (   defined($option_ref->{'password'})
+            ? ('--password', $option_ref->{'password'}) : ()
+        ),
+        $source,
+        $target,
+    );
+    $attrib_ref->{util}->event(FCM::Context::Event->CM_CREATE_TARGET, $target);
+
+    # Switch working copy to point to newly created branch
+    if ($option_ref->{'switch'}) {
+        $ACTION_OF{'cm_switch'}->($attrib_ref, $option_ref, $target);
+    }
+
+    $target;
+}
+
+# Filter lists branches in projects.
+sub _cm_branch_list {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    _parse_args($attrib_ref, $option_ref, \@args);
+    my $used_default_arg;
+    if (!@args) {
+        @args = cwd() . '@HEAD';
+        $used_default_arg = 1;
+    }
+    my %common_patterns_at;
+    if ($option_ref->{'only'} && @{$option_ref->{'only'}}) {
+        for (@{$option_ref->{'only'}}) {
+            my ($depth, $pattern) = split(qr{:}msx, $_, 2);
+            $common_patterns_at{$depth} ||= [];
+            push(@{$common_patterns_at{$depth}}, $pattern);
+        }
+    }
+    my $UTIL = $attrib_ref->{'util'};
+    ARG:
+    for my $arg (@args) {
+        my %patterns_at = %{dclone(\%common_patterns_at)};
+        my %info = eval {%{$attrib_ref->{svn}->get_info($arg)->[0]}};
+        if ($@) {
+            if ($used_default_arg) {
+                # Can't complain about a bad arg if we put it there.
+                return $E->throw($E->SHELL, $@->{ctx}, $@->{ctx}->{e});
+            }
+            return $E->throw($E->CM_ARG, $arg);
+        }
+        my $url = $info{'url'} . '@' . $info{'revision'};
+        my $layout = $attrib_ref->{svn}->get_layout($url);
+        my $root = $layout->get_root();
+        my $rev = $layout->get_peg_rev();
+        my $project = $layout->get_project();
+        if (!defined($project)) {
+            next ARG;
+        }
+        my $url_project = $root . ($project ? '/' . $project : q{});
+        my %layout_config = %{$layout->get_config()};
+        if ($layout_config{'level-owner-branch'} && !$option_ref->{'show-all'}) {
+            my $level = $layout_config{'level-owner-branch'};
+            if ($option_ref->{'user'} && @{$option_ref->{'user'}}) {
+                $patterns_at{$level} = [
+                    map {'^' . $_ . '$'}
+                    map {split(qr{[,:]}msx, $_)}
+                    @{$option_ref->{'user'}}
+                ];
+            }
+            elsif (!%patterns_at) {
+                my $owner = $attrib_ref->{svn}->get_username($root);
+                $patterns_at{$level} = ['^' . $owner . '$'];
+            }
+        }
+        my $url0 = $url_project;
+        if ($layout_config{'dir-branch'}) {
+            $url0 .= '/' . $layout_config{'dir-branch'};
+        }
+        else {
+            for my $key (qw{trunk tag}) {
+                if ($layout_config{"dir-$key"}) {
+                    $patterns_at{1} ||= [];
+                    push(
+                        @{$patterns_at{1}},
+                        '^(?!' . $layout_config{"dir-$key"} .  '$)',
+                    );
+                }
+            }
+        }
+        my @branches = $attrib_ref->{svn}->get_list(
+            $url0 . '@' . $rev,
+            sub {
+                my ($this_url, $this_name, $is_dir, $depth) = @_;
+                if (    exists($patterns_at{$depth})
+                    &&  !grep {$this_name =~ /$_/} @{$patterns_at{$depth}}
+                ) {
+                    return (0, 0);
+                }
+                my $can_return = $depth >= $layout_config{'depth-branch'};
+                ($can_return, ($is_dir && !$can_return));
+            },
+        );
+        if ($option_ref->{'url'}) {
+            $UTIL->event(
+                FCM::Context::Event->CM_BRANCH_LIST,
+                $url_project . '@' . $rev, @branches,
+            );
+        }
+        else {
+            $UTIL->event(
+                FCM::Context::Event->CM_BRANCH_LIST,
+                map {$UTIL->loc_as_keyword(FCM::Context::Locator->new($_))}
+                    ($url_project . '@' . $rev, @branches),
+            );
+        }
+    }
+}
+
+# Wraps "svn checkout".
+sub _cm_checkout {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    _parse_args($attrib_ref, $option_ref, \@args);
+    my $target = @args && !$attrib_ref->{util}->uri_match($args[-1])
+        ? $args[-1] : cwd();
+    my $info_entry = eval {$attrib_ref->{svn}->get_info($target)->[0]};
+    if ($@) {
+        $@ = undef; # OK, not a working copy
+    }
+    elsif (grep {index($_, 'wc-info:') == 0} keys(%{$info_entry})) {
+        return $E->throw($E->CM_CHECKOUT, [$target, $info_entry->{url}]);
+    }
+    $attrib_ref->{svn}->call('checkout', @args);
+}
+
+# Wraps "svn diff".
+sub _cm_diff {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    _parse_args($attrib_ref, $option_ref, \@args);
+    local(%ENV) = %ENV;
+    $ENV{FCM_GRAPHIC_DIFF}
+        ||= $attrib_ref->{util}->external_cfg_get('graphic-diff');
+    $attrib_ref->{svn}->call('diff', @args);
+}
+
+# Parse and print layout information of each target in @args.
+sub _cm_loc_layout {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    _parse_args($attrib_ref, $option_ref, \@args);
+    if (!@args) {
+        @args = qw{.};
+    }
+    my $OUT = sub {
+        $attrib_ref->{util}->event(FCM::Context::Event->OUT, @_);
+    };
+    my $not_first;
+    for my $arg (@args) {
+        if ($not_first) {
+            $OUT->("\n");
+        }
+        $not_first = 1;
+        $OUT->("target: $arg\n");
+        my $layout = $attrib_ref->{svn}->get_layout($arg);
+        $OUT->($layout->as_string());
+    }
+}
+
+# Create a new project in a repository.
+sub _cm_project_create {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    _parse_args($attrib_ref, $option_ref, \@args);
+    my ($name, $root_arg) = @args;
+    # Check project name
+    if (!$name || $name !~ qr{\A[\w\.\-/]+\z}msx) {
+        return $E->throw($E->CM_PROJECT_NAME, $name);
+    }
+    # Check root
+    if (!$root_arg) {
+        return $E->throw($E->CM_REPOSITORY, q{});
+    }
+    my $layout = $attrib_ref->{svn}->get_layout($root_arg);
+    my $root = $layout->get_root();
+    if (!$root) {
+        return $E->throw($E->CM_REPOSITORY, $root_arg);
+    }
+
+    # Check whether the depth of the project name is valid
+    my %layout_config = %{$layout->get_config()};
+    my @names = split(qr{/+}msx, $name);
+    my $depth_expected = $layout_config{'depth-project'};
+    if (defined($depth_expected) && $depth_expected != scalar(@names)) {
+        return $E->throw($E->CM_PROJECT_NAME, join('/', @names));
+    }
+    # Check whether the project (trunk) already exists
+    my $target = join('/', $root, @names, $layout_config{'dir-trunk'});
+    my $target_url = eval {$attrib_ref->{svn}->get_info($target)->[0]->{url}};
+    $@ = undef;
+    if ($target_url) {
+        return $E->throw($E->CM_ALREADY_EXIST, $target_url);
+    }
+
+    # Message for the commit log
+    my @message = sprintf("%s: new project.\n", join('/', @names));
+
+    # Create a temporary file for the commit log message
+    my $commit_message_ctx = $attrib_ref->{commit_message_util}->ctx();
+    $commit_message_ctx->set_auto_part(join(q{}, @message));
+    $commit_message_ctx->set_info_part(sprintf("%s    %s\n", 'A', $target));
+    if (!$option_ref->{'non-interactive'}) {
+        $attrib_ref->{commit_message_util}->edit($commit_message_ctx);
+    }
+    $attrib_ref->{commit_message_util}->notify($commit_message_ctx);
+    my $temp_handle
+        = $attrib_ref->{commit_message_util}->temp($commit_message_ctx);
+
+    # Check with the user to see if he/she wants to go ahead
+    if (    !$option_ref->{'non-interactive'}
+        &&  !$attrib_ref->{prompt}->question('PROJECT_CREATE')
+    ) {
+        return;
+    }
+
+    # Create the branch
+    $attrib_ref->{svn}->call(
+        'mkdir',
+        '--file', $temp_handle->filename(),
+        '--parents',
+        ($option_ref->{'svn-non-interactive'} ? '--non-interactive' : ()),
+        (   defined($option_ref->{'password'})
+            ? ('--password', $option_ref->{'password'}) : ()
+        ),
+        $target,
+    );
+    $attrib_ref->{util}->event(FCM::Context::Event->CM_CREATE_TARGET, $target);
+
+    $target;
+}
+
+# Returns a simple wrapper to FCM 1 FCM1::Cm functions.
+sub _fcm1_func {
+    my ($action_ref, $opt_mod_ref) = @_;
+    $opt_mod_ref ||= sub {};
+    sub {
+        my ($attrib_ref, $option_ref, @args) = @_;
+        _parse_args($attrib_ref, $option_ref, \@args);
+        local(@ARGV) = @args;
+        $opt_mod_ref->($option_ref);
+        eval {$action_ref->($option_ref, @args)};
+        if ($@) {
+            if (!FCM1::Cm::Abort->caught($@)) {
+                die($@);
+            }
+            if (!($@->get_code() eq $@->NULL || $@->get_code() eq $@->USER)) {
+                die($@);
+            }
+            $attrib_ref->{util}->event(
+                FCM::Context::Event->CM_ABORT, lc($@->get_code()),
+            );
+            $@ = undef;
+        }
+        return;
+    };
+}
+
+# Generate an option modifier to st_check_handler.
+sub _opt_mod_st_check_handler_func {
+    my $key = shift();
+    sub {
+        my $option_ref = shift();
+        if (!$option_ref->{'non-interactive'}) {
+            $option_ref->{st_check_handler} = $FCM1::Cm::CLI_HANDLER_OF{$key};
+        }
+    };
+}
+
+# Expands keywords in arguments.
+sub _parse_args {
+    my ($attrib_ref, $option_ref, $args_ref) = @_;
+    # Location keywords
+    my $UTIL = $attrib_ref->{util};
+    my $url;
+    for my $arg (@{$args_ref}) {
+        eval {
+            my $locator = FCM::Context::Locator->new($arg);
+            if ($UTIL->loc_what_type($locator) eq 'svn') {
+                my $new_arg = $UTIL->loc_as_normalised($locator);
+                my $SVN = $attrib_ref->{svn};
+                my ($new_arg_url, $new_arg_rev) = $SVN->split_by_peg($new_arg);
+                my (    $arg_url,     $arg_rev) = $SVN->split_by_peg($arg);
+                if (index($arg_url, $UTIL->loc_kw_prefix() . ':') == 0) {
+                    $arg_url = $new_arg_url;
+                }
+                if ($arg_rev && $new_arg_rev && $arg_rev ne $new_arg_rev) {
+                    $arg_rev = $new_arg_rev;
+                }
+                $arg = $arg_url . ($arg_rev ? '@' . $arg_rev : q{});
+                $url ||= $new_arg_url;
+            }
+        };
+        if (my $e = $@) {
+            if (    !FCM::Util::Exception->caught($e)
+                ||  index($e->get_code(), 'LOCATOR_') != 0
+            ) {
+                die($e);
+            }
+            $@ = undef;
+        }
+    }
+    # Revision keywords
+    $url ||= cwd();
+    my $in_opt_rev;
+    for my $arg (@{$args_ref}) {
+        my ($opt, $opt_arg);
+        if ($in_opt_rev) {
+            $in_opt_rev = 0;
+            ($opt, $opt_arg) = (q{}, $arg);
+        }
+        elsif (grep {$_ eq $arg} qw{-c --change -r --revision}) {
+            $in_opt_rev = 1;
+        }
+        else {
+            ($opt, $opt_arg)
+                = $arg =~ qr{\A(-[cr]|--(?:change|revision)=)(.*)\z}msx;
+        }
+        if ($opt_arg) {
+            $arg = $opt . _parse_args_rev($attrib_ref, $url, $opt_arg);
+        }
+    }
+    for my $key (grep {exists($option_ref->{$_})} qw{change revision}) {
+        $option_ref->{$key}
+            = _parse_args_rev($attrib_ref, $url, $option_ref->{$key});
+    }
+}
+
+# Expands revision keywords in an argument.
+sub _parse_args_rev {
+    my ($attrib_ref, $url, $arg) = @_;
+    my $UTIL = $attrib_ref->{util};
+    join(
+        ':',
+        map {
+            my $rev = $_;
+            my $locator = FCM::Context::Locator->new($url . '@' . $rev);
+            local($@);
+            my $value = eval{$UTIL->loc_as_normalised($locator)};
+            if ($value) {
+                (my $url, $rev) = $attrib_ref->{svn}->split_by_peg($value);
+            }
+            $rev;
+        } split(qr{:}msx, $arg, 2)
+    );
+}
+
+# Invokes a system "svn" call.
+sub _svn {
+    my ($attrib_ref, $app, $option_ref, @args) = @_;
+    _parse_args($attrib_ref, $option_ref, \@args);
+    $attrib_ref->{svn}->call($app, @args);
+}
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::CM
+
+=head1 SYNOPSIS
+
+    use FCM::System::CM;
+    my $system = FCM::System::CM->new(\%attrib);
+    my ($out, $err) = $system->svn({}, @args);
+
+=head1 DESCRIPTION
+
+The FCM code management sub-system. This is currently a thin adaptor of
+L<FCM1::Cm|FCM1::Cm>.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. This class should normally be initialised by
+L<FCM::System|FCM::System>.
+
+=item $system->cm_branch_create(\%option,@args)
+
+Implement the C<fcm branch-create> command. On success, return the branch name
+created.
+
+=item $system->cm_branch_list(\%option,@args)
+
+Implement the C<fcm branch-list> command.
+
+=item $system->cm_checkout(\%option,@args)
+
+Thin wrapper of the C<svn checkout> command. Ensure checkout to clean location.
+
+=item $system->cm_diff(\%option,@args)
+
+Thin wrapper of the C<svn diff> command. Allow --graphical option.
+
+=item $system->cm_loc_layout(\%option,@args)
+
+Implement the C<fcm loc-layout> command.
+
+=item $system->cm_project_create(\%option,@args)
+
+Implement the C<fcm project-create> command.
+
+=item $system->cm_branch_delete(\%option,@args)
+=item $system->cm_branch_info(\%option,@args)
+=item $system->cm_commit(\%option,@args)
+=item $system->cm_check_missing(\%option,@args)
+=item $system->cm_check_unknown(\%option,@args)
+=item $system->cm_merge(\%option,@args)
+=item $system->cm_mkpatch(\%option,@args)
+=item $system->cm_resolve_conflicts(\%option,@args)
+=item $system->cm_switch(\%option,@args)
+=item $system->cm_update(\%option,@args)
+
+Thin adaptors for the corresponding code management functions in
+L<FCM1::Cm|FCM1::Cm>.
+
+=item $system->svn($app,\%option,@args)
+
+Invokes a system call to L<svn|svn> $app with @args. %option is not currently
+used, but is left in the argument list for compatibility with the other methods.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/CommitMessage.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/CommitMessage.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/CommitMessage.pm	(revision 5095)
@@ -0,0 +1,312 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# Utility to manipulate FCM commit messages.
+package FCM::System::CM::CommitMessage;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd};
+use FCM::Context::Event;
+use FCM::System::Exception;
+use File::Spec::Functions qw{catfile};
+use File::Temp;
+use Text::ParseWords qw{shellwords};
+
+my $CTX = 'FCM::System::CM::CommitMessage::State';
+my $E = 'FCM::System::Exception';
+
+our $COMMIT_MESSAGE_BASE = '#commit_message#';
+our $DELIMITER_USER
+    = '--Add your commit message ABOVE - do not alter this line or those below--'
+    . "\n";
+our $DELIMITER_AUTO
+    = '--FCM message (will be inserted automatically)--'
+    . "\n";
+our $DELIMITER_INFO
+    = '--Change summary (not part of commit message)--'
+    . "\n";
+our $EDITOR = 'vi';
+our $GEDITOR = 'gvim -f';
+our $SUBVERSION_CONFIG_FILE = catfile((getpwuid($<))[7], qw{.subversion/config});
+
+__PACKAGE__->class({gui => '$', util => '&'},
+    {action_of => {
+        'ctx'       => sub {$CTX->new()},
+        'edit'      => \&_edit,
+        'load'      => \&_load,
+        'notify'    => \&_notify,
+        'path'      => \&_path,
+        'path_base' => sub {$COMMIT_MESSAGE_BASE},
+        'save'      => \&_save,
+        'temp'      => \&_temp,
+    }},
+);
+
+# Invokes an editor to edit the commit message context.
+sub _edit {
+    my ($attrib_ref, $commit_message_ctx) = @_;
+    my $UTIL = $attrib_ref->{'util'};
+    my $temp = File::Temp->new();
+    if ($commit_message_ctx->get_user_part()) {
+        print($temp $commit_message_ctx->get_user_part());
+    }
+    else {
+        print($temp "\n");
+    }
+    print($temp $DELIMITER_USER);
+    if ($commit_message_ctx->get_auto_part()) {
+        print($temp $DELIMITER_AUTO . $commit_message_ctx->get_auto_part());
+    }
+    print($temp $DELIMITER_INFO . $commit_message_ctx->get_info_part());
+    close($temp) || die("$temp: $!\n");
+    my $config_value;
+    my $editor_command
+        = $ENV{'SVN_EDITOR'} ? $ENV{'SVN_EDITOR'}
+        : ($config_value = _svn_config_get($attrib_ref, 'helpers', 'editor-cmd'))
+                             ? $config_value
+        : $ENV{'VISUAL'}     ? $ENV{'VISUAL'}
+        : $ENV{'EDITOR'}     ? $ENV{'EDITOR'}
+        : $attrib_ref->{gui} ? $GEDITOR
+        :                      $EDITOR
+        ;
+    $UTIL->event(FCM::Context::Event->CM_LOG_EDIT, $editor_command);
+    my @command = (shellwords($editor_command), $temp->filename());
+    !system(@command)
+        || return $E->throw($E->SHELL, {command_list => \@command, rc => $?});
+    # Note: cannot use FCM::Util->shell method for terminal based editor.
+    #my %value_of = %{$attrib_ref->{'util'}->shell_simple(\@command)};
+    #if ($value_of{'rc'}) {
+    #    return $E->throw($E->SHELL, {command_list => \@command, %value_of});
+    #}
+    my $user_part = _parse(
+        $attrib_ref,
+        scalar($UTIL->file_load($temp->filename())),
+        $DELIMITER_USER,
+    );
+    $commit_message_ctx->set_user_part($user_part);
+    if (($user_part . $commit_message_ctx->get_auto_part()) =~ qr{\A\s*\z}msx) {
+        return $E->throw($E->CM_LOG_EDIT_NULL);
+    }
+}
+
+# Reads a commit message file from $path or the standard location. Returns a
+# commit message context object.
+sub _load {
+    my ($attrib_ref, $path) = @_;
+    $path ||= _path($attrib_ref);
+    my ($user_part, $auto_part) = eval {
+        _parse($attrib_ref, scalar($attrib_ref->{'util'}->file_load($path)));
+    };
+    if (my $e = $@) {
+        $user_part = q{};
+        $auto_part = q{};
+        $@ = undef; # TODO: should raise a high verbosity event?
+    }
+    $CTX->new({'user_part' => $user_part, 'auto_part' => $auto_part});
+}
+
+# Raises an CM_COMMIT_MESSAGE event for the commit message.
+sub _notify {
+    my ($attrib_ref, $commit_message_ctx) = @_;
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->CM_COMMIT_MESSAGE, $commit_message_ctx,
+    );
+}
+
+# Parses a commit message into the user and auto parts. Returns the user part in
+# scalar context. Returns (user_part, auto_part) in list context.
+sub _parse {
+    my ($attrib_ref, $message, $no_delimiter_user) = @_;
+    my @parts = (q{}, q{});
+    my $state = 0;
+    LINE:
+    for my $line (split("\n", $message)) {
+        if ($state && !wantarray()) {
+            last LINE;
+        }
+        $line .= "\n";
+        if ($line eq $DELIMITER_INFO) {
+            last LINE;
+        }
+        elsif ($line eq $DELIMITER_AUTO) {
+            $state = 1;
+            next LINE;
+        }
+        elsif ($line eq $DELIMITER_USER) {
+            $no_delimiter_user = undef;
+            $state = -1;
+            next LINE;
+        }
+        if ($state >= 0) {
+            $parts[$state] .= $line;
+        }
+    }
+    if ($no_delimiter_user) {
+        return $E->throw($E->CM_LOG_EDIT_DELIMITER, $DELIMITER_USER);
+    }
+    for my $part (@parts) {
+        $part =~ s{\A\s*(.*?)\s*\z}{$1}msx;
+        if ($part) {
+            $part .= "\n";
+        }
+    }
+    wantarray() ? @parts : $parts[0];
+}
+
+# Returns the path to the commit message file in the current working directory
+# or the commit message file in $dir if $dir is set.
+sub _path {
+    my ($attrib_ref, $dir) = @_;
+    catfile(($dir ? $dir : cwd()), $COMMIT_MESSAGE_BASE);
+}
+
+# Saves the commit message to $path or the standard location for later
+# retrieval.
+sub _save {
+    my ($attrib_ref, $commit_message_ctx, $path) = @_;
+    $path ||= _path($attrib_ref);
+    my $string = $commit_message_ctx->get_user_part();
+    if ($commit_message_ctx->get_auto_part()) {
+        $string .= $DELIMITER_AUTO . $commit_message_ctx->get_auto_part();
+    }
+    $attrib_ref->{'util'}->file_save($path, $string);
+}
+
+# Returns a File::Temp object containing a commit message ready for the VCS.
+sub _temp {
+    my ($attrib_ref, $commit_message_ctx) = @_;
+    my $temp = File::Temp->new();
+    print($temp $commit_message_ctx->get_user_part());
+    print($temp $commit_message_ctx->get_auto_part());
+    close($temp) || die("$temp: $!\n");
+    $temp;
+}
+
+# Loads a setting from $HOME/.subversion/config, and returns its value.
+sub _svn_config_get {
+    my ($attrib_ref, $section, $key) = @_;
+    # Note: can use Config::IniFiles, but best to avoid another dependency.
+    # Note: not very efficient logic here, but should not yet matter.
+    my $handle = $attrib_ref->{'util'}->file_load_handle($SUBVERSION_CONFIG_FILE);
+    my $is_in_section;
+    my $value;
+    LINE:
+    while (my $line = readline($handle)) {
+        chomp($line);
+        if ($line =~ qr{\A\s*(?:[#;]|\z)}msx) {
+            next LINE;
+        }
+        if ($line =~ qr{\A\s*\[\s*$section\s*\]\s*\z}msx) {
+            $is_in_section = 1;
+        }
+        elsif ($line =~ qr{\A\s*\[}msx) {
+            $is_in_section = 0;
+        }
+        elsif ($is_in_section) {
+            my ($rhs) = $line =~ qr{\A\s*$key\s*=\s*(.*)\z}msx;
+            if (defined($rhs)) {
+                $value = $rhs;
+            }
+        }
+    }
+    close($handle);
+    $value;
+}
+
+#-------------------------------------------------------------------------------
+package FCM::System::CM::CommitMessage::State;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({
+    (map {($_ . '_part' => {isa => '$', default => q{}})} qw{auto info user}),
+});
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::CM::CommitMessage
+
+=head1 SYNOPSIS
+
+    use FCM::System::CM::CommitMessage;
+    my $commit_message_util = FCM::System::CM::CommitMessage->new(\%attrib);
+    my $commit_message_ctx = $commit_message_util->ctx();
+    $commit_message_util->edit($ctx);
+
+=head1 DESCRIPTION
+
+The commit message dumper, editor, loader, parser, etc for the FCM code
+management sub-system.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Return a new instance. This class should normally be initialised by
+L<FCM::System::CM|FCM::System::CM>.
+
+=item $commit_message_util->ctx()
+
+Return a new and empty commit message context.
+
+=item $commit_message_util->edit($commit_message_ctx)
+
+Invoke an editor to edit the commit message context.
+
+=item $commit_message_util->load($path)
+
+Load the content of a commit message file in $path, and return the result in a
+new commit message context.
+
+=item $commit_message_util->notify($commit_message_ctx)
+
+Raise a CM_COMMIT_MESSAGE event with the $commit_message_ctx.
+
+=item $commit_message_util->path($dir)
+
+Return the path to the commit message file in $dir or the current working
+directory if $dir is not specified.
+
+=item $commit_message_util->path($dir)
+
+Return the base name of the commit message file.
+
+=item $commit_message_util->save($commit_message_ctx, $path)
+
+Save the commit message to $path (or the standard location if $path is not
+specified).
+
+=item $commit_message_util->temp()
+
+Return a File::Temp object containing a commit message ready for the VCS.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/Prompt.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/Prompt.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/Prompt.pm	(revision 5095)
@@ -0,0 +1,223 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+package FCM::System::CM::Prompt;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+
+our $TYPE_YN = 'TYPE_YN';
+
+# Format string table
+my %S = (
+    'BRANCH_CREATE'     => 'Create the branch?',
+    'OVERWRITE'         => '%s: file exists, overwrite?',
+    'PROJECT_CREATE'    => 'Create the project?',
+    'RESOLVE'           => 'Run "svn resolve --accept working %s"?',
+    'TC'                => "Locally: %s.\n"
+                           . "Externally: %s.\n"
+                           . "Answer (y) to %s.\n"
+                           . "Answer (n) to %s.\n"
+                           . '%s'
+                           . 'Keep the local version?',
+    'TC_ACTION'         => 'accept the %s %s',
+    'TC_ACTION_ADD'     => 'keep the %s file filename',
+    'TC_ACTION_EDIT'    => 'keep the file',
+    'TC_FROM_LOC'       => 'local',
+    'TC_FROM_INC'       => 'external',
+    'TC_MERGE'          => "You can then merge in changes.\n",
+    'TC_ST_ADD'         => 'added',
+    'TC_ST_DELETE'      => 'deleted',
+    'TC_ST_EDIT'        => 'edited',
+    'TC_ST_REPLACE'     => 'replaced',
+    'TC_ST_RENAME'      => 'renamed to %s',
+);
+
+# Configuration for questions
+# KEY => {'format' => $|&, 'type' => $}
+my %Q_CONF = (
+    # Simple question prompts
+    (   map {($_ => {'format' => $S{$_}, 'type' => q{}})}
+            qw{BRANCH_CREATE OVERWRITE PROJECT_CREATE RESOLVE}
+    ),
+    # Tree conflicts prompts: TC_LxIy, for local x, incoming y
+    # where x and y correspond to:
+    # A => add,
+    # D => delete,
+    # E => edit,
+    # M => missing,
+    # P => replace,
+    # R => rename
+    (   map {('TC_' . $_ => {'format' => \&_q_tree_conflict, 'type' => $TYPE_YN})}
+            qw(LAIA LDID LDIE LDIR LEID LEIP LEIR LRID LRIE LRIR)
+    ),
+);
+
+__PACKAGE__->class(
+    {gui => '$', util => '&'},
+    {init => \&_init, action_of => {question => \&_q}},
+);
+
+sub _init {
+    my $attrib_ref = shift();
+    my $class = $attrib_ref->{gui}
+        ? 'FCM::System::CM::Prompt::Zenity' : 'FCM::System::CM::Prompt::Simple';
+    $attrib_ref->{impl} = $class->new({util => $attrib_ref->{util}});
+}
+
+sub _q {
+    my ($attrib_ref, $key, @args) = @_;
+    my $format = $Q_CONF{$key}{'format'};
+    my $prompt = ref($format) ? $format->(@args) : sprintf($format, @args);
+    $attrib_ref->{'impl'}->question($Q_CONF{$key}{'type'}, $prompt);
+}
+
+# Tree conflict prompt.
+# $tree_key is the FCM::System::CM::TreeConflictKey for the conflict.
+# $rename_loc is the new local name for the conflict file, if any.
+# $rename_inc is the new incoming name for the conflict file, if any.
+sub _q_tree_conflict {
+    my ($tree_key, $rename_loc, $rename_inc) = @_;
+    my %opt_of = (
+        'loc' => {'key' => $tree_key->get_local()   , 'rename' => $rename_loc},
+        'inc' => {'key' => $tree_key->get_incoming(), 'rename' => $rename_inc},
+    );
+    sprintf($S{'TC'}, (
+        (   map {
+                my $opt = $_;
+                my $message = $S{'TC_ST_' . uc($opt->{'key'})};
+                if ($opt->{'key'} eq 'rename') {
+                    $message = sprintf($message, $opt->{'rename'});
+                }
+                $message;
+            }
+            @opt_of{'loc', 'inc'}
+        ),
+        (   map {
+                my $location_key = $_;
+                my $from = $S{'TC_FROM_' . uc($location_key)};
+                my $key = $opt_of{$location_key}->{'key'};
+                  $key eq 'add'     ? sprintf($S{'TC_ACTION_ADD'}, $from)
+                : $key eq 'edit'    ? $S{'TC_ACTION_EDIT'}
+                :                     sprintf($S{'TC_ACTION'}, $from, $key)
+                ;
+            }
+            ('loc', 'inc')
+        ),
+        (   (        (grep {$opt_of{'loc'}{'key'} eq $_} qw{rename edit})
+                &&   (grep {$opt_of{'inc'}{'key'} eq $_} qw{rename edit})
+            )
+            ? $S{'TC_MERGE'} : q{}
+        ),
+    ));
+}
+
+#-------------------------------------------------------------------------------
+package FCM::System::CM::Prompt::Simple;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+
+our %SETTING_OF = (
+    q{}       => {'choices' => [qw{y n}], 'default' => 'n', 'positive' => 'y'},
+    'TYPE_YN' => {'choices' => [qw{y n}], 'default' => 'n', 'positive' => 'y'},
+);
+
+__PACKAGE__->class({util => '&'}, {action_of => {question => \&_question}});
+
+sub _question {
+    my ($attrib_ref, $type, $question) = @_;
+    my %setting = %{$SETTING_OF{$type}};
+    _prompt($attrib_ref, $question, $setting{'choices'}, $setting{'default'})
+        eq $setting{'positive'};
+}
+
+sub _prompt {
+    my ($attrib_ref, $question, $choices_ref, $default) = @_;
+    my ($tail, @heads) = reverse(@{$choices_ref});
+    my $prompt
+        = $question . "\n"
+        . sprintf('Enter "%s" or "%s"', join(q{, }, reverse(@heads)), $tail)
+        . sprintf(' (or just press <return> for "%s") ', $default);
+    my $answer;
+    while (!defined($answer)) {
+        $attrib_ref->{util}->event(FCM::Context::Event->OUT, $prompt);
+        $answer = readline(STDIN);
+        chomp($answer);
+        if (!$answer) {
+            $answer = $default;
+        }
+        if (!grep {$_ eq $answer} @{$choices_ref}) {
+            $answer = undef;
+        }
+    }
+    return $answer;
+}
+
+#-------------------------------------------------------------------------------
+package FCM::System::CM::Prompt::Zenity;
+use base qw{FCM::Class::CODE};
+
+our %OPTIONS_OF = (
+    q{}       => [],
+    'TYPE_YN' => ['--ok-label=_Yes', '--cancel-label=_No'],
+);
+
+__PACKAGE__->class({util => '&'}, {action_of => {question => \&_question}});
+
+sub _question {
+    my ($attrib_ref, $type, $question) = @_;
+    _zenity($attrib_ref, qw{--question --text}, $question, @{$OPTIONS_OF{$type}});
+}
+
+sub _zenity {
+    my ($attrib_ref, @args) = @_;
+    my @command = ('zenity', @args);
+    my %value_of = %{$attrib_ref->{util}->shell_simple(\@command)};
+    !$value_of{rc};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::CM::Prompt
+
+=head1 SYNOPSIS
+
+    use FCM::System::CM::Prompt;
+    my $prompt = FCM::System::CM::Prompt->new(\%attrib);
+    if ($prompt->question($key, @args)) {
+        # do something
+    }
+
+=head1 DESCRIPTION
+
+Helper module for prompts in the FCM code management sub-system.
+See L<FCM::System::CM|FCM::System::CM> for detail.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/ResolveConflicts.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/ResolveConflicts.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/ResolveConflicts.pm	(revision 5095)
@@ -0,0 +1,685 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+package FCM::System::CM::ResolveConflicts;
+use base qw{Exporter};
+our @EXPORT_OK = qw{_cm_resolve_conflicts};
+
+use Cwd qw{cwd};
+use FCM::Context::Event;
+use FCM::System::Exception;
+use File::Basename qw{basename dirname};
+use File::Copy qw{copy};
+use File::Spec::Functions qw{abs2rel catfile rel2abs};
+use File::Temp;
+
+# LxIy stands for local x, incoming y in the tree conflict description.
+# The letters of x and y correspond to:
+# A => add,
+# D => delete,
+# E => edit,
+# M => missing,
+# P => replace,
+# R => rename,
+# although the 'rename' has to be detected by our code below.
+
+our %TREE_CONFLICT_GET_GRAPHIC_SOURCES_FUNC_FOR = (
+    LEIR => \&_cm_tree_conflict_get_graphic_sources_for_leir,
+    LRIE => \&_cm_tree_conflict_get_graphic_sources_for_lrie,
+    LRIR => \&_cm_tree_conflict_get_graphic_sources_for_lrir,
+);
+
+# A tree conflict key must be present here for auto-resolving.
+our %TREE_CONFLICT_GET_FINAL_ACTIONS_FUNC_FOR = (
+    LAIA => \&_cm_tree_conflict_get_actions_for_laia,
+    LDID => sub {},
+    LDIE => \&_cm_tree_conflict_get_actions_for_ldie,
+    LDIR => \&_cm_tree_conflict_get_actions_for_ldir,
+    LEID => \&_cm_tree_conflict_get_actions_for_leid,
+    LEIR => \&_cm_tree_conflict_get_actions_for_leir,
+    LEIP => \&_cm_tree_conflict_get_actions_for_leip,
+    LRID => \&_cm_tree_conflict_get_actions_for_lrid,
+    LRIE => \&_cm_tree_conflict_get_actions_for_lrie,
+    LRIR => \&_cm_tree_conflict_get_actions_for_lrir,
+);
+
+# Handle aliases for actions.
+our %TREE_CONFLICT_GET_UNALIAS_FOR = (
+    'obstruction' => 'add',
+    'missing' => 'delete',
+);
+
+# Number of renamed files that triggers a time warning.
+our $TREE_CONFLICT_WARN_FILES_THRESHOLD = 10;
+
+my $E = 'FCM::System::Exception';
+# Regular expressions
+my %RE = (
+    # determines if a file was copied, i.e. added with history from "svn status"
+    ST_COPIED => qr{^A..\+....(.*)}msx,
+);
+
+# Resolve conflicts.
+sub _cm_resolve_conflicts {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    my $UTIL = $attrib_ref->{util};
+    my $pwd = cwd();
+    if (!@args) {
+        push(@args, '.');
+    }
+    for my $arg (@args) {
+        if (!-e $arg) {
+            die("$arg: $!\n");
+        }
+        chdir($attrib_ref->{svn}->get_wc_root($arg)) || die("$arg: $!\n");
+        my @command = qw{svn status};
+        my %value_of = %{$UTIL->shell_simple(\@command)};
+        if ($value_of{rc}) {
+            return $E->throw($E->SHELL, {command_list => \@command, %value_of});
+        }
+        my @status_lines = grep {$_} split("\n", $value_of{o});
+        local(%ENV) = %ENV;
+        $ENV{FCM_GRAPHIC_MERGE} ||= $UTIL->external_cfg_get('graphic-merge');
+        for my $path (map {($_ =~ qr{\AC.{6}\s(.*)\z}msx)} @status_lines) {
+            _cm_resolve_text_conflict(
+                $attrib_ref,
+                $option_ref,
+                $path,
+                @status_lines,
+            );
+        }
+        for my $path (map {($_ =~ qr{\A.{6}C\s(.*)\z}msx)} @status_lines) {
+            _cm_resolve_tree_conflict(
+                $attrib_ref,
+                $option_ref,
+                $path,
+                @status_lines,
+            );
+        }
+    }
+    chdir($pwd);
+}
+
+# Helper for _cm_resolve_conflicts, launch graphic merge tool.
+sub _cm_graphic_merge {
+    my $attrib_ref = shift();
+    my @command = ('fcm_graphic_merge', @_);
+    my $UTIL = $attrib_ref->{util};
+    my %value_of = %{$UTIL->shell_simple(\@command)};
+    # rc==0: all conflicts resovled
+    # rc==1: some conflicts not resolved
+    # rc==2: trouble
+    if (!grep {$_ eq $value_of{rc}} (0, 1)) {
+        return $E->throw(
+            $E->SHELL, {command_list => \@command, %value_of}, $value_of{e},
+        );
+    }
+    $UTIL->event(FCM::Context::Event->OUT, $value_of{o});
+    $value_of{rc};
+}
+
+# Resolve a text conflict.
+sub _cm_resolve_text_conflict {
+    my ($attrib_ref, $option_ref, $path) = @_;
+    my $PROMPT = $attrib_ref->{prompt};
+    my $UTIL = $attrib_ref->{util};
+    if (-B $path) {
+        $UTIL->event(FCM::Context::Event->CM_CONFLICT_TEXT_SKIP, $path);
+        return;
+    }
+    $UTIL->event(FCM::Context::Event->CM_CONFLICT_TEXT, $path);
+
+    # Get conflicts markers files
+    my %info = %{$attrib_ref->{svn}->get_info($path)->[0]};
+    my @keys = map {"conflict:$_-file"} qw{prev-wc prev-base cur-base};
+    # Subversion 1.6: conflict filenames are relative paths.
+    # Subversion 1.8: conflict filenames are absolute paths.
+    my ($mine, $older, $yours) = map {
+        rel2abs($_, rel2abs(dirname($path)))
+    } @info{@keys};
+
+    # If $path is newer (by more than a second), it may contain saved changes.
+    if (    -f $path && (stat($path))[9] > (stat($mine))[9] + 1
+        &&  !$PROMPT->question('OVERWRITE', $path)
+    ) {
+        return;
+    }
+
+    # Launch graphic merge tool
+    if (_cm_graphic_merge($attrib_ref, $path, $mine, $older, $yours)) {
+        return; # rc==1, some conflicts not resolved
+    }
+
+    # Prompt user to run "svn resolve --accept working" on the file
+    if ($PROMPT->question('RESOLVE', $path)) {
+        $attrib_ref->{svn}->call(qw{resolve --accept working}, $path);
+    }
+}
+
+# Resolve a tree conflict.
+sub _cm_resolve_tree_conflict {
+    my ($attrib_ref, $option_ref, $path, @status_lines) = @_;
+    my $PROMPT = $attrib_ref->{prompt};
+    my $UTIL = $attrib_ref->{util};
+
+    # Skip directories - too complex for now.
+    if (-d $path) {
+        $UTIL->event(FCM::Context::Event->CM_CONFLICT_TREE_SKIP, $path);
+        return;
+    }
+
+    # Get basic information about the tree conflict, and the filename.
+    my %info = %{$attrib_ref->{svn}->get_info($path)->[0]};
+
+    # Skip non-existent or unhandled tree conflicts.
+    if (!exists($info{'tree-conflict:operation'})) {
+        return
+    }
+    if ($info{'tree-conflict:operation'} ne 'merge') {
+        $UTIL->event(FCM::Context::Event->CM_CONFLICT_TREE_SKIP, $path);
+        return;
+    }
+    
+    my $tree_reason = $info{'tree-conflict:reason'};
+    if (grep {$tree_reason eq $_} keys(%TREE_CONFLICT_GET_UNALIAS_FOR)) {
+        $tree_reason = $TREE_CONFLICT_GET_UNALIAS_FOR{$tree_reason};
+    }
+    my $tree_key = FCM::System::CM::TreeConflictKey->new(
+        {   'local'    => $tree_reason,
+            'incoming' => $info{'tree-conflict:action'},
+            'type'     => $info{'tree-conflict:operation'},
+        },
+    );
+    my $tree_filename = $info{'path'};
+
+    my %wc_info = %{$attrib_ref->{svn}->get_info()->[0]};
+
+    my $repos_root = $wc_info{'repository:root'};
+    my $wc_branch = substr($wc_info{'url'}, length($repos_root) + 1);
+
+    my $tree_full_filename = '/' . $wc_branch . '/' . $tree_filename;
+
+    # Check for external renaming, by examining files added with history
+    my $ext_renamed_file = '';
+    my $ext_branch = '';
+    COPIED_FILE:
+    for my $copied_file (map {($_ =~ $RE{ST_COPIED})} @status_lines) {
+        my %copy_info = %{$attrib_ref->{svn}->get_info($copied_file)->[0]};
+        my $url = (
+              $copy_info{'wc-info:copy-from-url'}
+            . '@' . $copy_info{'wc-info:copy-from-rev'}
+        );
+        my $copy_log_ref = $attrib_ref->{svn}->get_log($url);
+        if (!$ext_branch) {
+            my $copy_full_path = substr(
+                $copy_info{'wc-info:copy-from-url'},
+                length($repos_root) + 1,
+            );
+            $ext_branch = substr(
+                $copy_full_path,
+                0,
+                -length($copied_file) - 1,
+            );
+        }
+        my $copied_full_filename = '/' . $ext_branch . '/' . $copied_file;
+        my $tree_ext_name
+            = ('/' . $info{'tree-conflict:source-right:path-in-repos'});
+        my $search_name = $tree_ext_name;
+        for my $log_entry_ref (reverse(@{$copy_log_ref})) {
+            for my $path_entry (@{$log_entry_ref->{'paths'}}) {
+                if (    exists $path_entry->{'copyfrom-path'}
+                    &&  $path_entry->{'copyfrom-path'} eq $search_name
+                ) {
+                    $search_name = $path_entry->{'path'};
+                    if ($search_name eq $copied_full_filename) {
+                        $ext_renamed_file = $copied_file;
+                        last COPIED_FILE;
+                    }
+                }
+            }
+        }
+    }
+
+    # Check for local renaming of the tree conflict file
+    my $local_renamed_file = '';
+    if ($tree_reason eq 'delete') {
+        $local_renamed_file = _cm_tree_conflict_get_local_rename(
+            $attrib_ref,
+            $wc_branch,
+            $tree_full_filename,
+            $ext_renamed_file
+        );
+    }
+
+    # The tree conflict identifier (tree_key) needs to be adjusted for reality
+    if ($local_renamed_file) {
+        $tree_key->set_local('rename');
+    }
+    if ($ext_renamed_file) {
+        $tree_key->set_incoming('rename');
+    }
+
+    # Skip and return if the tree key does not match a key in final cmds.
+    my $cmds_getter
+        = $TREE_CONFLICT_GET_FINAL_ACTIONS_FUNC_FOR{$tree_key->as_string()};
+    if (!$cmds_getter) {
+        $UTIL->event(FCM::Context::Event->CM_CONFLICT_TREE_SKIP, $path);
+        return;
+    }
+
+    # Print the tree conflict event message
+    $UTIL->event(FCM::Context::Event->CM_CONFLICT_TREE, $path);
+
+    # These are the relevant files for this tree conflict.
+    my @file_args = grep {$_} ($path, $local_renamed_file, $ext_renamed_file);
+
+    # Prompt which version of events to accept - local or incoming.
+    my $keep_local = $PROMPT->question(
+        'TC_' . $tree_key->as_string(),
+        $tree_key, $local_renamed_file, $ext_renamed_file,
+    );
+
+    # Add any graphic merge commands
+    my @cmds = _cm_tree_conflict_get_graphic_cmds(
+        $attrib_ref, $tree_key->as_string(), $keep_local, \@file_args,
+    );
+    # Now load any miscellaneous actions or commands - for example 'svn delete'
+    if ($tree_key->get_local() eq 'add' && $tree_key->get_incoming() eq 'add') {
+        # We need to generate a new filename and a temporary one in this case.
+        @file_args = ($path);
+        my $tree_dir = dirname($path);
+        my $newfile_handle = File::Temp->new(
+            DIR => $tree_dir,
+            TEMPLATE => basename($path) . '.XXXX',
+            UNLINK => 0,
+        );
+        unlink("$newfile_handle");  # Delete it, or it will block the copy.
+        push(@file_args, "$newfile_handle");
+    }
+    push(@cmds, $cmds_getter->($attrib_ref, $keep_local, \@file_args));
+
+    # Run the actions, including any subroutine references.
+    for my $cmd_ref (@cmds) {
+        $cmd_ref->();
+    }
+    # svn resolve.
+    $attrib_ref->{svn}->call(qw{resolve --accept working}, $path);
+}
+
+# Tree conflicts: check if a file was renamed locally.
+sub _cm_tree_conflict_get_local_rename {
+    my ($attrib_ref, $wc_branch, $tree_full_filename, $ext_renamed_file) = @_;
+    my $UTIL = $attrib_ref->{util};
+
+    # Get the verbose log for the working copy.
+    # Find the revision where the file was deleted, and store any copied
+    # filenames at that revision, and since that revision.
+    my ($d_rev, @rev_copied_filenames, $found_delete);
+    my @since_copied_filenames;
+    my $wc_log_ref = $attrib_ref->{svn}->get_log();
+    ENTRY:
+    for my $log_entry_ref (@{$wc_log_ref}) {
+        $d_rev = $log_entry_ref->{'revision'};
+        @rev_copied_filenames = ();
+        for my $path_entry (@{$log_entry_ref->{'paths'}}) {
+            if ($path_entry->{'copyfrom-path'}) {
+                push(@rev_copied_filenames, $path_entry->{'path'});
+                push(@since_copied_filenames, $path_entry->{'path'});
+            }
+            if (    $path_entry->{'path'} eq $tree_full_filename
+                &&  $path_entry->{'action'} eq 'D'
+            ) {
+                $found_delete = 1;
+            }
+        }
+        if ($found_delete) {
+            last ENTRY;
+        }
+    }
+
+    # We need to detect a copy and a deletion of the file to continue.
+    if (!$found_delete || !@rev_copied_filenames) {
+        return;
+    }
+
+    # Get rid of the branch name in our copied files
+    for my $copied_file (@rev_copied_filenames) {
+        $copied_file =~ s{^/$wc_branch/}{}msx;
+    }
+    for my $copied_file (@since_copied_filenames) {
+        $copied_file =~ s{^/$wc_branch/}{}msx;
+    }
+
+    # Get the pre-existing working copy files.
+    my @wc_files
+        = grep {$_ && $_ =~ qr{[^/]$}msx}
+            $attrib_ref->{svn}->stdout(qw{svn ls -R});
+
+    # Examine the files added with history at the revision log.
+    # A single rename will be detected here.
+    my @result;
+    COPIED_AT_REV_FILE:
+    for my $file (@rev_copied_filenames) {
+        if (!grep {$_ eq $file} @wc_files) {
+            next COPIED_AT_REV_FILE;
+        }
+        my @log_entry_refs
+            = @{$attrib_ref->{svn}->get_log($file . '@' . $d_rev)};
+        my $search_name = $tree_full_filename;
+        my $full_potential_rename = '/' . $wc_branch . '/' . $file;
+        for my $log_entry_ref (@log_entry_refs) {
+            for my $path_entry (@{$log_entry_ref->{'paths'}}) {
+                if (    exists($path_entry->{'copyfrom-path'})
+                    &&  $path_entry->{'copyfrom-path'} eq $tree_full_filename
+                    &&  $path_entry->{'action'} eq 'A'
+                    &&  $path_entry->{'path'} eq $full_potential_rename
+                ) {
+                    return $file;
+                }
+            }
+        }
+    }
+
+    # If no rename was detected, there may have been more than one.
+    # Get the logs for all current filenames that match copied filenames
+    # since the deletion, according to the working copy log.
+
+    # Warn if the number of files to be examined > the threshold.
+    if (    @wc_files > $TREE_CONFLICT_WARN_FILES_THRESHOLD
+        &&  @since_copied_filenames > $TREE_CONFLICT_WARN_FILES_THRESHOLD
+    ) {
+        my $tree_path = substr($tree_full_filename, length($wc_branch) + 2);
+        $UTIL->event(
+            FCM::Context::Event->CM_CONFLICT_TREE_TIME_WARN,
+            $tree_path,
+        );
+    }
+    WC_FILE:
+    for my $file (@wc_files) {
+        if (!grep {$_ eq $file} @since_copied_filenames) {
+            next WC_FILE;
+        }
+        my @log_entry_refs = @{$attrib_ref->{svn}->get_log($file)};
+        my $search_name = $tree_full_filename;
+        my $full_potential_rename = '/' . $wc_branch . '/' . $file;
+        for my $log_entry_ref (reverse(@log_entry_refs)) {
+            my $revision = $log_entry_ref->{'revision'};
+            for my $path_entry (@{$log_entry_ref->{'paths'}}) {
+                if (    exists($path_entry->{'copyfrom-path'})
+                    &&  $path_entry->{'copyfrom-path'} eq $search_name
+                    &&  $path_entry->{'action'} eq 'A'
+                ) {
+                    $search_name = $path_entry->{'path'};
+                    if (   $search_name eq $full_potential_rename
+                        && $revision >= $d_rev
+                    ) {
+                        return $file;
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
+# Return the tree conflict command related to fcm_graphic_merge.
+sub _cm_tree_conflict_get_graphic_cmds {
+    my ($attrib_ref, $key, $keep_local, $files_ref) = @_;
+    my ($cfile, @rename_args) = @{$files_ref};
+    if (!@rename_args) {
+        return;
+    }
+    # Get the source argument subroutine reference, if it exists.
+    my $get_srcs_func_ref = $TREE_CONFLICT_GET_GRAPHIC_SOURCES_FUNC_FOR{$key};
+    if (!$get_srcs_func_ref) {
+        return;
+    }
+    # Get the sources for the graphic merge files.
+    my ($older_src, $merge_src, $working_src, $base)
+        = $get_srcs_func_ref->($cfile, $keep_local, \@rename_args);
+
+    # Set up the filenames.
+    my ($older_url, $older_peg)
+        = _cm_tree_conflict_source($attrib_ref, 'left', $older_src);
+    my $mine = $base . '.working';
+    my $older = $base . '.merge-left.r' . $older_peg;
+    my ($merge_url, $merge_peg)
+        = _cm_tree_conflict_source($attrib_ref, 'right', $merge_src);
+    my $yours = $base . '.merge-right.r' . $merge_peg;
+    # Set up the conflict files as in a text conflict.
+    sub {
+        $attrib_ref->{svn}->call(qw{export -q}, $older_url, $older);
+        $attrib_ref->{svn}->call(qw{export -q}, $merge_url, $yours);
+        copy($working_src, $mine);
+        _cm_graphic_merge($attrib_ref, $base, $mine, $older, $yours);
+        unlink($mine, $older, $yours);
+    };
+}
+
+# Return the source-left or source-right url from svn info.
+sub _cm_tree_conflict_source {
+    my ($attrib_ref, $direction, $info_filename) = @_;
+    my %info = %{$attrib_ref->{svn}->get_info($info_filename)->[0]};
+    my ($source_url, $source_peg);
+    if ($info{"tree-conflict:source-$direction:repos-url"}) {
+        $source_url
+            = $info{"tree-conflict:source-$direction:repos-url"}
+            . '/'
+            . $info{"tree-conflict:source-$direction:path-in-repos"};
+        $source_peg = $info{"tree-conflict:source-$direction:revision"};
+    }
+    elsif ($direction eq 'right') {
+        $source_url = $info{'wc-info:copy-from-url'};
+        $source_peg = $info{'wc-info:copy-from-rev'};
+    }
+    ($source_url . '@' . $source_peg, $source_peg);
+}
+
+# Select the files needed for the xxdiff (local edit, incoming rename)
+sub _cm_tree_conflict_get_graphic_sources_for_leir {
+    my ($cfile, $keep_local, $renames) = @_;
+    my $ext_rename = shift(@{$renames});
+    (   $cfile,
+        $ext_rename,
+        $cfile,
+        ($keep_local ? $cfile : $ext_rename),
+    );
+}
+
+# Select the files needed for the xxdiff (local rename, incoming edit)
+sub _cm_tree_conflict_get_graphic_sources_for_lrie {
+    my ($cfile, $keep_local, $renames) = @_;
+    my $local_rename = shift(@{$renames});
+    (   $cfile,
+        $cfile,
+        $local_rename,
+        $local_rename,
+    );
+}
+
+# Select the files needed for the xxdiff (local rename, incoming rename)
+sub _cm_tree_conflict_get_graphic_sources_for_lrir {
+    my ($cfile, $keep_local, $renames) = @_;
+    my ($local_rename, $ext_rename) = @{$renames};
+    (   $cfile,
+        $ext_rename,
+        $local_rename,
+        ($keep_local ? $local_rename : $ext_rename),
+    );
+}
+
+# Return the actions needed to resolve 'local add, incoming add'
+sub _cm_tree_conflict_get_actions_for_laia {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile, $new_name) = @{$files_ref};
+    my ($url, $url_peg) = _cm_tree_conflict_source($attrib_ref, 'right', $cfile);
+    my ($basename) = basename($cfile);
+    my $cdir = dirname($cfile);
+    sub {
+        if (!$keep_local) {
+            my $content = $attrib_ref->{svn}->stdout(qw{svn cat}, $url);
+            $attrib_ref->{util}->file_save($cfile, $content);
+        }
+    };
+}
+
+
+# Return the actions needed to resolve 'local missing, incoming edit'
+sub _cm_tree_conflict_get_actions_for_ldie {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile) = @{$files_ref};
+    my ($url, $url_peg) = _cm_tree_conflict_source($attrib_ref, 'right', $cfile);
+    my $cdir = dirname($cfile);
+    sub {
+        if (!$keep_local) {
+            $attrib_ref->{svn}->call('copy', $url, "$cdir/");
+        }
+    };
+}
+
+# Return the actions needed to resolve 'local delete, incoming rename'
+sub _cm_tree_conflict_get_actions_for_ldir {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile, $ext_rename) = @{$files_ref};
+    sub {
+        if ($keep_local) {
+            $attrib_ref->{svn}->call('revert', $ext_rename);
+            unlink($ext_rename);
+        }
+    };
+}
+
+# Return the actions needed to resolve 'local edit, incoming delete'
+sub _cm_tree_conflict_get_actions_for_leid {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile) = @{$files_ref};
+    sub {
+        if (!$keep_local) {
+            $attrib_ref->{svn}->call('delete', $cfile);
+        }
+    };
+}
+
+# Return the actions needed to resolve 'local edit, incoming replace'
+sub _cm_tree_conflict_get_actions_for_leip {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile) = @{$files_ref};
+    my ($url, $url_peg) = _cm_tree_conflict_source($attrib_ref, 'right', $cfile);
+    my $cdir = dirname($cfile);
+    sub {
+        if (!$keep_local) {
+            $attrib_ref->{svn}->call('delete', $cfile);
+            $attrib_ref->{svn}->call('copy', $url, "$cdir/");
+        }
+    };
+}
+
+# Return the actions needed to resolve 'local edit, incoming rename'
+sub _cm_tree_conflict_get_actions_for_leir {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile, $ext_rename) = @{$files_ref};
+    sub {
+        if ($keep_local) {
+            $attrib_ref->{svn}->call('revert', $ext_rename);
+            unlink($ext_rename);
+        }
+        else {
+            $attrib_ref->{svn}->call('delete', $cfile);
+        }
+    };
+}
+
+# Return the actions needed to resolve 'local rename, incoming delete'
+sub _cm_tree_conflict_get_actions_for_lrid {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile, $lcl_rename) = @{$files_ref};
+    sub {
+        if (!$keep_local) {
+            $attrib_ref->{svn}->call('delete', $lcl_rename);
+        }
+    };
+}
+
+# Return the actions needed to resolve 'local rename, incoming edit'
+sub _cm_tree_conflict_get_actions_for_lrie {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile, $lcl_rename) = @{$files_ref};
+    sub {
+        if (!$keep_local) {
+            $attrib_ref->{svn}->call('rename', $lcl_rename, $cfile)
+        }
+    };
+}
+
+# Return the actions needed to resolve 'local rename, incoming rename'
+sub _cm_tree_conflict_get_actions_for_lrir {
+    my ($attrib_ref, $keep_local, $files_ref) = @_;
+    my ($cfile, $lcl_rename, $ext_rename) = @{$files_ref};
+    sub {
+        if ($keep_local) {
+            $attrib_ref->{svn}->call('revert', $ext_rename);
+            unlink($ext_rename);
+        }
+        else {
+            $attrib_ref->{svn}->call('delete', $lcl_rename);
+        }
+    };
+}
+
+# -----------------------------------------------------------------------------
+# Stores the identifier of the type of tree conflict.
+package FCM::System::CM::TreeConflictKey;
+use base qw{FCM::Class::HASH};
+
+# Creates the class.
+# 'local' is the local change (e.g. edit or delete),
+# 'incoming' is the external change (e.g. add or rename),
+# 'type' is one of merge, switch, or update.
+__PACKAGE__->class({'local' => '$', 'incoming' => '$', 'type' => '$'});
+
+# Returns a label string of the form LXIY e.g. LEID for local edit,
+# incoming delete.
+sub as_string {
+    my ($self) = shift();
+    my $local = $self->get_local() eq 'replace'
+        ? 'P' : uc(substr($self->get_local(), 0, 1));
+    my $incoming = $self->get_incoming() eq 'replace'
+        ? 'P' : uc(substr($self->get_incoming(), 0, 1));
+    sprintf('L%sI%s', $local, $incoming);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::CM::ResolveConflicts
+
+=head1 DESCRIPTION
+
+Part of L<FCM::System::CM|FCM::System::CM>.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/SVN.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/SVN.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/CM/SVN.pm	(revision 5095)
@@ -0,0 +1,1005 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+package FCM::System::CM::SVN;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd};
+use FCM::Context::Event;
+use FCM::Context::Locator;
+use FCM::System::Exception;
+use Memoize qw{memoize};
+use File::Basename qw{dirname};
+use File::Spec::Functions qw{catfile rel2abs};
+use Time::Piece;
+use XML::Parser;
+
+my $E = 'FCM::System::Exception';
+
+# Settings for the default repository layout
+our %LAYOUT_CONFIG = (
+    'depth-project' => undef,
+    'depth-branch' => 3,
+    'depth-tag' => 1,
+    'dir-trunk' => 'trunk',
+    'dir-branch' => 'branches',
+    'dir-tag' => 'tags',
+    'level-owner-branch' => 2,
+    'level-owner-tag' => undef,
+    'owner' => undef,
+    'template-branch' => '{category}/{owner}/{name_prefix}{name}',
+    'template-tag' => undef,
+);
+
+# Layout configuration file basename
+our $LAYOUT_CFG_BASE = 'svn-repos-layout.cfg';
+
+# "svn log --xml" handlers.
+# -> element node start tag handlers
+my %SVN_LOG_ELEMENT_START_HANDLER_FOR = (
+#   tag        => handler
+    'logentry' => \&_get_log_handle_element_enter_logentry,
+    'path'     => \&_get_log_handle_element_enter_path,
+);
+# -> text node (after a start tag) handlers
+my %SVN_LOG_TEXT_HANDLER_FOR = (
+#   tag    => handler
+    'date' => \&_get_log_handle_text_date,
+    'path' => \&_get_log_handle_text_path,
+);
+
+our $SUBVERSION_SERVERS_CONF = catfile((getpwuid($<))[7], qw{.subversion/servers});
+
+my %ACTION_OF = (
+    'call'               => \&_call,
+    'get_info'           => \&_get_info,
+    'get_layout'         => \&_get_layout,
+    'get_layout_common'  => \&_get_layout_common,
+    'get_list'           => \&_get_list,
+    'get_log'            => \&_get_log,
+    'get_username'       => \&_get_username,
+    'get_wc_root'        => \&_get_wc_root,
+    'load_layout_config' => \&_load_layout_config,
+    'split_by_peg'       => \&_split_by_peg,
+    'stdout'             => \&_stdout,
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   layout_cfg_base => {isa => '$', default => $LAYOUT_CFG_BASE},
+        layout_config_of=> '%',
+        util            => '&',
+    },
+    {action_of => \%ACTION_OF},
+);
+
+# Calls "svn".
+sub _call {
+    my ($attrib_ref, @args) = @_;
+    my @command = ('svn', @args);
+    my $timer = $attrib_ref->{util}->timer();
+    my $rc = system(@command);
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->SHELL, \@command, $rc, $timer->());
+    if ($rc) {
+        $rc = $? == -1 ? $!
+            : $? & 127 ? $? & 127
+            :            $? >> 8
+            ;
+        return $E->throw($E->SHELL, {command_list => \@command, rc => $rc});
+    }
+    return;
+}
+
+# Invokes "svn info --xml @paths", and returns a LIST of info entries.
+memoize('_get_info');
+sub _get_info {
+    my $attrib_ref = shift();
+    my %option = ('recursive' => undef, 'revision' => undef);
+    if (@_ && ref($_[0]) && ref($_[0]) eq 'HASH') {
+        %option = (%option, %{shift()});
+    }
+    my @paths = @_;
+    if (!@paths) {
+        @paths = (q{.});
+    }
+    my (@entries, @stack);
+    my $parser = XML::Parser->new(Handlers => {
+        'Start' => sub {_get_info_handle_element_enter(\@entries, \@stack, @_)},
+        'End'   => sub {_get_info_handle_element_leave(\@entries, \@stack, @_)},
+        'Char'  => sub {_get_info_handle_text(         \@entries, \@stack, @_)},
+    });
+    $parser->parse(scalar(_stdout(
+        $attrib_ref,
+        qw{svn info --xml},
+        ($option{'recursive'} ? '--recursive' : ()),
+        ($option{'revision'} ? ('--revision', $option{'revision'}) : ()),
+        @paths,
+    )));
+    \@entries;
+}
+
+# Helper for _get_info. Handle the start tag of an XML element.
+sub _get_info_handle_element_enter {
+    my ($entries_ref, $stack_ref, $expat, $tag, %attrib) = @_;
+    # "entry": create a new entry in the list
+    if ($tag eq 'entry') {
+        push(@{$entries_ref}, {});
+    }
+    # "tree-conflict:version": need to handle differently
+    if (    $tag eq 'version'
+        &&  @{$stack_ref}
+        &&  $stack_ref->[-1] eq 'tree-conflict'
+    ) {
+        my (undef, undef, @names) = @{$stack_ref};
+        push(@names, delete($attrib{side}));
+        while (my ($key, $value) = each(%attrib)) {
+            my $name = join(':', @names, $key);
+            $entries_ref->[-1]->{$name} = delete($attrib{$key});
+        }
+    }
+    # Add current tag to stack
+    push(@{$stack_ref}, $tag);
+    # Add attributes to current entry, if appropriate
+    if (@{$entries_ref} && @{$stack_ref} >= 2 && %attrib) {
+        my (undef, undef, @names) = @{$stack_ref};
+        while (my ($key, $value) = each(%attrib)) {
+            my $name = join(':', @names, $key);
+            $entries_ref->[-1]->{$name} = $value;
+        }
+    }
+}
+
+# Helper for _get_info. Handle the end tag of an XML element.
+sub _get_info_handle_element_leave {
+    my ($entries_ref, $stack_ref, $expat, $tag) = @_;
+    pop(@{$stack_ref}) eq $tag;
+}
+
+# Helper for _get_info. Handle an XML text node.
+sub _get_info_handle_text {
+    my ($entries_ref, $stack_ref, $expat, $text) = @_;
+    if (@{$stack_ref} <= 2 || !@{$entries_ref} || $text eq "\n") {
+        return;
+    }
+    my (undef, undef, @names) = @{$stack_ref};
+    my $name = join(':', @names);
+    $entries_ref->[-1]->{$name} .= $text;
+}
+
+# Return an object containing the repository layout information of a URL.
+sub _get_layout {
+    my ($attrib_ref, $url_arg) = @_;
+    my %info = %{_get_info($attrib_ref, $url_arg)->[0]};
+    my ($url, $root, $peg_rev) = @info{'url', 'repository:root', 'revision'};
+    my $path = substr($url, length($root));
+    my $layout = _get_layout_common($attrib_ref, $root, $peg_rev, $path);
+    $layout->set_url($root . $path . '@' . $peg_rev);
+    $layout->set_username(_get_username($attrib_ref, $root));
+    $layout;
+}
+
+# Return an object containing the repository layout information of a URL.
+sub _get_layout_common {
+    my ($attrib_ref, $root, $rev, $path, $is_local) = @_;
+
+    my %layout_config = _load_layout_config(
+        $attrib_ref, ($is_local ? 'file://' . $root : $root),
+    );
+    my ($project, $branch, $category, $owner, $sub_tree);
+    my @names = split(qr{/+}msx, $path);
+    shift(@names); # element 1 should be an empty string
+
+    # Search for the project
+    my $depth = $layout_config{'depth-project'};
+    if (defined($depth)) {
+        if (@names >= $depth) {
+            my @project_names = ();
+            for (1 .. $layout_config{'depth-project'}) {
+                push(@project_names, shift(@names));
+            }
+            $project = join('/', @project_names);
+        }
+    }
+    elsif (!grep {!defined($layout_config{"dir-$_"})} qw{trunk branch tag}) {
+        # trunk, branches and tags are ALL in specific sub-directories under
+        # the project
+        my @dirs = map {$layout_config{"dir-$_"}} qw{trunk branch tag};
+        my @head = ();
+        my @tail = @names;
+        while (my $name = shift(@tail)) {
+            if (grep {$_ eq $name} @dirs) {
+                $project = join('/', @head);
+                @names = ($name, @tail);
+                last;
+            }
+            push(@head, $name);
+        }
+        if (!defined($project)) {
+            # $path does not contain the specific sub-directories that
+            # contain the trunk, branches and tags, but $path itself may be
+            # the project
+            my $target = $path . '/' . $layout_config{'dir-trunk'};
+            if (_verify_path($attrib_ref, $root, $rev, $target, $is_local)) {
+                $project = join('/', @names);
+            }
+            @names = ();
+        }
+    }
+    else {
+        # Can only assume that trunk is in a specific sub-directory under the
+        # project
+        my @head = ();
+        my @tail = @names;
+        while (my $name = shift(@tail)) {
+            if ($name eq $layout_config{'dir-trunk'}) {
+                $project = join('/', @head);
+                @names = ($name, @tail);
+                last;
+            }
+            push(@head, $name);
+        }
+        if (!defined($project)) {
+            # $path does not contain the trunk sub-directory, need to search
+            # for it
+            my @head = ();
+            my @tail = @names;
+            while (@head <= @names) {
+                my $target = join('/', @head, $layout_config{'dir-trunk'});
+                if (_verify_path($attrib_ref, $root, $rev, $target, $is_local)) {
+                    $project = join('/', @head);
+                    @names = @tail;
+                    last;
+                }
+                push(@head, shift(@tail));
+            }
+        }
+    }
+
+    # Search for the branch
+    if (defined($project) && @names) {
+        KEY:
+        for my $key (qw{trunk branch tag}) {
+            my @branch_names;
+            if ($layout_config{"dir-$key"}) {
+                if ($names[0] eq $layout_config{"dir-$key"}) {
+                    @branch_names = (shift(@names));
+                }
+                else {
+                    next KEY;
+                }
+            }
+            my $depth = $layout_config{"depth-$key"}
+                ? $layout_config{"depth-$key"} : 0;
+            if (@names >= $depth) {
+                for my $i (1 .. $depth) {
+                    my $name = shift(@names);
+                    push(@branch_names, $name);
+                    if (    $layout_config{"level-owner-$key"}
+                        &&  $layout_config{"level-owner-$key"} == $i
+                    ) {
+                        $owner = $name;
+                    }
+                }
+                $branch = join('/', @branch_names);
+                $category = $key;
+            }
+            last KEY;
+        }
+    }
+    # Remainder is the sub-tree under the branch
+    if (defined($branch)) {
+        $sub_tree = join('/', @names);
+    }
+    FCM::System::CM::SVN::Layout->new({
+        config          => \%layout_config,
+        root            => $root, 
+        path            => $path, 
+        peg_rev         => $rev,
+        project         => $project, 
+        branch          => $branch, 
+        branch_category => $category, 
+        branch_owner    => $owner, 
+        sub_tree        => $sub_tree,
+    });
+}
+
+# Return a (filtered) recursive listing of $url_arg.
+sub _get_list {
+    my ($attrib_ref, $url_arg, $filter_func) = @_;
+    my @list;
+    my ($url0, $rev) = _split_by_peg($attrib_ref, $url_arg);
+    my @items = ([$url0, 0]);
+    while (my $item = shift(@items)) {
+        my ($url, $depth) = @{$item};
+        ++$depth;
+        my @lines = _stdout($attrib_ref, qw{svn list}, $url . '@' . $rev);
+        for my $line (@lines) {
+            my ($this_name, $is_dir) = $line =~ qr{\A(.*?)(/?)\z};
+            my $this_url = $url . '/' . $this_name ;
+            my ($can_return, $can_recurse) = (1, $is_dir);
+            if (defined($filter_func)) {
+                ($can_return, $can_recurse)
+                    = $filter_func->($this_url, $this_name, $is_dir, $depth);
+            }
+            if ($can_return) {
+                push(@list, $this_url . '@' . $rev);
+            }
+            if ($can_recurse && $is_dir) {
+                push(@items, [$this_url, $depth]);
+            }
+        }
+    }
+    @list;
+}
+
+# Invokes "svn log --xml".
+sub _get_log {
+    my $attrib_ref = shift();
+    my %option = ('revision' => undef, 'stop-on-copy' => undef);
+    if (@_ && ref($_[0]) && ref($_[0]) eq 'HASH') {
+        %option = (%option, %{shift()});
+    }
+    my @paths = @_;
+    if (!@paths) {
+        @paths = (q{.});
+    }
+    my (@entries, @stack);
+    my $parser = XML::Parser->new(Handlers => {
+        'Start' => sub {_get_log_handle_element_enter(\@entries, \@stack, @_)},
+        'End'   => sub {_get_log_handle_element_leave(\@entries, \@stack, @_)},
+        'Char'  => sub {_get_log_handle_text(     \@entries, \@stack, @_)},
+    });
+    $parser->parse(scalar(_stdout(
+        $attrib_ref,
+        qw{svn log --xml -v},
+        ($option{'revision'} ? ('--revision', $option{'revision'}) : ()),
+        ($option{'stop-on-copy'} ? ('--stop-on-copy') : ()),
+        @paths,
+    )));
+    \@entries;
+}
+
+# Helper for "_get_log", handle beginning of an XML element.
+sub _get_log_handle_element_enter {
+    my ($entries_ref, $stack_ref, $expat, $tag, %attrib) = @_;
+    push(@{$stack_ref}, $tag);
+    if (exists($SVN_LOG_ELEMENT_START_HANDLER_FOR{$tag})) {
+        $SVN_LOG_ELEMENT_START_HANDLER_FOR{$tag}->(
+            $entries_ref,
+            $tag,
+            %attrib,
+        );
+    }
+}
+
+# Helper for "_get_log", handle beginning of the "logentry" element.
+sub _get_log_handle_element_enter_logentry {
+    my ($entries_ref, $tag, %attrib) = @_;
+    push(
+        @{$entries_ref},
+        {   'author'   => q{},
+            'date'     => q{},
+            'msg'      => q{},
+            'paths'    => [],
+            'revision' => $attrib{'revision'},
+        },
+    );
+}
+
+# Helper for "_get_log", handle beginning of the "path" element.
+sub _get_log_handle_element_enter_path {
+    my ($entries_ref, $tag, %attrib) = @_;
+    push(@{$entries_ref->[-1]->{'paths'}}, {%attrib, 'path' => q{}});
+}
+
+# Helper for "_get_log", handle end of an element.
+sub _get_log_handle_element_leave {
+    my ($entries_ref, $stack_ref, $expat, $tag) = @_;
+    pop(@{$stack_ref}) eq $tag;
+}
+
+# Helper for "_get_log", handle text node.
+sub _get_log_handle_text {
+    my ($entries_ref, $stack_ref, $expat, $text) = @_;
+    if (!exists($stack_ref->[-1])) {
+        return;
+    }
+    if (exists($SVN_LOG_TEXT_HANDLER_FOR{$stack_ref->[-1]})) {
+        $SVN_LOG_TEXT_HANDLER_FOR{$stack_ref->[-1]}->($entries_ref, $text);
+    }
+    elsif ( @{$entries_ref}
+        &&  exists($entries_ref->[-1]->{$stack_ref->[-1]})
+        &&  !ref($entries_ref->[-1]->{$stack_ref->[-1]})
+    ) {
+        $entries_ref->[-1]->{$stack_ref->[-1]} .= $text;
+    }
+}
+
+# Helper for "_get_log", handle text node in a "date" element.
+sub _get_log_handle_text_date {
+    my ($entries_ref, $text) = @_;
+    # "svn log --xml" may return a date with trailing spaces!
+    $text =~ s{\s+\z}{}gmsx;
+    my $head = Time::Piece->strptime(substr($text, 0, -8), '%Y-%m-%dT%H:%M:%S');
+    my $tail = substr($text, -8, -1);
+    $entries_ref->[-1]->{'date'} = $head->epoch() + $tail;
+}
+
+# Helper for "_get_log", handle text node in a "path" element.
+sub _get_log_handle_text_path {
+    my ($entries_ref, $text) = @_;
+    $entries_ref->[-1]->{'paths'}->[-1]->{'path'} .= $text;
+}
+
+# Return the username of the host of a given target URL.
+memoize('_get_username');
+sub _get_username {
+    my ($attrib_ref, $target) = @_;
+    my ($scheme, $sps) = $attrib_ref->{util}->uri_match($target);
+    my ($host) = $sps =~ qr{\A//([^/]+)(?:/|\z)}msx;
+    # Note: can use Config::IniFiles, but best to avoid another dependency.
+    # Note: not very efficient logic here, but should not yet matter.
+    my $subversion_servers_conf = exists($ENV{'FCM_SUBVERSION_SERVERS_CONF'})
+        ? $ENV{'FCM_SUBVERSION_SERVERS_CONF'} : $SUBVERSION_SERVERS_CONF;
+    my $handle
+        = $attrib_ref->{'util'}->file_load_handle($subversion_servers_conf);
+    my $is_in_section;
+    my $group;
+    LINE:
+    while (my $line = readline($handle)) {
+        chomp($line);
+        if ($line =~ qr{\A\s*(?:[#;]|\z)}msx) {
+            next LINE;
+        }
+        if ($line =~ qr{\A\s*\[\s*groups\s*\]\s*\z}msx) {
+            $is_in_section = 1;
+        }
+        elsif ($line =~ qr{\A\s*\[}msx) {
+            $is_in_section = 0;
+        }
+        elsif ($is_in_section) {
+            my ($lhs, $rhs) = $line =~ qr{\A\s*(\S+)\s*=\s*(\S+)\s*\z}msx;
+            if ($rhs) {
+                $rhs =~ s{[.]}{\\.}gmsx;
+                $rhs =~ s{[*]}{.*}gmsx;
+                $rhs =~ s{[?]}{.?}gmsx;
+                if ($host && $host =~ qr{\A$rhs\z}msx) {
+                    $group = $lhs;
+                    last LINE;
+                }
+            }
+        }
+    }
+    my $username = scalar(getpwuid($<)); # current user ID
+    if ($group) {
+        seek($handle, 0, 0);
+        LINE:
+        while (my $line = readline($handle)) {
+            chomp($line);
+            if ($line =~ qr{\A\s*(?:[#;]|\z)}msx) {
+                next LINE;
+            }
+            if ($line =~ qr{\A\s*\[\s*$group\s*\]\s*\z}msx) {
+                $is_in_section = 1;
+            }
+            elsif ($line =~ qr{\A\s*\[}msx) {
+                $is_in_section = 0;
+            }
+            elsif ($is_in_section) {
+                my ($rhs) = $line =~ qr{\A\s*username\s*=\s*(\S+)\s*\z}msx;
+                if ($rhs) {
+                    $username = $rhs;
+                    last LINE;
+                }
+            }
+        }
+    }
+    close($handle);
+    return $username;
+}
+
+# Return path to the root working copy directory of the argument.
+sub _get_wc_root {
+    my ($attrib_ref, $path) = @_;
+    $path ||= cwd();
+    my ($entries_ref) = _get_info($attrib_ref, $path);
+    if (    defined($entries_ref)
+        &&  @{$entries_ref}
+        &&  exists($entries_ref->[0]->{'wc-info:wcroot-abspath'})
+    ) {
+        return $entries_ref->[0]->{'wc-info:wcroot-abspath'};
+    }
+    if (-f $path) {
+        $path = dirname($path);
+    }
+    $path = rel2abs($path);
+    my $return;
+    if (-e catfile($path, qw{.svn entries})) {
+        while (   -e catfile($path, qw{.svn entries})
+               && $path ne dirname($path)
+        ) {
+            $return = $path;
+            $path = dirname($path);
+        }
+    }
+    else {
+        while (   !-e catfile($path, qw{.svn entries})
+               && $path ne dirname($path)
+        ) {
+            $path = dirname($path);
+            $return = $path;
+        }
+    }
+    return $return;
+}
+
+# Load layout related configuration for a given URL root.
+memoize('_load_layout_config');
+sub _load_layout_config {
+    my ($attrib_ref, $root) = @_;
+    if (exists($attrib_ref->{layout_config_of}{$root})) {
+        return %{$attrib_ref->{layout_config_of}{$root}};
+    }
+    my %site_layout_config;
+    if (exists($attrib_ref->{layout_config_of}{q{}})) {
+        %site_layout_config = %{$attrib_ref->{layout_config_of}{q{}}};
+    }
+    else {
+        %site_layout_config = %LAYOUT_CONFIG;
+        $attrib_ref->{util}->cfg_init(
+            $attrib_ref->{layout_cfg_base},
+            sub {
+                my $config_reader = shift();
+                my @unknown_entries;
+                while (defined(my $entry = $config_reader->())) {
+                    if (exists($site_layout_config{$entry->get_label()})) {
+                        my $value
+                            = $entry->get_value() ? $entry->get_value() : undef;
+                        $site_layout_config{$entry->get_label()} = $value;
+                    }
+                    else {
+                        push(@unknown_entries, $entry);
+                    }
+                }
+                if (@unknown_entries) {
+                    return $E->throw($E->CONFIG_UNKNOWN, \@unknown_entries);
+                }
+            },
+        );
+        $attrib_ref->{layout_config_of}{q{}} = {%site_layout_config};
+    }
+    $attrib_ref->{layout_config_of}{$root} = {%site_layout_config};
+    my @prop_lines = eval {
+        _stdout($attrib_ref, qw{svn propget fcm:layout}, $root);
+    };
+    if ($@) {
+        $@ = undef;
+    }
+    PROP_LINE:
+    while (defined(my $prop_line = shift(@prop_lines))) {
+        chomp($prop_line);
+        if ($prop_line =~ qr{\A\s*(?:\#|\z)}msx) { # comment line
+            next PROP_LINE;
+        }
+        ($prop_line) = $prop_line =~ qr{\A\s*(.+?)\s*\z}msx; # trim
+        my ($key, $value) = split(qr{\s*=\s*}msx, $prop_line, 2);
+        if (exists($attrib_ref->{layout_config_of}{$root}{$key})) {
+            $attrib_ref->{layout_config_of}{$root}{$key} = $value;
+        }
+    }
+    %{$attrib_ref->{layout_config_of}{$root}};
+}
+
+# Splits a URL@REV by the @.
+sub _split_by_peg {
+    my ($attrib_ref, $url) = @_;
+    $url =~ qr{\A(.*?)(?:@([^@/]+))?\z}msx;
+}
+
+# Calls "svn", return its standard output.
+sub _stdout {
+    my ($attrib_ref, @command) = @_;
+    my %value_of = %{$attrib_ref->{util}->shell_simple(\@command)};
+    if ($value_of{rc}) {
+        return $E->throw(
+            $E->SHELL,
+            {command_list => \@command, %value_of},
+            $value_of{e}
+        );
+    }
+    wantarray() ? split("\n", $value_of{o}) : $value_of{o};
+}
+
+# Return true if $path is in $repos for this $rev
+sub _verify_path {
+    my ($attrib_ref, $root, $rev, $path, $is_local) = @_;
+    if ($is_local) {
+        my $opt = $rev =~ qr{\A\d+\z}msx ? '-r' : '-t';
+        eval {
+            _stdout($attrib_ref, qw{svnlook tree -N}, $opt, $rev, $root, $path);
+        };
+        if ($@) {
+            $@ = q{};
+            return;
+        }
+        return ($root, $rev, $path);
+    }
+    else {
+        my $target = $root . '/' . $path . '@' . $rev;
+        my $url = eval {_get_info($attrib_ref, $target)->[0]->{url}};
+        if ($@ || !$url) {
+            $@ = q{};
+            return;
+        }
+        return ($root, $rev, $path);
+    }
+}
+
+#-------------------------------------------------------------------------------
+# Represent the layout information of a Subversion URL.
+package FCM::System::CM::SVN::Layout;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class({
+    config          => '%',
+    url             => '$',
+    root            => '$',
+    path            => '$',
+    peg_rev         => '$',
+    project         => '$',
+    branch          => '$',
+    branch_category => '$',
+    branch_owner    => '$',
+    sub_tree        => '$',
+    username        => {isa => '$', default => scalar(getpwuid($<))},
+});
+
+sub is_trunk {
+    $_[0]->{branch_category} && $_[0]->{branch_category} eq 'trunk';
+}
+
+sub is_branch {
+    $_[0]->{branch_category} && $_[0]->{branch_category} eq 'branch';
+}
+
+sub is_tag {
+    $_[0]->{branch_category} && $_[0]->{branch_category} eq 'tag';
+}
+
+sub is_owned_by_user {
+    my ($self, $user) = @_;
+    $user ||= $self->get_username();
+    $self->{branch_owner} && $self->{branch_owner} eq $user;
+}
+
+sub is_shared {
+    my ($self) = @_;
+    $self->{branch_owner}
+        && grep {$_ eq $self->{branch_owner}} qw{Share Config Rel};
+}
+
+sub as_string {
+    my ($self) = @_;
+    my $return = q{};
+    for my $key (qw{
+        url
+        root
+        path
+        peg_rev
+        project
+        branch
+        branch_category
+        branch_owner
+        sub_tree
+    }) {
+        my $value = $self->{$key};
+        if ($key ne 'config' && defined($value)) {
+            $return .= "$key: $value\n";
+        }
+    }
+    return $return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::CM::SVN
+
+=head1 DESCRIPTION
+
+Part of L<FCM::System::CM|FCM::System::CM>. Provides an interface for common SVN
+functionalities used in the FCM CM sub-system.
+
+=head1 METHODS
+
+This is a sub-class of L<FCM::Class::CODE|FCM::Class::CODE>.
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Return a new instance of this class. %attrib accepts a single "util" key for an
+instance of an L<FCM::Util|FCM::Util> object.
+
+=item $instance->call(@args)
+
+Call the command line "svn" with a list of arguments in @args.
+
+=item $instance->get_info(@path)
+=item $instance->get_info(\%option, @path)
+
+Invokes "svn info --xml @paths", and returns a LIST of info entries. If @paths
+is not specified, use ("."). If %option is specified, it may contain the keys:
+
+=over 4
+
+=item recursive
+
+If value of this key is not undef, add --recursive to "svn info".
+
+=item revision
+
+If value of this key is not undef, add --revision VALUE to "svn info".
+
+=back
+
+Each info entry is a HASH with keys reflecting the tag or attribute name in an
+entry element. The original hierarchy below the entry element is delimited by a
+colon in the name. For example, a return structure may look like this:
+    [   {   'commit:author' => 'fred',
+            'commit:date' => '2011-11-09T15:41:14.514665Z',
+            'commit:revision' => '4549',
+            'kind' => 'dir',
+            'path' => 'trunk',
+            'revision' => '4552',
+            'repository:root' => 'svn://host/my-repos',
+            'repository:uuid' => '91f685bf-fbee-0310-99e6-f3aa9e660bd5'
+            'url' => 'svn://host/my-repos/FCM/trunk',
+        },
+    ]
+
+=item $instance->get_layout($url)
+
+Return an instance of L<FCM::System::CM::SVN::Layout|/FCM::System::CM::SVN::Layout>
+containing the repository layout information of $url.
+
+=item $instance->get_layout_common($root, $rev, $path, $is_local)
+
+Return an instance of L<FCM::System::CM::SVN::Layout|/FCM::System::CM::SVN::Layout>
+containing the repository layout information for $path in $root at $rev. If
+$is_local is true, use "svnlook" to verify the existence of $path in $root
+at $rev. Otherwise, it uses "svn info" instead. If $rev is assumed to be a
+transaction if it is not numeric.
+
+=item $instance->get_list($url_arg, $filter_func)
+
+Call "svn list" multiple times to obtain a recursive listing of files and
+directories under $url_arg. Return a list containing the listing. If
+$filter_func is defined, it should be a CODE reference, which would be invoked
+for each file/directory found. It should have the interface:
+
+    ($can_return, $can_recurse)
+        = $filter_func->($this_url, $this_name, $is_dir, $depth);
+
+where $this_url is the URL of the file/directory found, $this_name is the
+base name of the file/directory found, $is_dir is true if it is a directory,
+$depth is the directory depth of $this_url relative to $url_arg.
+
+The $filter_func CODE reference should return a 2-element list ($can_return,
+$can_recurse). The get_list method will only return $this_url in the listing
+if $can_return is set to true. If $is_dir is true and $can_recurse is true, the
+get_list method will go down to do more listing in $this_url.
+
+=item $instance->get_log(@path)
+=item $instance->get_log(\%option, @path)
+
+Invokes "svn log --xml".  If @paths is not specified, use ("."). If %option is
+specified, it may contain the keys:
+
+=over 4
+
+=item revision
+
+If value of this key is not undef, add --revision VALUE to "svn log".
+
+=item stop-on-copy
+
+If value of this key is not undef, add --stop-on-copy to "svn log".
+
+=back
+
+Returns an ARRAY reference. Each element is a data structure that represents a
+log entry. The data structure should look like:
+    [   {   'author'   => $author,
+            'date'     => $date, # seconds since epoch
+            'msg'      => $msg,
+            'paths'    => [
+                {   'path'          => $path,
+                    'action'        => $action,
+                    'copyfrom-path' => $p,
+                    'copyfrom-rev'  => $r,
+                },
+                # ...
+            ],
+            'revision' => $revision,
+        },
+    ]
+
+=item $instance->get_username($target)
+
+Return the user name associated with $target.
+
+=item $instance->get_wc_root($path)
+
+Return the path to the root working copy directory of the argument.
+
+=item $instance->load_layout_config($root)
+
+Return a HASH (not a reference) containing the layout configuration of $root.
+See %LAYOUT_CONFIG for default settings. $root should be the URL to a
+repository root.
+
+=item $instance->split_by_peg($location)
+
+Split a location string (either a URL@PEG or a PATH@PEG) and return a
+two-element list: either (URL, PEG) or (PATH, PEG).
+
+=item $instance->stdout(@command)
+
+Call a @command, capture and return the STDOUT on success. In scalar context,
+return the STDOUT as-is. In array context, return it as a list of lines with the
+new line characters removed.
+
+=back
+
+=head1 EXCEPTION
+
+Methods in this class may throw an
+L<FCM::System::Exception|FCM::System::Exception> on error.
+
+=head1 FCM::System::CM::SVN::Layout
+
+The FCM::System::CM::SVN::Layout class inherits from
+L<FCM::Class::HASH|FCM::Class::HASH>. An instance represents the layout
+information in a Subversion URL based on the default or specified FCM layout
+information. It has the following attributes:
+
+=over 4
+
+=item config
+
+is a HASH containing the layout configuration applied to this URL.
+Valid keys and their default values are:
+
+=over 4
+
+=item depth-project => undef
+Number of sub-directories used by the name of a project.
+
+=item depth-branch => 3
+Number of sub-directories (under "branches") used by the name of branch.
+
+=item depth-tag => 1
+Number of sub-directories (under "tags") used by the name of branch.
+
+=item dir-trunk => 'trunk'
+Name of the master/trunk directory.
+
+=item dir-branch => 'branches'
+Name of the directory where all branches live. May be empty.
+
+=item dir-tag => 'tags'
+Name of the directory where all tags live. May be empty.
+
+=item level-owner-branch => 2
+Sub-directory level in the name of a branch containing the its owner.
+
+=item level-owner-branch => undef
+Sub-directory level in the name of a tag containing the its owner.
+
+=item template-branch => '{category}/{owner}/{name_prefix}{name}'
+Branch name template.
+
+=item template-tag => undef
+Tag name template.
+
+=back
+
+=item url
+
+is the full URL@PEG.
+
+=item root
+
+is the repository root.
+
+=item path
+
+is the path below the repository root.
+
+=item peg_rev
+
+is the (peg) revision of the URL.
+
+=item project
+
+is the project name in the URL. It is undef if the URL does not contain a valid
+project name for the given repository. An empty string is possible, for example,
+if the layout means that the trunk is at the root level.
+
+=item branch
+
+is the "branch" name in the URL, (which may be the name of the master/trunk
+branch or the name of a tag). It is undef if the URL does not contain a valid
+branch name for the given repository.
+
+=item branch_category
+
+is the category (i.e. "trunk", "branch" or "tag") of the branch.
+
+=item branch_owner
+
+is the owner of the branch, if it can be derived from the URL.
+
+=item sub_tree
+
+is the path in the URL under the branch of a project tree. It is undef if the
+URL is not at or below the level of a branch of the project tree. An empty
+string means the that the URL is at root level of the project tree.
+
+=back
+
+An FCM::System::CM::SVN::Layout instance has the following convenient methods:
+
+=over 4
+
+=item $layout->is_trunk()
+
+The URL is in the trunk of a project.
+
+=item $layout->is_branch()
+
+The URL is in a branch of a project.
+
+=item $layout->is_tag()
+
+The URL is in a tag of a project.
+
+=item $layout->is_owned_by_user($user)
+
+The URL is in a branch owned by $user. If $user is not defined, it defaults to
+the current user ID.
+
+=item $layout->is_shared()
+
+The URL is in a shared branch.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Exception.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Exception.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Exception.pm	(revision 5095)
@@ -0,0 +1,428 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM::System::Exception;
+use base qw{FCM::Exception};
+use Scalar::Util qw{blessed};
+
+use constant {
+    BUILD_SOURCE     => 'BUILD_SOURCE',
+    BUILD_SOURCE_SYN => 'BUILD_SOURCE_SYN',
+    BUILD_TARGET     => 'BUILD_TARGET',
+    BUILD_TARGET_BAD => 'BUILD_TARGET_BAD',
+    BUILD_TARGET_CYC => 'BUILD_TARGET_CYC',
+    BUILD_TARGET_DEP => 'BUILD_TARGET_DEP',
+    BUILD_TARGET_DUP => 'BUILD_TARGET_DUP',
+    CACHE_LOAD       => 'CACHE_LOAD',
+    CACHE_TYPE       => 'CACHE_TYPE',
+    CM_ALREADY_EXIST => 'CM_ALREADY_EXIST',
+    CM_ARG           => 'CM_ARG',
+    CM_BRANCH_NAME   => 'CM_BRANCH_NAME',
+    CM_BRANCH_SOURCE => 'CM_BRANCH_SOURCE',
+    CM_CHECKOUT      => 'CM_CHECKOUT',
+    CM_LOG_EDIT_DELIMITER => 'CM_LOG_EDIT_DELIMITER',
+    CM_LOG_EDIT_NULL => 'CM_LOG_EDIT_NULL',
+    CM_OPT_ARG       => 'CM_OPT_ARG',
+    CM_PROJECT_NAME  => 'CM_PROJECT_NAME',
+    CM_REPOSITORY    => 'CM_REPOSITORY',
+    CONFIG_CONFLICT  => 'CONFIG_CONFLICT',
+    CONFIG_INHERIT   => 'CONFIG_INHERIT',
+    CONFIG_MODIFIER  => 'CONFIG_MODIFIER',
+    CONFIG_NS        => 'CONFIG_NS',
+    CONFIG_NS_VALUE  => 'CONFIG_NS_VALUE',
+    CONFIG_UNKNOWN   => 'CONFIG_UNKNOWN',
+    CONFIG_VALUE     => 'CONFIG_VALUE',
+    CONFIG_VERSION   => 'CONFIG_VERSION',
+    COPY             => 'COPY',
+    DEST_CLEAN       => 'DEST_CLEAN',
+    DEST_CREATE      => 'DEST_CREATE',
+    DEST_LOCKED      => 'DEST_LOCKED',
+    EXPORT_ITEMS_SRC => 'EXPORT_ITEMS_SRC',
+    EXTRACT_LOC_BASE => 'EXTRACT_LOC_BASE',
+    EXTRACT_MERGE    => 'EXTRACT_MERGE',
+    EXTRACT_NS       => 'EXTRACT_NS',
+    MIRROR           => 'MIRROR',
+    MIRROR_NULL      => 'MIRROR_NULL',
+    MIRROR_SOURCE    => 'MIRROR_SOURCE',
+    MIRROR_TARGET    => 'MIRROR_TARGET',
+    MAKE             => 'MAKE',
+    MAKE_ARG         => 'MAKE_ARG',
+    MAKE_CFG         => 'MAKE_CFG',
+    MAKE_CFG_FILE    => 'MAKE_CFG_FILE',
+    MAKE_PROP_NS     => 'MAKE_PROP_NS',
+    MAKE_PROP_VALUE  => 'MAKE_PROP_VALUE',
+    SHELL            => 'SHELL',
+};
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Exception
+
+=head1 SYNOPSIS
+
+    eval {
+        # ...
+        FCM::System::Exception->throw($code, $ctx);
+        # ...
+        FCM::System::Exception->throw($code, $ctx, {exception => $e});
+        # ...
+    };
+    if (my $e = $@) {
+        if (FCM::System::Exception->caught($e)) {
+            # do something ...
+        }
+        else {
+            # do something else ...
+        }
+    }
+
+=head1 DESCRIPTION
+
+This exception represents an error condition in an FCM sub-system. It is a
+sub-class of L<FCM::Exception|FCM::Exception>.
+
+=head1 CONSTANTS
+
+The following are known error code:
+
+=over 4
+
+=item FCM::System::Exception->BUILD_SOURCE
+
+The build sub-system fails because a specified source does not exist. Expects
+$e->get_ctx() to return the source path.
+
+=item FCM::System::Exception->BUILD_SOURCE_SYN
+
+The build sub-system fails because a specified source has a syntax error.
+Expects $e->get_ctx() to return an ARRAY reference containing the source path
+and the line number where the error occurs.
+
+=item FCM::System::Exception->BUILD_TARGET
+
+The build sub-system fails because a target does not exist when it is supposed
+to be updated. Expects $e->get_ctx() to return an instance of
+L<FCM::Context::Make::Build::Target|FCM::Context::Make::Build/FCM::Context::Make::Build::Target>.
+
+=item FCM::System::Exception->BUILD_TARGET_BAD
+
+The build sub-system fails because the user has specified invalid targets.
+Expects $e->get_ctx() to return an ARRAY reference of the bad target keys.
+
+=item FCM::System::Exception->BUILD_TARGET_CYC
+
+The build sub-system fails due to cyclic dependency in a target. Expects
+$e->get_ctx() to return a HASH {$key => {'keys' => \@stack}, ...}, where each
+$key is the ID of a problematic target and the @stack is an ARRAY reference of a
+stack of target keys where the problem is detected.
+
+=item FCM::System::Exception->BUILD_TARGET_DEP
+
+The build sub-system fails because some targets have missing dependencies.
+Expects $e->get_ctx() to return a HASH
+{$key => {'keys' => \@stack, 'values' => [$dep_key, $dep_type]}, ...}, where each
+$key is the ID of a problematic target, the @stack is an ARRAY reference of a
+stack of target keys where the problem is detected, and the
+[$dep_key, $dep_type] ARRAY contains the key and type of the dependency.
+
+=item FCM::System::Exception->BUILD_TARGET_DUP
+
+The build sub-system fails because there are multiple versions of a build
+target. Expects $e->get_ctx() to return a HASH
+{$key => {'keys' => \@stack, 'values' => \@ns}, ...} where each $key is the ID of a
+problematic target, the @stack is an ARRAY
+reference of a stack of target keys where the problem is detected,
+and @ns contains the name-spaces of the sources that give the same target key.
+
+=item FCM::System::Exception->CACHE_LOAD
+
+The system is unable to load a cache from a make destination. Expects
+$e->get_ctx() to return the path it fails to load; and the $e->get_exception()
+to return the original exception that triggers this failure.
+
+=item FCM::System::Exception->CACHE_TYPE
+
+The system loaded a cache into a data structure, but the data structure is not
+the expected object type. Expects $e->get_ctx() to return the path to the cache.
+
+=item FCM::System::Exception->CM_ALREADY_EXIST
+
+Attempt to create a target that already exists. Expects $e->get_ctx() to return the
+target URL.
+
+=item FCM::System::Exception->CM_ARG
+
+Attempt to supply a bad argument. Expects $e->get_ctx() to return the bad value.
+
+=item FCM::System::Exception->CM_BRANCH_NAME
+
+Attempt to create a branch with a bad name. Expects $e->get_ctx() to return the
+bad name.
+
+=item FCM::System::Exception->CM_BRANCH_SOURCE
+
+Attempt to create a branch with an invalid source. Expects $e->get_ctx() to
+return the source.
+
+=item FCM::System::Exception->CM_CHECKOUT
+
+Attempt to checkout to an existing working copy. Expects $e->get_ctx() to return
+an ARRAY containing the target path and the URL it is pointing to.
+
+=item FCM::System::Exception->CM_LOG_EDIT_DELIMITER
+
+The commit message delimiter is modified after an edit.
+
+=item FCM::System::Exception->CM_LOG_EDIT_NULL
+
+The commit message is empty after an edit.
+
+=item FCM::System::Exception->CM_OPT_ARG
+
+Attempt to supply a bad argument to a valid option. Expects $e->get_ctx() to
+return the option key and the bad value.
+
+=item FCM::System::Exception->CM_PROJECT_NAME
+
+Attempt to create a project with a bad name. Expects $e->get_ctx() to return the
+bad name.
+
+=item FCM::System::Exception->CM_REPOSITORY
+
+Attempt to access an invalid repository. Expects $e->get_ctx() to return the
+bad repository.
+
+=item FCM::System::Exception->CONFIG_CONFLICT
+
+In a make configuration file, a declaration attempts to modify a value that is
+inherited from a previous make, and considered read-only. Expects $e->get_ctx()
+to return a L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object.
+
+=item FCM::System::Exception->CONFIG_INHERIT
+
+In a make configuration file, a declaration attempts to inherit from a make that
+is either incomplete or failed. Expects $e->get_ctx() to return a
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object.
+
+=item FCM::System::Exception->CONFIG_MODIFIER
+
+In a make configuration file, a modifier of in a declaration is incorrect.
+Expects $e->get_ctx() to return a
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object and
+$e->get_exception() to return (if any) the original exception that triggers this
+failure.
+
+=item FCM::System::Exception->CONFIG_NS
+
+In a make configuration file, a declaration is missing a required name-space, or
+the name-space declaration is incorrect. Expects $e->get_ctx() to return a
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object and
+$e->get_exception() to return (if any) the original exception that triggers this
+failure.
+
+=item FCM::System::Exception->CONFIG_NS_VALUE
+
+In a make configuration file, the name-space of a declaration is incompatible
+with the value. (E.g. the number of name-space elements does not match with the
+number of words in a value.) Expects $e->get_ctx() to return a
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object and
+$e->get_exception() to return (if any) the original exception that triggers this
+failure.
+
+=item FCM::System::Exception->CONFIG_UNKNOWN
+
+In a make configuration file, the label of a declaration is unrecognised by the
+system. Expects $e->get_ctx() to return a reference to an ARRAY containing
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> objects.
+
+=item FCM::System::Exception->CONFIG_VALUE
+
+In a make configuration file, the value of a declaration is incorrect. Expects
+$e->get_ctx() to return a L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry>
+object and $e->get_exception() to return (if any) the original exception that
+triggers this failure.
+
+=item FCM::System::Exception->CONFIG_VERSION
+
+A make configuration file requires certain version of FCM. Expects
+$e->get_ctx() to return a L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry>
+object, and the current FCM version.
+
+=item FCM::System::Exception->COPY
+
+The system fails to perform a file copy. Expects $e->get_ctx() to return a
+2-element ARRAY reference to represent the source and the target, and the
+$e->get_exception() to return the original exception that triggers this failure.
+
+=item FCM::System::Exception->DEST_CLEAN
+
+A destination path cannot be removed. Expects $e->get_ctx() to return the path
+that the system fails to remove, and $e->get_exception() to return the original
+exception that triggers this failure.
+
+=item FCM::System::Exception->DEST_CREATE
+
+The system is unable to create a path at a make destination. Expects
+$e->get_ctx() to return the path it fails to create; and the $e->get_exception()
+to return the original exception that triggers this failure.
+
+=item FCM::System::Exception->DEST_LOCKED
+
+A lock file exists at the destination. Expects $e->get_ctx() to return the
+path to the lock file.
+
+=item FCM::System::Exception->EXPORT_ITEMS_SRC
+
+The system fails because the source location is not specified.
+
+=item FCM::System::Exception->EXTRACT_LOC_BASE
+
+The system fails to determine the location of a base tree of a project. Expects
+$e->get_ctx() to return the name-space of the project.
+
+=item FCM::System::Exception->EXTRACT_MERGE
+
+The system fails to merge the sources of an extract target. Expects
+$e->get_ctx() to return a HASH reference with the following keys:
+
+=over 4
+
+=item target
+
+The FCM::Context::Make::Extract::Target object associated with this failure.
+
+=item output
+
+The path to a file containing the failed merge output.
+
+=item keys_done
+
+The keys of the source trees providing the source files for this target that
+have already been merged.
+
+=item key
+
+The key of the source tree providing the source file for this target that causes
+the merge conflict.
+
+=item keys_left
+
+The keys of the source trees providing the source files for this target that are
+yet to be merged.
+
+=back
+
+=item FCM::System::Exception->EXTRACT_NS
+
+The system fails because there are some extract declarations for the name-spaces
+but the settings are not used. Expects $e->get_ctx() to return an ARRAY of bad
+name-spaces.
+
+=item FCM::System::Exception->MIRROR
+
+The mirror operation failed. Expects $e->get_ctx() to return a reference of a
+2-element ARRAY containing the source and the target of the mirror, and
+$e->get_exception() to return the original exception that triggers this failure.
+
+=item FCM::System::Exception->MIRROR_NULL
+
+The mirror step failed because a target is not specified. The $e->get_ctx() is
+undefined.
+
+=item FCM::System::Exception->MIRROR_SOURCE
+
+The mirror step failed because the destination of a completed step in the make
+is not suitable for mirroring. Expects $e->get_ctx() to return an ARRAY
+reference containing the names of the unsuitable steps.
+
+=item FCM::System::Exception->MIRROR_TARGET
+
+The mirror step failed to create the target. Expects $e->get_ctx() to
+return the target of the mirror, and $e->get_exception() to return the original
+exception that triggers this failure.
+
+=item FCM::System::Exception->MAKE
+
+A named step in a make is not implemented. Expects $e->get_ctx() to return the
+name of the step.
+
+=item FCM::System::Exception->MAKE_ARG
+
+A make sub-system fails because of bad command line arguments. Expects
+$e->get_ctx() to return an ARRAY reference of something like this:
+
+    my @list = @{$e->get_ctx()};
+    for (@list) {
+        my ($arg_index, $arg) = @{$_};
+        warn("Argument $arg_index ($arg) is invalid\n");
+    }
+
+=item FCM::System::Exception->MAKE_CFG
+
+A make sub-system fails because it can find no configuration.
+
+=item FCM::System::Exception->MAKE_CFG_FILE
+
+A make sub-system fails because it cannot file a named configuration file.
+Expects $e->get_ctx() to return the configuration file name.
+
+=item FCM::System::Exception->MAKE_PROP_NS
+
+A make sub-system fails because a property is specified with an invalid
+name-space. Expects $e->get_ctx() to return an ARRAY reference of something like
+this:
+
+    my @list = @{$e->get_ctx()};
+    for (@list) {
+        my ($step_name, $prop_name, $ns, $value) = @{$_};
+        warn("{$prop_name}[$ns]: prop ns is invalid\n");
+    }
+
+=item FCM::System::Exception->MAKE_PROP_VALUE
+
+A make sub-system fails because a property is specified with an invalid
+value. Expects $e->get_ctx() to return an ARRAY reference of something like
+this:
+
+    my @list = @{$e->get_ctx()};
+    for (@list) {
+        my ($step_name, $prop_name, $ns, $value) = @{$_};
+        warn("{$prop_name}[$ns] = $value: prop value is bad\n");
+    }
+
+=item FCM::System::Exception->SHELL
+
+A shell command returns an error. Expects $e->get_ctx() to return a HASH
+reference containing {command_list}, an ARRAY reference representing the
+command; {rc}, the return code; {out}, the standard output of the command and
+{err}, the standard error of the command. Expects $e->get_exception() to return
+the standard error of the command.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make.pm	(revision 5095)
@@ -0,0 +1,452 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::ConfigEntry;
+use FCM::Context::Event;
+use FCM::System::Exception;
+use FCM::System::Make::Build;
+use FCM::System::Make::Extract;
+use FCM::System::Make::Mirror;
+use FCM::System::Make::Preprocess;
+use FCM::System::Make::Share::Config;
+use FCM::System::Make::Share::Dest;
+use File::Basename qw{basename};
+use File::Copy qw{copy};
+use File::Path qw{rmtree};
+use File::Spec::Functions qw{catfile};
+use File::Temp;
+use POSIX qw{strftime};
+use Sys::Hostname qw{hostname};
+
+# Actions of the named common steps
+my %ACTION_OF = (
+    'config-parse' => \&_config_parse,
+    'dest-init'    => \&_dest_init   ,
+);
+# Alias to class name
+my $E = 'FCM::System::Exception';
+# The initial steps to run
+my @INIT_STEPS = (qw{config-parse dest-init});
+# The name of the system
+our $NAME = 'make';
+# Base name of common configuration file
+our $CFG_BASE = 'make.cfg';
+# A map of named helper utilities
+our %SHARED_UTIL_OF = (
+    'config' => 'FCM::System::Make::Share::Config',
+    'dest'   => 'FCM::System::Make::Share::Dest'  ,
+);
+# A map of named subsystems
+our %SUBSYSTEM_OF = (
+    'build'      => 'FCM::System::Make::Build'     ,
+    'extract'    => 'FCM::System::Make::Extract'   ,
+    'mirror'     => 'FCM::System::Make::Mirror'    ,
+    'preprocess' => 'FCM::System::Make::Preprocess',
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   cfg_base       => {isa => '$', default => $CFG_BASE},
+        name           => {isa => '$', default => $NAME},
+        shared_util_of => '%',
+        subsystem_of   => '%',
+        util           => '&',
+    },
+    {init => \&_init, action_of => {main => \&_main}},
+);
+
+# Initialises an instance.
+sub _init {
+    my $attrib_ref = shift();
+    for (
+        ['shared_util_of', \%SHARED_UTIL_OF],
+        ['subsystem_of'  , \%SUBSYSTEM_OF  ],
+    ) {
+        my ($key, $hash_ref) = @{$_};
+        while (my ($id, $class) = each(%{$hash_ref})) {
+            if (!exists($attrib_ref->{$key}{$id})) {
+                $attrib_ref->{$key}{$id} = $class->new({
+                    'shared_util_of' => $attrib_ref->{'shared_util_of'},
+                    'subsystem_of'   => $attrib_ref->{'subsystem_of'},
+                    'util'           => $attrib_ref->{'util'},
+                });
+            }
+        }
+    }
+    $attrib_ref->{util}->cfg_init(
+        $attrib_ref->{cfg_base},
+        sub {
+            my $config_reader = shift();
+            my @unknown_entries;
+            while (defined(my $entry = $config_reader->())) {
+                my ($id, $label) = split(qr{\.}msx, $entry->get_label(), 2);
+                if (exists($attrib_ref->{subsystem_of}{$id})) {
+                    my $subsystem = $attrib_ref->{subsystem_of}{$id};
+                    if (!$subsystem->config_parse_class_prop($entry, $label)) {
+                        push(@unknown_entries, $entry);
+                    }
+                }
+                else {
+                    push(@unknown_entries, $entry);
+                }
+            }
+            if (@unknown_entries) {
+                return $E->throw($E->CONFIG_UNKNOWN, \@unknown_entries);
+            }
+        },
+    );
+}
+
+# Sets up the destination.
+sub _config_parse {
+    my ($attrib_ref, $m_ctx, @args) = @_;
+    my $entry_callback_ref = sub {
+        my ($entry) = @_;
+        print({$attrib_ref->{handle_cfg}} $entry->as_string(), "\n");
+    };
+    $attrib_ref->{shared_util_of}{config}->parse(
+        $entry_callback_ref, $m_ctx, @args,
+    );
+}
+
+# Sets up the destination.
+sub _dest_init {
+    my ($attrib_ref, $m_ctx) = @_;
+    my $DEST_UTIL = $attrib_ref->{shared_util_of}{dest};
+    $DEST_UTIL->dest_init($m_ctx);
+
+    # Move temporary log file to destination
+    my $now = strftime("%Y%m%dT%H%M%S", gmtime());
+    my $log = $DEST_UTIL->path($m_ctx, 'sys-log');
+    my $log_actual = sprintf("%s-%s", $log, $now);
+    _symlink(basename($log_actual), $log);
+    (       close($attrib_ref->{handle_log})
+        &&  copy($attrib_ref->{handle_log}->filename(), $log)
+        &&  open(my $handle_log, '>>', $log)
+    ) || return $E->throw($E->DEST_CREATE, $log, $!);
+    _symlink(
+        $DEST_UTIL->path({'name' => $m_ctx->get_name()}, 'sys-log'),
+        $DEST_UTIL->path($m_ctx, 'sys-log-symlink'),
+    );
+    my $log_ctx = $attrib_ref->{util}->util_of_report()->get_ctx($m_ctx);
+    $log_ctx->set_handle($handle_log);
+
+    # Saves as parsed config
+    my $cfg = $DEST_UTIL->path($m_ctx, 'sys-config-as-parsed');
+    (       close($attrib_ref->{handle_cfg})
+        &&  copy($attrib_ref->{handle_cfg}->filename(), $cfg)
+    ) || return $E->throw($E->DEST_CREATE, $cfg, $!);
+    _symlink(
+        $DEST_UTIL->path({'name' => $m_ctx->get_name()}, 'sys-config-as-parsed'),
+        $DEST_UTIL->path($m_ctx, 'sys-config-as-parsed-symlink'),
+    );
+}
+
+# The main function of an instance of this class.
+sub _main {
+    my ($attrib_ref, $option_hash_ref, @args) = @_;
+    my @bad_args;
+    for my $i (0 .. $#args) {
+        if (index($args[$i], "=") < 0) {
+            push(@bad_args, [$i, $args[$i]]);
+        }
+    }
+    if (@bad_args) {
+        return $E->throw($E->MAKE_ARG, \@bad_args);
+    }
+    # Starts the system
+    my $m_ctx = FCM::Context::Make->new({option_of => $option_hash_ref});
+    if ($m_ctx->get_option_of('name')) {
+        $m_ctx->set_name($m_ctx->get_option_of('name'));
+    }
+    my $T = sub {_timer_wrap($attrib_ref, $m_ctx, @_)};
+    my $DEST_UTIL = $attrib_ref->{shared_util_of}{dest};
+    eval {$T->(
+        sub {
+            my %attrib = (
+                %{$attrib_ref},
+                handle_log => File::Temp->new(),
+                handle_cfg => File::Temp->new(),
+            );
+            $attrib_ref->{util}->util_of_report()->add_ctx(
+                $m_ctx, # key
+                {   handle    => $attrib{handle_log},
+                    type      => undef,
+                    verbosity => $attrib_ref->{util}->util_of_report()->HIGH,
+                },
+            );
+            my $version = $attrib_ref->{util}->version();
+            $attrib_ref->{util}->event(
+                FCM::Context::Event->FCM_VERSION, "FCM $version",
+            );
+            for my $step (@INIT_STEPS) {
+                $T->(sub {$ACTION_OF{$step}->(\%attrib, $m_ctx, @args)}, $step);
+            }
+            my $prev_m_ctx = $m_ctx->get_prev_ctx();
+            if (defined($prev_m_ctx)) {
+                for my $step (keys(%{$prev_m_ctx->get_ctx_of()})) {
+                    if (!grep {$_ eq $step} @{$m_ctx->get_steps()}) {
+                        delete($prev_m_ctx->get_ctx_of()->{$step});
+                    }
+                }
+            }
+            for my $step (@{$m_ctx->get_steps()}) {
+                my $ctx = $m_ctx->get_ctx_of($step);
+                if (!defined($ctx)) {
+                    return $E->throw($E->MAKE, $step);
+                }
+                my $id_of_class = $ctx->get_id_of_class();
+                if (!exists($attrib_ref->{subsystem_of}{$id_of_class})) {
+                    return $E->throw($E->MAKE, $step);
+                }
+                my $impl = $attrib_ref->{subsystem_of}{$id_of_class};
+                $ctx->set_status($m_ctx->ST_INIT);
+                if ($ctx->can('set_dest')) {
+                    $ctx->set_dest(
+                        $DEST_UTIL->path($m_ctx, 'target', $ctx->get_id()),
+                    );
+                }
+                eval {$T->(sub {$impl->main($m_ctx, $ctx)}, $step)};
+                if (my $e = $@) {
+                    $ctx->set_status($m_ctx->ST_FAILED);
+                    die($e);
+                }
+                $ctx->set_status($m_ctx->ST_OK);
+                if (    defined($prev_m_ctx)
+                    &&  exists($prev_m_ctx->get_ctx_of()->{$step})
+                ) {
+                    delete($prev_m_ctx->get_ctx_of()->{$step});
+                }
+            }
+        },
+    )};
+    if (my $e = $@) {
+        $m_ctx->set_status($m_ctx->ST_FAILED);
+        $m_ctx->set_error($e);
+        $attrib_ref->{util}->event(FCM::Context::Event->E, $e);
+        _main_finally($attrib_ref, $m_ctx);
+        die("\n");
+    }
+    $m_ctx->set_status($m_ctx->ST_OK);
+    $DEST_UTIL->save(
+        [$attrib_ref->{shared_util_of}{config}->unparse($m_ctx)],
+        $m_ctx,
+        'sys-config-on-success',
+    );
+    _symlink(
+        $DEST_UTIL->path({'name' => $m_ctx->get_name()}, 'sys-config-on-success'),
+        $DEST_UTIL->path($m_ctx, 'sys-config-on-success-symlink'),
+    );
+    _main_finally($attrib_ref, $m_ctx);
+    return $m_ctx;
+}
+
+# Helper to run the "finally" part of "_main".
+sub _main_finally {
+    my ($attrib_ref, $m_ctx) = @_;
+    $m_ctx->set_inherit_ctx_list([]);
+    $m_ctx->set_prev_ctx(undef);
+    $attrib_ref->{shared_util_of}{dest}->dest_done($m_ctx);
+    my $log_ctx = $attrib_ref->{util}->util_of_report()->del_ctx($m_ctx);
+    close($log_ctx->get_handle());
+}
+
+# Wrap "symlink".
+sub _symlink {
+    my ($source, $target) = @_;
+    if (-l $target && readlink($target) eq $source) {
+        return;
+    }
+    if (-e $target || -l $target) {
+        rmtree($target);
+    }
+    symlink($source, $target) || return $E->throw($E->DEST_CREATE, $target, $!);
+}
+
+# Wraps a piece of code with timer events.
+sub _timer_wrap {
+    my ($attrib_ref, $m_ctx, $code_ref, @names) = @_;
+    my @event_args = (
+        FCM::Context::Event->TIMER,
+        join(
+            q{ },
+            $attrib_ref->{name},
+            ($m_ctx->get_name() ? $m_ctx->get_name() : ()),
+            @names,
+        ),
+        time(),
+    );
+    $attrib_ref->{util}->event(@event_args);
+    my $timer = $attrib_ref->{util}->timer();
+    my $return = eval {wantarray() ? [$code_ref->()] : $code_ref->()};
+    my $e = $@;
+    $attrib_ref->{util}->event(@event_args, $timer->(), $e);
+    if ($e) {
+        die($e);
+    }
+    return (wantarray() ? @{$return} : $return);
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make;
+    my $system = FCM::System::Make->new(\%attrib);
+    $system->(\%option);
+
+
+=head1 DESCRIPTION
+
+Invokes the FCM make system.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance. The %attrib may contain the following:
+
+=over 4
+
+=item cfg_base
+
+The base name of the common (site/user) configuration file. (default="make.cfg")
+
+=item name
+
+The name of this sub-system. (default="make")
+
+=item shared_util_of
+
+A HASH to map the names to the classes of the named helper utilities for the
+make system and its sub-systems. (default = %FCM::System::Make::SHARED_UTIL_OF)
+
+=item subsystem_of
+
+A HASH to map the names to the classes of the subsystems. (default =
+%FCM::System::Make::SUBSYSTEM_OF)
+
+=item util
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $system->(\%option)
+
+Invokes a make. The %option may contain the following:
+
+=over 4
+
+=item config-file
+
+The path to the configuration file. (default = $PWD/fcm-make.cfg)
+
+=item ignore-lock
+
+This flag can be used to ignore the lock file. The system creates a lock file in
+the destination to prevent another command from running in the same destination.
+If this flag is set, the system will continue even if it encounters a lock file
+in the destination. (default = false)
+
+=item jobs
+
+The number of (child) jobs that can be used to run parallel tasks.
+
+=item new
+
+A flag to tell the system to perform a new make. (default = false, i.e.
+incremental make)
+
+=back
+
+Throws L<FCM::System::Exception|FCM::System::Exception> on error.
+
+=back
+
+=head1 SUBSYSTEMS
+
+A subsystem of the make system should be a CODE-based class that implements a
+particular set of methods. (Some of these methods can be imported from
+L<FCM::System::Make::Share::Subsystem|FCM::System::Make::Share::Subsystem>.) The
+methods that should be implemented are:
+
+=over 4
+
+=item $subsystem_class->new(\%attrib)
+
+Creates a new instance of the subsystem. The make system passes the
+I<shared_util_of>, I<subsystem_of> and I<util> attributes to this method.
+
+=item $subsystem->config_parse($ctx,$entry,$label)
+
+Reads the settings of $entry into the $ctx. The $label is the configuration
+entry label in the context of the subsystem. (This is normally the
+$entry->get_label() but with the context ID prefix removed.). Returns true on
+success.
+
+=item $subsystem->config_parse_inherit_hook($ctx,$i_ctx)
+
+This method is called when the make inherits from an existing make. The $ctx is
+the current subsystem context, and the $i_ctx is the inherited subsystem
+context. This method allows the subsystem to make use of the inherited settings
+in the current context.
+
+=item $subsystem->config_unparse($ctx)
+
+Returns a list of L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> to
+represent the settings of the $ctx.
+
+=item $subsystem->ctx($id_of_class,$id)
+
+Returns a new context for the subsystem. The $id_of_class is the ID of the
+subsystem class. The $id is the step ID of the context.
+
+=item $subsystem->config_parse_class_prop($entry,$label)
+
+Reads a configuration $entry into the subsystem default property. The $label is
+the label of the $entry, but with the prefix (the subsystem ID plus a dot)
+removed.
+
+=item $subsystem->main($m_ctx,$ctx)
+
+Invokes the subsystem. The $m_ctx is the current context of the make (as a
+blessed reference of L<FCM::Context::Make|FCM::Context::Make>). The $ctx is the
+context of the subsystem.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build.pm	(revision 5095)
@@ -0,0 +1,1834 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd realpath};
+use FCM::Context::ConfigEntry;
+use FCM::Context::Event;
+use FCM::Context::Make::Build;
+use FCM::Context::Task;
+use FCM::System::Exception;
+use FCM::System::Make::Build::FileType::C;
+use FCM::System::Make::Build::FileType::CXX;
+use FCM::System::Make::Build::FileType::Data;
+use FCM::System::Make::Build::FileType::Fortran;
+use FCM::System::Make::Build::FileType::H;
+use FCM::System::Make::Build::FileType::NS;
+use FCM::System::Make::Build::FileType::Script;
+use FCM::System::Make::Share::Subsystem;
+use File::Basename qw{basename dirname fileparse};
+use File::Find qw{find};
+use File::Path qw{mkpath rmtree};
+use File::Spec::Functions qw{abs2rel catfile rel2abs splitdir splitpath};
+use Storable qw{dclone};
+use Text::ParseWords qw{shellwords};
+
+# Aliases
+our ($EVENT, $UTIL);
+my $E = 'FCM::System::Exception';
+my $STATE = 'FCM::System::Make::Build::State';
+
+# Classes for working with typed source files
+our @FILE_TYPE_UTILS = (
+    'FCM::System::Make::Build::FileType::C',
+    'FCM::System::Make::Build::FileType::CXX',
+    'FCM::System::Make::Build::FileType::Data',
+    'FCM::System::Make::Build::FileType::Fortran',
+    'FCM::System::Make::Build::FileType::H',
+    'FCM::System::Make::Build::FileType::NS',
+    'FCM::System::Make::Build::FileType::Script',
+);
+
+# Default target selection
+our %TARGET_SELECT_BY = (
+    'category' => {},
+    'key'      => {},
+    'task'     => {},
+);
+
+# Configuration parser label to action map
+our %CONFIG_PARSER_OF = (
+    'ns-excl' => _config_parse_ns_filter_func(sub {$_[0]->get_input_ns_excl()}),
+    'ns-incl' => _config_parse_ns_filter_func(sub {$_[0]->get_input_ns_incl()}),
+    'source'  => \&_config_parse_source,
+    'target'  => \&_config_parse_target,
+    'target-rename' => \&_config_parse_target_rename,
+);
+
+# Default properties
+our %PROP_OF = (
+    #                               [default       , ns-ok]
+    'archive-ok-target-category' => [q{include o}  , undef],
+    'checksum-method'            => [q{}           , undef],
+    'ignore-missing-dep-ns'      => [q{}           , undef],
+    'no-step-source'             => [q{}           , undef],
+    'no-inherit-source'          => [q{}           , undef],
+    'no-inherit-target-category' => [q{bin etc lib}, undef],
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   config_parser_of  => {isa => '%', default => {%CONFIG_PARSER_OF}},
+        file_type_utils   => {isa => '@', default => [@FILE_TYPE_UTILS]},
+        file_type_util_of => '%',
+        prop_of           => {isa => '%', default => {%PROP_OF}},
+        target_select_by  => {isa => '%', default => {%TARGET_SELECT_BY}},
+        util              => '&',
+    },
+    {   init => \&_init,
+        action_of => {
+            config_parse              => \&_config_parse,
+            config_parse_class_prop   => \&_config_parse_class_prop,
+            config_parse_inherit_hook => \&_config_parse_inherit_hook,
+            config_unparse            => \&_config_unparse,
+            config_unparse_class_prop => \&_config_unparse_class_prop,
+            ctx                       => \&_ctx,
+            ctx_load_hook             => \&_ctx_load_hook,
+            main                      => \&_main,
+        },
+    },
+);
+
+# Initialises the helpers of the class.
+sub _init {
+    my ($attrib_ref) = @_;
+    # Initialises file type utilities, if necessary
+    for my $class (@{$attrib_ref->{file_type_utils}}) {
+        $attrib_ref->{util}->class_load($class);
+        my $file_type_util = $class->new({util => $attrib_ref->{util}});
+        my $id = $file_type_util->id();
+        if (!defined($attrib_ref->{file_type_util_of}{$id})) {
+            $attrib_ref->{file_type_util_of}{$id} = $file_type_util;
+        }
+    }
+    # Initialises properties derived from the file type utilities
+    # TBD: warn if a property is already set and is different from previous?
+    while (
+        my ($id, $file_type_util) = each(%{$attrib_ref->{file_type_util_of}})
+    ) {
+        # File name extension, name pattern and she-bang pattern
+        for my $key (qw{ext pat she}) {
+            my $method = 'file_' . $key;
+            if ($file_type_util->can($method)) {
+                my $value = $file_type_util->$method();
+                if (defined($value)) {
+                    $attrib_ref->{prop_of}{"file-$key.$id"} = [$value];
+                }
+            }
+        }
+        # Dependency types
+        if ($file_type_util->can('source_analyse_deps')) {
+            for my $name ($file_type_util->source_analyse_deps()) {
+                $attrib_ref->{prop_of}{"dep.$name"} = [q{}, 1];
+                $attrib_ref->{prop_of}{"no-dep.$name"} = [q{}, 1];
+            }
+        }
+        # Name-space dependency types
+        if ($file_type_util->can('ns_targets_deps')) {
+            for my $name ($file_type_util->ns_targets_deps()) {
+                $attrib_ref->{prop_of}{"ns-dep.$name"} = [q{}, 1];
+            }
+        }
+        # Target extensions
+        if ($file_type_util->can('target_file_ext_of')) {
+            while (my ($key, $value)
+                = each(%{$file_type_util->target_file_ext_of()})
+            ) {
+                $attrib_ref->{prop_of}{"file-ext.$key"} = [$value, 1];
+            }
+        }
+        # Target file naming options
+        if ($file_type_util->can('target_file_name_option_of')) {
+            while (my ($key, $value)
+                = each(%{$file_type_util->target_file_name_option_of()})
+            ) {
+                $attrib_ref->{prop_of}{"file-name-option.$key"} = [$value, 1];
+            }
+        }
+        # Task properties
+        my %task_of = %{$file_type_util->task_of()};
+        while (my ($name, $task) = each(%task_of)) {
+            if ($task->can('prop_of')) {
+                my %prop_of = %{$task->prop_of()};
+                while (my ($key, $value) = each(%prop_of)) {
+                    $attrib_ref->{prop_of}{$key} = [$value, 1];
+                }
+            }
+        }
+    }
+}
+
+# A hook command for the "inherit/use" declaration.
+sub _config_parse_inherit_hook {
+    my ($attrib_ref, $ctx, $i_ctx) = @_;
+    push(@{$ctx->get_input_ns_excl()}, @{$i_ctx->get_input_ns_excl()});
+    push(@{$ctx->get_input_ns_incl()}, @{$i_ctx->get_input_ns_incl()});
+    while (my ($key, $value) = each(%{$i_ctx->get_target_key_of()})) {
+        $ctx->get_target_key_of()->{$key} = $value;
+    }
+    while (my ($key, $item_ref) = each(%{$i_ctx->get_target_select_by()})) {
+        while (my ($key2, $attr_set) = each(%{$item_ref})) {
+            if (ref($attr_set)) {
+                $ctx->get_target_select_by()->{$key}{$key2} = {%{$attr_set}};
+            }
+            else {
+                # Backward compat, $key2 is an $attr
+                $ctx->get_target_select_by()->{$key}{q{}}{$key2} = 1;
+            }
+        }
+    }
+    _config_parse_inherit_hook_prop($attrib_ref, $ctx, $i_ctx);
+}
+
+# Returns a function to parse a build/preprocess.ns-??cl declaration.
+sub _config_parse_ns_filter_func {
+    my ($getter) = @_;
+    sub {
+        my ($attrib_ref, $ctx, $entry) = @_;
+        if (@{$entry->get_ns_list()}) {
+            return $E->throw($E->CONFIG_NS, $entry);
+        }
+        @{$getter->($ctx)} = map {$_ eq q{/} ? q{} : $_} $entry->get_values();
+    };
+}
+
+# Parses a build/preprocess.source declaration.
+sub _config_parse_source {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    my ($ns) = @{$entry->get_ns_list()};
+    $ns ||= q{};
+    $ctx->get_input_source_of()->{$ns} = [$entry->get_values()];
+}
+
+# Parses a build/preprocess.target declaration.
+sub _config_parse_target {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    my %modifier_of = %{$entry->get_modifier_of()};
+    if (!(%modifier_of)) {
+        %modifier_of = (key => 1);
+    }
+    my @ns_list = map {$_ eq q{/} ? q{} : $_} @{$entry->get_ns_list()};
+    if (exists($modifier_of{'key'}) && grep {$_} @ns_list) {
+        return $E->throw($E->CONFIG_NS, $entry);
+    }
+    if (!@ns_list) {
+        @ns_list = (q{});
+    }
+    while (my $name = each(%modifier_of)) {
+        if (!grep {$_ eq $name} qw{category key task}) {
+            return $E->throw($E->CONFIG_MODIFIER, $entry);
+        }
+        my %attr_set = map {($_ => 1)} $entry->get_values();
+        for my $ns (@ns_list) {
+            $ctx->get_target_select_by()->{$name}{$ns} = \%attr_set;
+        }
+    }
+}
+
+# Parses a build/preprocess.target-rename declaration.
+sub _config_parse_target_rename {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    $ctx->set_target_key_of({
+        map {
+            my ($old, $new) = split(qr{:}msx, $_, 2);
+            if (!$old || !$new) {
+                return $E->throw($E->CONFIG_VALUE, $entry);
+            }
+            ($old => $new);
+        } ($entry->get_values()),
+    });
+}
+
+# Turns a context into a list of configuration entries.
+sub _config_unparse {
+    my ($attrib_ref, $ctx) = @_;
+    my %LABEL_OF
+        = map {($_ => $ctx->get_id() . q{.} . $_)} keys(%CONFIG_PARSER_OF);
+    (   (   @{$ctx->get_input_ns_excl()}
+            ? FCM::Context::ConfigEntry->new({
+                label => $LABEL_OF{'ns-excl'},
+                value => _config_unparse_join(
+                    map {$_ ? $_ : q{/}} @{$ctx->get_input_ns_excl()}
+                ),
+            })
+            : ()
+        ),
+        (   @{$ctx->get_input_ns_incl()}
+            ? FCM::Context::ConfigEntry->new({
+                label => $LABEL_OF{'ns-incl'},
+                value => _config_unparse_join(
+                    map {$_ ? $_ : q{/}} @{$ctx->get_input_ns_incl()}
+                ),
+            })
+            : ()
+        ),
+        (   map {
+                FCM::Context::ConfigEntry->new({
+                    label   => $LABEL_OF{source},
+                    ns_list => [$_],
+                    value   => _config_unparse_join(
+                        sort(@{$ctx->get_input_source_of()->{$_}})
+                    ),
+                })
+            }
+            sort keys(%{$ctx->get_input_source_of()})
+        ),
+        (   keys(%{$ctx->get_target_key_of()})
+            ? FCM::Context::ConfigEntry->new({
+                label => $LABEL_OF{'target-rename'},
+                value => _config_unparse_join(
+                    map {$_ . ':' . $ctx->get_target_key_of()->{$_}}
+                    sort keys(%{$ctx->get_target_key_of()})
+                ),
+            })
+            : ()
+        ),
+        (   map {
+                my $modifier = $_;
+                map {
+                    my $ns = $_;
+                    my @values = sort(keys(
+                        %{$ctx->get_target_select_by()->{$modifier}{$ns}}
+                    ));
+                    FCM::Context::ConfigEntry->new({
+                        label       => $LABEL_OF{'target'},
+                        modifier_of => {$modifier => 1},
+                        ns_list     => [$ns],
+                        value       => _config_unparse_join(@values),
+                    });
+                }
+                sort keys(%{$ctx->get_target_select_by()->{$modifier}});
+            }
+            sort keys(%{$ctx->get_target_select_by()})
+        ),
+        _config_unparse_prop($attrib_ref, $ctx),
+    );
+}
+
+# Returns a new context.
+sub _ctx {
+    my ($attrib_ref, $id_of_class, $id) = @_;
+    FCM::Context::Make::Build->new({
+        id               => $id,
+        id_of_class      => $id_of_class,
+        target_select_by => dclone($attrib_ref->{target_select_by}),
+    });
+}
+
+# Hook when loading a previous ctx.
+sub _ctx_load_hook {
+    my ($attrib_ref, $old_m_ctx, $old_ctx, $old_m_dest, $old_dest) = @_;
+    my $path_mod_func = sub {
+        my ($get_func, $set_func) = @_;
+        my $path = $get_func->();
+        if (!defined($path)) {
+            return;
+        }
+        my $rel_path = abs2rel($path, $old_m_dest);
+        if (index($rel_path, '..') != 0) {
+            $set_func->(catfile($old_m_ctx->get_dest(), $rel_path));
+        }
+    };
+    if (@{$old_ctx->get_dests()}) {
+        $old_ctx->get_dests()->[0] = $old_ctx->get_dest();
+    }
+    while (my ($ns, $source) = each(%{$old_ctx->get_source_of()})) {
+        $path_mod_func->(
+            sub {$source->get_path()},
+            sub {$source->set_path(@_)},
+        );
+    }
+    while (my ($key, $target) = each(%{$old_ctx->get_target_of()})) {
+        $path_mod_func->(
+            sub {$target->get_path()},
+            sub {$target->set_path(@_)},
+        );
+        $path_mod_func->(
+            sub {$target->get_path_of_prev()},
+            sub {$target->set_path_of_prev(@_)},
+        );
+        $path_mod_func->(
+            sub {$target->get_path_of_source()},
+            sub {$target->set_path_of_source(@_)},
+        );
+    }
+}
+
+# The main function of the class.
+sub _main {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    local($UTIL) = $attrib_ref->{util};
+    local($EVENT) = sub {$UTIL->event(@_)};
+    for my $function (
+        \&_sources_locate,
+        \&_sources_type,
+        \&_sources_analyse,
+        \&_targets_update,
+    ) {
+        $function->($attrib_ref, $m_ctx, $ctx);
+    }
+}
+
+# Locates the actual source files, and determines their types.
+sub _sources_locate {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    # From inherited
+    my %NO_INHERIT_FROM
+        = map {($_ => 1)} _props($attrib_ref, 'no-inherit-source', $ctx);
+    if (!$NO_INHERIT_FROM{'*'}) {
+        for my $i_ctx (_i_ctx_list($m_ctx, $ctx)) {
+            while (my ($ns, $source) = each(%{$i_ctx->get_source_of()})) {
+                if (!exists($NO_INHERIT_FROM{$ns})) { # exact name-spaces only
+                    $ctx->get_source_of()->{$ns} = dclone($source);
+                }
+            }
+        }
+    }
+    # From specified input
+    while (my ($ns, $input_sources_ref) = each(%{$ctx->get_input_source_of()})) {
+        for my $input_source (@{$input_sources_ref}) {
+            my $path = realpath(rel2abs($input_source, $m_ctx->get_dest()));
+            _sources_locate_by_find($attrib_ref, $m_ctx, $ctx, $ns, $path);
+        }
+    }
+    # From completed make destinations
+    my %NO_SOURCE_FROM
+        = map {($_, 1)} _props($attrib_ref, 'no-step-source', $ctx);
+    for my $step (@{$m_ctx->get_steps()}) {
+        my $a_ctx = $m_ctx->get_ctx_of($step);
+        if (    !exists($NO_SOURCE_FROM{$step})
+            &&  defined($a_ctx)
+            &&  $a_ctx->get_status() eq $m_ctx->ST_OK
+            &&  $a_ctx->can('get_target_of')
+        ) {
+            my @target_list
+                = grep {$_->can_be_source()} values(%{$a_ctx->get_target_of()});
+            for my $target (@target_list) {
+                if ($target->is_ok() && -e $target->get_path()) {
+                    my $checksum;
+                    if ($target->can('get_checksum')) {
+                        $checksum = $target->get_checksum();
+                    }
+                    my $source = $ctx->CTX_SOURCE->new({
+                        checksum => $checksum,
+                        ns       => $target->get_ns(),
+                        path     => $target->get_path(),
+                    });
+                    $ctx->get_source_of()->{$target->get_ns()} = $source;
+                }
+                elsif (exists($ctx->get_source_of()->{$target->get_ns()})) {
+                    delete($ctx->get_source_of()->{$target->get_ns()});
+                }
+            }
+        }
+    }
+    # Applies filter
+    my %INPUT_NS_EXCL = map {($_, 1)} @{$ctx->get_input_ns_excl()};
+    my %INPUT_NS_INCL = map {($_, 1)} @{$ctx->get_input_ns_incl()};
+    if (keys(%INPUT_NS_EXCL) || keys(%INPUT_NS_INCL)) {
+        while (my ($ns, $source) = each(%{$ctx->get_source_of()})) {
+            my $ns_iter_ref = $UTIL->ns_iter($ns, $UTIL->NS_ITER_UP);
+            NS:
+            while (defined(my $head = $ns_iter_ref->())) {
+                if (exists($INPUT_NS_INCL{$head})) {
+                    last NS;
+                }
+                if (exists($INPUT_NS_EXCL{$head})) {
+                    delete($ctx->get_source_of()->{$ns});
+                    last NS;
+                }
+            }
+        }
+    }
+}
+
+# Locates the actual source files in $path.
+sub _sources_locate_by_find {
+    my ($attrib_ref, $m_ctx, $ctx, $key, $path) = @_;
+    if (!-e $path) {
+        return $E->throw($E->BUILD_SOURCE, $path, $!);
+    }
+    find(
+        sub {
+            my $path_found = $File::Find::name;
+            if (-d $path_found) {
+                return;
+            }
+            my $ns = abs2rel($path_found, $path);
+            if ($ns ne q{.}) {
+                for my $name (split(q{/}, $ns)) {
+                    if (index($name, q{.}) == 0) {
+                        return; # ignore Unix hidden/system files
+                    }
+                }
+            }
+            if ($key) {
+                $ns = $UTIL->ns_cat($key, $ns);
+            }
+            $ctx->get_source_of()->{$ns}
+                = $ctx->CTX_SOURCE->new({ns => $ns, path => $path_found});
+        },
+        $path,
+    );
+}
+
+# Determines source types.
+sub _sources_type {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my %INPUT_FILE_EXT_TO_TYPE_MAP;
+    my %INPUT_FILE_PAT_TO_TYPE_MAP;
+    my %INPUT_FILE_SHE_TO_TYPE_MAP;
+    for (
+        ['file-ext.', \%INPUT_FILE_EXT_TO_TYPE_MAP, 1],
+        ['file-pat.', \%INPUT_FILE_PAT_TO_TYPE_MAP, 0],
+        ['file-she.', \%INPUT_FILE_SHE_TO_TYPE_MAP, 0],
+    ) {
+        my ($prefix, $map_ref, $value_is_words) = @{$_};
+        for my $id (keys(%{$attrib_ref->{file_type_util_of}})) {
+            my $name = $prefix . $id;
+            my $value = _prop($attrib_ref, $name, $ctx);
+            if (defined($value)) {
+                for my $key (($value_is_words ? shellwords($value) : ($value))) {
+                    $map_ref->{$key} = $id;
+                }
+            }
+        }
+    }
+    my $type_func = sub {
+        my ($path) = @_;
+        # Try file name extension
+        my $extension = $UTIL->file_ext($path);
+        $extension = $extension ? q{.} . $extension : undef;
+        if ($extension && exists($INPUT_FILE_EXT_TO_TYPE_MAP{$extension})) {
+            return $INPUT_FILE_EXT_TO_TYPE_MAP{$extension};
+        }
+        # Try she-bang line
+        if (-T $path) {
+            my $line = $UTIL->file_head($path);
+            if ($line) {
+                while (my ($pattern, $type) = each(%INPUT_FILE_SHE_TO_TYPE_MAP)) {
+                    if (index($line, '#!') == 0) { # OK to hard code this
+                        keys(%INPUT_FILE_SHE_TO_TYPE_MAP); # reset iterator
+                        return $type;
+                    }
+                }
+            }
+        }
+        # Try file name pattern
+        my $base_name = basename($path);
+        while (my ($pattern, $type) = each(%INPUT_FILE_PAT_TO_TYPE_MAP)) {
+            if ($base_name =~ $pattern) {
+                keys(%INPUT_FILE_PAT_TO_TYPE_MAP); # reset iterator
+                return $type;
+            }
+        }
+        return q{};
+    };
+    while (my ($ns, $source) = each(%{$ctx->get_source_of()})) {
+        if (!defined($source->get_type())) {
+            $source->set_type($type_func->($source->get_path()));
+        }
+    }
+}
+
+# Reads source files to gather dependency and other information.
+sub _sources_analyse {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my $timer = $UTIL->timer();
+    my $checksum_method = _prop($attrib_ref, 'checksum-method', $ctx);
+    my %FILE_TYPE_UTIL_OF = %{$attrib_ref->{file_type_util_of}};
+    # Checksum
+    while (my ($ns, $source) = each(%{$ctx->get_source_of()})) {
+        if (    exists($FILE_TYPE_UTIL_OF{$source->get_type()})
+            &&  !defined($source->get_checksum())
+        ) {
+            $source->set_checksum(
+                $UTIL->file_checksum($source->get_path(), $checksum_method),
+            );
+        }
+    }
+    # Source information
+    my $n_jobs = $m_ctx->get_option_of('jobs');
+    my $runner = $UTIL->task_runner(
+        sub {_source_analyse($attrib_ref, @_)},
+        $n_jobs,
+    );
+    my $elapse_tasks = 0;
+    my $n = eval {
+        $runner->main(
+            _source_analyse_get_func($attrib_ref, $m_ctx, $ctx),
+            _source_analyse_put_func($attrib_ref, $m_ctx, $ctx, \$elapse_tasks),
+        );
+    };
+    my $e = $@;
+    $runner->destroy();
+    if ($e) {
+        die($e);
+    }
+    my $n_total = scalar(keys(%{$ctx->get_source_of()}));
+    $EVENT->(
+        FCM::Context::Event->MAKE_BUILD_SOURCE_SUMMARY,
+        $n_total, $n, $timer->(), $elapse_tasks,
+    );
+}
+
+# Reads a source to gather information.
+sub _source_analyse {
+    my ($attrib_ref, $source) = @_;
+    my $FILE_TYPE_UTIL
+        = $attrib_ref->{file_type_util_of}->{$source->get_type()};
+    if (!$FILE_TYPE_UTIL->can('source_analyse')) {
+        return;
+    }
+    $FILE_TYPE_UTIL->source_analyse($source);
+}
+
+# Generates an iterator for each source file requiring information gathering.
+sub _source_analyse_get_func {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my $P_SOURCE_GETTER
+        = _prev_hash_item_getter($m_ctx, $ctx, sub {$_[0]->get_source_of()});
+    my %FILE_TYPE_UTIL_OF = %{$attrib_ref->{file_type_util_of}};
+    my $exhausted;
+    sub {
+        if ($exhausted) {
+            return;
+        }
+        SOURCE:
+        while (my ($ns, $source) = each(%{$ctx->get_source_of()})) {
+            my $type = $source->get_type();
+            if (!exists($FILE_TYPE_UTIL_OF{$type})) {
+                next SOURCE;
+            }
+            # Stores the current properties relevant to the source
+            for my $dep_type ($FILE_TYPE_UTIL_OF{$type}->source_analyse_deps()) {
+                for my $n (map {$_ . q{.} . $dep_type} qw{dep no-dep}) {
+                    $source->get_prop_of()->{$n}
+                        = _prop($attrib_ref, $n, $ctx, $ns);
+                }
+            }
+            # Compare with previous source, if possible
+            my $p_source = $P_SOURCE_GETTER->($ns);
+            if (defined($p_source)) {
+                $source->set_up_to_date(
+                    $p_source->get_checksum() eq $source->get_checksum());
+                if (    $source->get_up_to_date()
+                    &&  !$UTIL->hash_cmp(
+                            map {$_->get_prop_of()} ($source, $p_source)
+                        )
+                ) {
+                    $source->set_info_of(dclone($p_source->get_info_of()));
+                    $source->set_deps(   dclone($p_source->get_deps()   ));
+                    next SOURCE;
+                }
+            }
+            return FCM::Context::Task->new({ctx => $source, id  => $ns});
+        }
+        $exhausted = 1;
+        return;
+    };
+}
+
+# Generates a callback when a source read completes.
+sub _source_analyse_put_func {
+    my ($attrib_ref, $m_ctx, $ctx, $elapse_tasks_ref) = @_;
+    my %FILE_TYPE_UTIL_OF = %{$attrib_ref->{file_type_util_of}};
+    sub {
+        my ($task) = @_;
+        if ($task->get_state() eq $task->ST_FAILED) {
+            die($task->get_error());
+        }
+        my $ns = $task->get_id();
+        my $source = $ctx->get_source_of()->{$ns} = $task->get_ctx();
+        for my $type (
+            $FILE_TYPE_UTIL_OF{$source->get_type()}->source_analyse_deps()
+        ) {
+            # Note: "dep" property: use name-space value only
+            my $key = 'dep.' . $type;
+            push(
+                @{$source->get_deps()},
+                (map {[$_, $type]} _props($attrib_ref, $key, $ctx, $ns)),
+            );
+        }
+        ${$elapse_tasks_ref} += $task->get_elapse();
+        $EVENT->(
+            FCM::Context::Event->MAKE_BUILD_SOURCE_ANALYSE,
+            $source, $task->get_elapse(),
+        );
+    }
+}
+
+# Updates the targets.
+sub _targets_update {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my $timer = $UTIL->timer();
+    # Creates and changes directory to the destination
+    eval {mkpath($ctx->get_dest())};
+    if ($@) {
+        return $E->throw($E->DEST_CREATE, $ctx->get_dest());
+    }
+    my $old_cwd = cwd();
+    chdir($ctx->get_dest()) || die(sprintf("%s: %s\n", $ctx->get_dest(), $!));
+    # Extract any target category directories that are in .tar.gz
+    opendir(my $handle, '.');
+    while (my $name = readdir($handle)) {
+        if ((fileparse($name, '.tar.gz'))[2] eq '.tar.gz') {
+            my %value_of = %{$UTIL->shell_simple([qw{tar -x -z}, '-f', $name])};
+            if ($value_of{'rc'} == 0) {
+                unlink($name);
+            }
+        }
+    }
+    closedir($handle);
+    # Determines the destination search path
+    my $id = $ctx->get_id();
+    @{$ctx->get_dests()} = (
+        $ctx->get_dest(),
+        map {$_->get_ctx_of($id) ? @{$_->get_ctx_of($id)->get_dests()} : ()}
+            @{$m_ctx->get_inherit_ctx_list()}
+        ,
+    );
+    # Performs targets update
+    my $checksum_method = _prop($attrib_ref, 'checksum-method', $ctx);
+    my %stat_of = ();
+    eval {
+        my $n_jobs = $m_ctx->get_option_of('jobs');
+        my $runner = $UTIL->task_runner(
+            sub {_target_update($attrib_ref, $checksum_method, @_)},
+            $n_jobs,
+        );
+        eval {
+            my ($get_ref, $put_ref) = _targets_manager_funcs(
+                 $attrib_ref, $m_ctx, $ctx, \%stat_of,
+            );
+            $runner->main($get_ref, $put_ref);
+        };
+        my $e = $@;
+        $runner->destroy();
+        if ($e) {
+            die($e);
+        }
+    };
+    my $e = $@;
+    # Back to original working directory
+    chdir($old_cwd) || die(sprintf("%s: %s\n", $old_cwd, $!));
+    if ($e) {
+        _finally($attrib_ref, $m_ctx, $ctx);
+        die($e);
+    }
+    # Finally
+    my @targets = values(%{$ctx->get_target_of()});
+    for my $key (sort(keys(%stat_of))) {
+        $stat_of{$key}{n}{$ctx->CTX_TARGET->ST_MODIFIED} ||= 0;
+        $stat_of{$key}{n}{$ctx->CTX_TARGET->ST_UNCHANGED} ||= 0;
+        $stat_of{$key}{n}{$ctx->CTX_TARGET->ST_FAILED} ||= 0;
+        $stat_of{$key}{t} ||= 0.0;
+        $EVENT->(
+            FCM::Context::Event->MAKE_BUILD_TARGET_TASK_SUMMARY,
+            $key,
+            $stat_of{$key}{n}{$ctx->CTX_TARGET->ST_MODIFIED},
+            $stat_of{$key}{n}{$ctx->CTX_TARGET->ST_UNCHANGED},
+            $stat_of{$key}{n}{$ctx->CTX_TARGET->ST_FAILED},
+            $stat_of{$key}{t},
+        );
+    }
+    $EVENT->(
+        FCM::Context::Event->MAKE_BUILD_TARGET_SUMMARY,
+        scalar(grep {$_->is_modified() } @targets),
+        scalar(grep {$_->is_unchanged()} @targets),
+        scalar(grep {$_->is_failed()   } @targets),
+        $timer->(),
+    );
+    my @failed_targets = grep {$_->is_failed()} @targets;
+    if (@failed_targets) {
+        $EVENT->(
+            FCM::Context::Event->MAKE_BUILD_TARGETS_FAIL,
+            \@failed_targets
+        );
+        _finally($attrib_ref, $m_ctx, $ctx);
+        die("\n");
+    }
+    _finally($attrib_ref, $m_ctx, $ctx);
+}
+
+# Updates a target.
+sub _target_update {
+    my ($attrib_ref, $checksum_method, $target) = @_;
+    my $file_type_util = $attrib_ref->{file_type_util_of}{$target->get_type()};
+    eval {$file_type_util->task_of()->{$target->get_task()}->main($target)};
+    if ($@) {
+        if ($target->get_path() && -e $target->get_path()) {
+            unlink($target->get_path());
+        }
+        die($@);
+    }
+    if (! -e $target->get_path()) {
+        return $E->throw($E->BUILD_TARGET, $target);
+    }
+    $target->set_status($target->ST_MODIFIED);
+    my $checksum = $UTIL->file_checksum($target->get_path(), $checksum_method);
+    if ($target->get_checksum() && $checksum eq $target->get_checksum()) {
+        $target->set_status($target->ST_UNCHANGED);
+        if ($target->get_path_of_prev()) {
+            $target->set_path($target->get_path_of_prev());
+        }
+    }
+    $target->set_checksum($checksum);
+    $target->set_prop_of_prev_of({}); # unset
+    $target->set_path_of_prev(undef); # unset
+}
+
+# Returns the get/put functions to send/receive targets to update.
+sub _targets_manager_funcs {
+    my ($attrib_ref, $m_ctx, $ctx, $stat_hash_ref) = @_;
+
+    my @targets;
+    _targets_from_sources($attrib_ref, $m_ctx, $ctx, \@targets);
+    _targets_props_assign($attrib_ref, $m_ctx, $ctx, \@targets);
+
+    my ($stack_ref, $state_hash_ref)
+        = _targets_select($attrib_ref, $m_ctx, $ctx, \@targets);
+
+    my $checksum_method = _prop($attrib_ref, 'checksum-method', $ctx);
+    my $get_action_ref = sub {
+        STATE:
+        while (my $state = pop(@{$stack_ref})) {
+            if (    !$state->is_ready()
+                ||  !_target_deps_are_done($state, $state_hash_ref, $stack_ref)
+            ) {
+                next STATE;
+            }
+            my $target = $state->get_target();
+            if (_target_check_failed_dep($state, $state_hash_ref)) {
+                _target_update_failed(
+                    $stat_hash_ref, $ctx, $target, $state_hash_ref, $stack_ref,
+                );
+            }
+            elsif (_target_check_ood($state, $state_hash_ref, $checksum_method)) {
+                _target_prep($state, $ctx);
+                $state->set_value($STATE->PENDING);
+                # Adds tasks that can be triggered by this task
+                for my $key (sort @{$target->get_triggers()}) {
+                    if (    exists($state_hash_ref->{$key})
+                        &&  !$state_hash_ref->{$key}->is_done()
+                        &&  !grep {$_->get_id() eq $key} @{$stack_ref}
+                    ) {
+                        my $trigger_target
+                            = $state_hash_ref->{$key}->get_target();
+                        $trigger_target->set_status($trigger_target->ST_OOD);
+                        push(@{$stack_ref}, $state_hash_ref->{$key});
+                    }
+                }
+                return FCM::Context::Task->new(
+                    {ctx => $target, id => $state->get_id()},
+                );
+            }
+            else {
+                _target_update_ok(
+                    $stat_hash_ref, $ctx, $target, $state_hash_ref, $stack_ref,
+                );
+            }
+        }
+        return;
+    };
+    my $put_action_ref = sub {
+        my $task = shift();
+        my $target = $task->get_ctx();
+        if ($task->get_state() eq $task->ST_FAILED) {
+            $EVENT->(FCM::Context::Event->E, $task->get_error());
+            _target_update_failed(
+                $stat_hash_ref, $ctx, $target, $state_hash_ref, $stack_ref,
+                $task->get_elapse(),
+            );
+        }
+        else {
+            my $target = $task->get_ctx();
+            _target_update_ok(
+                $stat_hash_ref, $ctx, $target, $state_hash_ref, $stack_ref,
+                $task->get_elapse(),
+            );
+        }
+    };
+    ($get_action_ref, $put_action_ref);
+}
+
+# Determines and returns the targets from the sources.
+sub _targets_from_sources {
+    my ($attrib_ref, $m_ctx, $ctx, $targets_ref) = @_;
+    my %FILE_TYPE_UTIL_OF = %{$attrib_ref->{file_type_util_of}};
+    my %FILE_EXT_OF;
+    my %FILE_NAME_OPTION_OF;
+    for my $FILE_TYPE_UTIL (values(%FILE_TYPE_UTIL_OF)) {
+        while (my $key = each(%{$FILE_TYPE_UTIL->target_file_ext_of()})) {
+            $FILE_EXT_OF{$key} ||= _prop($attrib_ref, 'file-ext.' . $key, $ctx);
+        }
+        while (my $key = each(%{$FILE_TYPE_UTIL->target_file_name_option_of()})) {
+            $FILE_NAME_OPTION_OF{$key}
+                ||= _prop($attrib_ref, 'file-name-option.' . $key, $ctx);
+        }
+    }
+    # Determine the targets for each source
+    my %reverse_deps_of = ();
+    SOURCE:
+    while (my ($ns, $source) = each(%{$ctx->get_source_of()})) {
+        my $type = $source->get_type();
+        $type ||= q{};
+        if (!exists($FILE_TYPE_UTIL_OF{$type})) {
+            next SOURCE;
+        }
+        my $FILE_TYPE_UTIL = $FILE_TYPE_UTIL_OF{$type};
+        if (!$FILE_TYPE_UTIL->can('source_to_targets')) {
+            next SOURCE;
+        }
+        for my $target (
+            $FILE_TYPE_UTIL->source_to_targets(
+                $source, \%FILE_EXT_OF, \%FILE_NAME_OPTION_OF)
+        ) {
+            my $key = $target->get_key();
+            if (exists($ctx->get_target_key_of()->{$key})) {
+                $key = $ctx->get_target_key_of()->{$key};
+                $target->set_key($key);
+            }
+            push(@{$targets_ref}, $target);
+            $target->set_ns($ns);
+            $target->set_path(
+                catfile($ctx->get_dest(), $target->get_category(), $key),
+            );
+            $target->set_path_of_source($source->get_path());
+            $target->set_type($type);
+            if (!$source->get_up_to_date()) {
+                $target->set_status($target->ST_OOD);
+            }
+            if (exists($target->get_info_of()->{'parents'})) {
+                for my $parent (@{$target->get_info_of()->{'parents'}}) {
+                    if (!exists($reverse_deps_of{$parent})) {
+                        $reverse_deps_of{$parent} = [];
+                    }
+                    push(
+                        @{$reverse_deps_of{$parent}},
+                        [$key, $target->get_category()],
+                    );
+                }
+            }
+        }
+    }
+    # Add reverse dependencies
+    # E.g. Fortran module has link time dependency on its submodules
+    for my $target (@{$targets_ref}) {
+        if (exists($reverse_deps_of{$target->get_key()})) {
+            push(@{$target->get_deps()}, @{$reverse_deps_of{$target->get_key()}});
+        }
+    }
+    # Determines name-space dependencies
+    my %deps_in_ns_in_cat_of; # $cat => {$ns => [$targets ...]}
+    FILE_TYPE_UTIL:
+    while (my ($type, $FILE_TYPE_UTIL) = each(%FILE_TYPE_UTIL_OF)) {
+        if (!$FILE_TYPE_UTIL->can('ns_targets')) {
+            next FILE_TYPE_UTIL;
+        }
+        for my $cat ($FILE_TYPE_UTIL->ns_targets_deps()) {
+            $deps_in_ns_in_cat_of{$cat} = {};
+        }
+        for my $target (
+            $FILE_TYPE_UTIL->ns_targets(
+                $targets_ref, \%FILE_EXT_OF, \%FILE_NAME_OPTION_OF)
+        ) {
+            my $key = $target->get_key();
+            if (exists($ctx->get_target_key_of()->{$key})) {
+                $key = $ctx->get_target_key_of()->{$key};
+                $target->set_key($key);
+            }
+            push(@{$targets_ref}, $target);
+            $target->set_type($type);
+            $target->set_path(
+                catfile($ctx->get_dest(), $target->get_category(), $key),
+            );
+        }
+    }
+    for my $target (
+        sort {
+            $a->get_ns() cmp $b->get_ns() || $a->get_key() cmp $b->get_key();
+        } @{$targets_ref}
+    ) {
+        $EVENT->(
+            FCM::Context::Event->MAKE_BUILD_TARGET_FROM_NS,
+            ($target->get_ns() ? $target->get_ns() : '/'),
+            $target->get_task(),
+            $target->get_category(),
+            $target->get_key(),
+        );
+    }
+    # Target categories and name-spaces.
+    for my $target (@{$targets_ref}) {
+        my $cat = $target->get_category();
+        if ($cat && exists($deps_in_ns_in_cat_of{$cat})) {
+            my $ns_iter = $UTIL->ns_iter($target->get_ns(), $UTIL->NS_ITER_UP);
+            # $ns_iter->(); # discard
+            while (defined(my $ns = $ns_iter->())) {
+                $deps_in_ns_in_cat_of{$cat}{$ns} ||= [];
+                push(@{$deps_in_ns_in_cat_of{$cat}{$ns}}, $target->get_key());
+            }
+        }
+    }
+
+    my %CTX_PROP_OF = %{$ctx->get_prop_of()};
+    for my $target (@{$targets_ref}) {
+        my $key = $target->get_key();
+        # Adds categorised name-space dependencies.
+        if (exists($target->get_info_of()->{'deps'})) {
+            CATEGORY:
+            while (my ($cat, $deps_in_ns_ref) = each(%deps_in_ns_in_cat_of)) {
+                if (!exists($target->get_info_of()->{'deps'}{$cat})) {
+                    next CATEGORY;
+                }
+                my $cfg_key = 'ns-dep.' . $cat;
+                my @ns_list = map {$_ eq q{/} ? q{} : $_}
+                    _props($attrib_ref, $cfg_key, $ctx, $target->get_ns());
+                for my $ns (@ns_list) {
+                    if (exists($deps_in_ns_ref->{$ns})) {
+                        push(
+                            @{$target->get_deps()},
+                            (   map  {[$_, $cat]}
+                                grep {$_ ne $key}
+                                @{$deps_in_ns_ref->{$ns}}
+                            ),
+                        );
+                    }
+                    else {
+                        # This will be reported later as missing dependency
+                        push(@{$target->get_deps()}, [$ns, $cat, 'ns-dep']);
+                    }
+                }
+            }
+        }
+        # Remove target dependencies, if necessary
+        my @deps;
+        DEP:
+        for my $dep (@{$target->get_deps()}) {
+            my ($dep_key, $dep_type) = @{$dep};
+            my $cfg_key = 'no-dep.' . $dep_type;
+            if (    !exists($CTX_PROP_OF{$cfg_key})
+                ||  !exists($CTX_PROP_OF{$cfg_key}->get_ctx_of()->{$key})
+            ) {
+                push(@deps, $dep);
+                next DEP;
+            }
+            my @no_dep_keys = shellwords(
+                $CTX_PROP_OF{$cfg_key}->get_ctx_of()->{$key}->get_value());
+            if (!grep {$_ eq $dep_key} @no_dep_keys) {
+                push(@deps, $dep);
+                next DEP;
+            }
+        }
+        $target->set_deps(\@deps);
+        # Add target dependencies, if necessary
+        for my $dep_type (keys(%{$target->get_dep_policy_of()})) {
+            my $cfg_key = 'dep.' . $dep_type;
+            if (    exists($CTX_PROP_OF{$cfg_key})
+                &&  exists($CTX_PROP_OF{$cfg_key}->get_ctx_of()->{$key})
+            ) {
+                my @dep_keys = shellwords(
+                    $CTX_PROP_OF{$cfg_key}->get_ctx_of()->{$key}->get_value());
+                for my $dep_key (@dep_keys) {
+                    push(@{$target->get_deps()}, [$dep_key, $dep_type]);
+                }
+            }
+        }
+    }
+}
+
+# Stores the properties relevant to the target.
+# Assigns previous checksum and properties, where appropriate.
+sub _targets_props_assign {
+    my ($attrib_ref, $m_ctx, $ctx, $targets_ref) = @_;
+    my $P_TARGET_GETTER
+        = _prev_hash_item_getter($m_ctx, $ctx, sub {$_[0]->get_target_of()});
+    my %NO_INHERIT_CATEGORY_IN
+        = map {$_ => 1} _props($attrib_ref, 'no-inherit-target-category', $ctx);
+    my %CTX_PROP_OF = %{$ctx->get_prop_of()};
+    for my $target (@{$targets_ref}) {
+        my $key = $target->get_key();
+        # Properties
+        my $FILE_TYPE_UTIL
+            = $attrib_ref->{file_type_util_of}->{$target->get_type()};
+        my $task = $FILE_TYPE_UTIL->task_of()->{$target->get_task()};
+        if ($task->can('prop_of')) {
+            my %prop_of = %{$task->prop_of($target)};
+            while (my $name = each(%prop_of)) {
+                if (    exists($CTX_PROP_OF{$name})
+                    &&  exists($CTX_PROP_OF{$name}->get_ctx_of()->{$key})
+                ) {
+                    $target->get_prop_of()->{$name}
+                        = $CTX_PROP_OF{$name}->get_ctx_of()->{$key}->get_value();
+                }
+                else {
+                    $target->get_prop_of()->{$name}
+                        = _prop($attrib_ref, $name, $ctx, $target->get_ns());
+                }
+            }
+        }
+        if ($FILE_TYPE_UTIL->can('target_deps_filter')) {
+            $FILE_TYPE_UTIL->target_deps_filter($target);
+        }
+        # Path, checksum and previous properties
+        my $p_target = $P_TARGET_GETTER->($key);
+        if (defined($p_target)) {
+            $target->set_checksum($p_target->get_checksum());
+            if ($p_target->is_ok()) {
+                $target->set_path_of_prev($p_target->get_path());
+                $target->set_prop_of_prev_of($p_target->get_prop_of());
+            }
+            else {
+                $target->set_path_of_prev($p_target->get_path_of_prev());
+                $target->set_prop_of_prev_of($p_target->get_prop_of_prev_of());
+                $target->set_status($target->ST_OOD);
+            }
+            if (exists($NO_INHERIT_CATEGORY_IN{$target->get_category()})) {
+                $target->set_path_of_prev($target->get_path());
+            }
+        }
+    }
+}
+
+# Selects targets to update.
+sub _targets_select {
+    my ($attrib_ref, $m_ctx, $ctx, $targets_ref) = @_;
+    my $time = time();
+    my $timer = $UTIL->timer();
+    my %select_by = %{$ctx->get_target_select_by()};
+    my %target_of;
+    my %targets_of;
+    my %target_set;
+    my %has_ns_in; # available sets of name-spaces
+    for my $target (@{$targets_ref}) {
+        ATTR_NAME:
+        for (
+            #$attr_name, $attr_func
+            ['key'     , sub {$_[0]->get_key()}],
+            ['category', sub {$_[0]->get_category()}],
+            ['task'    , sub {$_[0]->get_task()}],
+        ) {
+            my ($attr_name, $attr_func) = @{$_};
+            for my $ns (sort keys(%{$select_by{$attr_name}})) {
+                my %attr_set = %{$select_by{$attr_name}->{$ns}};
+                if (    exists($attr_set{$attr_func->($target)})
+                    &&  (!$ns || $UTIL->ns_in_set($target->get_ns(), {$ns => 1}))
+                ) {
+                    $target_set{$target->get_key()} = 1;
+                    last ATTR_NAME;
+                }
+            }
+        }
+        if (exists($target_of{$target->get_key()})) {
+            if (!exists($targets_of{$target->get_key()})) {
+                $targets_of{$target->get_key()}
+                    = [delete($target_of{$target->get_key()})];
+            }
+            push(@{$targets_of{$target->get_key()}}, $target);
+        }
+        else {
+            $target_of{$target->get_key()} = $target;
+        }
+        # Name-spaces
+        my $ns_iter = $UTIL->ns_iter($target->get_ns(), $UTIL->NS_ITER_UP);
+        NS:
+        while (defined(my $ns = $ns_iter->())) {
+            if (exists($has_ns_in{$ns})) {
+                last NS;
+            }
+            $has_ns_in{$ns} = 1;
+        }
+    }
+    my @target_keys = sort keys(%target_set);
+
+    # Wraps each relevant target with a state object.
+    # Walks the target dependency tree to build a state dependency tree.
+    # Checks for missing dependencies.
+    # Checks for duplicated target.
+    my @items = map {[[$_, undef]]} @target_keys;
+    my %state_of;
+    my %dup_in;
+    my %cyc_in;
+    my %missing_deps_in;
+    ITEM:
+    while (my $item = pop(@items)) {
+        my ($unit, @up_units) = @{$item};
+        my ($key, $type) = @{$unit};
+        my @up_keys = map {$_->[0]} @up_units;
+        if (   exists($cyc_in{$key})
+            || exists($dup_in{$key})
+            || exists($missing_deps_in{$key})
+        ) {
+            next ITEM;
+        }
+        if (exists($state_of{$key})) {
+            # Already visited this ITEM
+            # Detect cyclic dependency
+            if (    !$state_of{$key}->get_cyclic_ok()
+                &&  grep {$_->[0] eq $key} @up_units
+            ) {
+                my @_up_units = (@up_units, $unit);
+                my $_up_unit_last = pop(@_up_units);
+                DEP_UP_KEY:
+                while (my $_up_unit = pop(@_up_units)) {
+                    my ($_up_key, $_up_type) = @{$_up_unit};
+                    my @dep_up_deps = @{$state_of{$_up_key}->get_deps()};
+                    # If parent of $_up_unit_last does not depend on
+                    # $_up_unit_last, chain is broken, and we are OK.
+                    my ($_up_key_last, $_up_type_last) = @{$_up_unit_last};
+                    if (!grep {     $_->[0]->get_key() eq $_up_key_last
+                                ||  $_->[1] eq $_up_type_last
+                        } @dep_up_deps
+                    ) {
+                        last DEP_UP_KEY;
+                    }
+                    if ($type && $key eq $_up_key && $type eq $_up_type) {
+                        $cyc_in{$key} = {'keys' => [@up_keys, $key]};
+                        next ITEM;
+                    }
+                    $_up_unit_last = $_up_unit;
+                }
+            }
+            $state_of{$key}->set_cyclic_ok(1);
+            # Float current target up dependency chain
+            my $is_directly_related = 1;
+            UP_KEY:
+            for my $up_key (reverse(@up_keys)) {
+                if ($state_of{$up_key}->add_visitor(
+                        $state_of{$key}->get_target(),
+                        $type,
+                        $is_directly_related,
+                )) {
+                    last UP_KEY;
+                }
+                $is_directly_related = 0;
+            }
+            # Add floatable dependencies up the dependency chain
+            for my $visitor (values(%{$state_of{$key}->get_floatables()})) {
+                UP_KEY:
+                for my $up_key (reverse(@up_keys)) {
+                    if ($state_of{$up_key}->add_visitor(@{$visitor})) {
+                        last UP_KEY;
+                    }
+                }
+            }
+            next ITEM;
+        }
+
+        # First visit to this ITEM
+        # Checks for duplicated target
+        if (exists($targets_of{$key})) {
+            $dup_in{$key} = {
+                'keys' => [@up_keys, $key],
+                'values' => [map {$_->get_ns()} @{$targets_of{$key}}],
+            };
+            next ITEM;
+        }
+        # Wraps all required targets with a STATE object
+        $state_of{$key} = $STATE->new(
+            {id => $key, target => $target_of{$key}},
+        );
+        my $target = $target_of{$key};
+        DEP:
+        for (
+            grep {$_->[0] ne $key}
+            sort {$a->[0] cmp $b->[0]}
+            @{$target->get_deps()}
+        ) {
+            my ($dep_key, $dep_type, $dep_remark) = @{$_};
+            # Duplicated targets
+            if (exists($targets_of{$dep_key})) {
+                $dup_in{$dep_key} = {
+                    'keys' => [@up_keys, $key, $dep_key],
+                    'values' => [map {$_->get_ns()} @{$targets_of{$dep_key}}],
+                };
+                next DEP;
+            }
+            # Missing dependency
+            if (!exists($target_of{$dep_key})) {
+                if (!exists($missing_deps_in{$key})) {
+                    $missing_deps_in{$key} = {
+                        'keys'   => [@up_keys, $key, $dep_key],
+                        'values' => [],
+                    };
+                }
+                push(
+                    @{$missing_deps_in{$key}{'values'}},
+                    [$dep_key, $dep_type, $dep_remark],
+                );
+                next DEP;
+            }
+            # OK
+            push(@items, [[$dep_key, $dep_type], @up_units, [$key, $type]]);
+            # add_visitor, is_directly_related=1
+            $state_of{$key}->add_visitor($target_of{$dep_key}, $dep_type, 1)
+        }
+        # Float current target up dependency chain
+        my $is_directly_related = 1;
+        UP_KEY:
+        for my $up_key (reverse(@up_keys)) {
+            if ($state_of{$up_key}->add_visitor(
+                    $target, $type, $is_directly_related,
+            )) {
+                last UP_KEY;
+            }
+            $is_directly_related = 0;
+        }
+        # Adds triggers
+        for my $trigger_key (@{$target->get_triggers()}) {
+            if (!exists($state_of{$trigger_key})) {
+                unshift(@items, [[$trigger_key, undef]]);
+            }
+        }
+    }
+    # Visitors no longer used
+    for my $state (values(%state_of)) {
+        $state->free_visitors();
+    }
+    # Assigns targets to build context
+    %{$ctx->get_target_of()}
+        = map {($_->get_id() => $_->get_target())} values(%state_of);
+
+    # Report cyclic dependencies
+    # Report duplicated targets
+    # Report missing dependencies
+    # Report bad keys in target select
+    if (keys(%cyc_in)) {
+        return $E->throw($E->BUILD_TARGET_CYC, \%cyc_in);
+    }
+    if (keys(%dup_in)) {
+        return $E->throw($E->BUILD_TARGET_DUP, \%dup_in);
+    }
+    my @ignore_missing_dep_ns_list = map {$_ eq q{/} ? q{} : $_} (
+        _props($attrib_ref, 'ignore-missing-dep-ns', $ctx)
+    );
+    KEY:
+    for my $key (sort(keys(%missing_deps_in))) {
+        my $target = $target_of{$key};
+        for my $ns (@ignore_missing_dep_ns_list) {
+            if ($UTIL->ns_common($ns, $target->get_ns()) eq $ns) { # target in ns
+                my $hash_ref = delete($missing_deps_in{$key});
+                my @deps = @{$hash_ref->{"values"}};
+                for my $dep (@deps) {
+                    $EVENT->(
+                        FCM::Context::Event->MAKE_BUILD_TARGET_MISSING_DEP,
+                        $key, @{$dep},
+                    );
+                }
+                next KEY;
+            }
+        }
+    }
+    if (keys(%missing_deps_in)) {
+        return $E->throw($E->BUILD_TARGET_DEP, \%missing_deps_in);
+    }
+    if (exists($select_by{key}{q{}})) {
+        my @bad_keys = grep {!exists($state_of{$_})} keys(%{$select_by{key}{q{}}});
+        if (@bad_keys) {
+            return $E->throw($E->BUILD_TARGET_BAD, \@bad_keys);
+        }
+    }
+    # Walk the tree and report it
+    my @report_items = map {[$_]} sort @target_keys;
+    my %reported;
+    ITEM:
+    while (my $item = pop(@report_items)) {
+        my ($key, @stack) = @{$item};
+        my @deps = sort {$a->[0]->get_key() cmp $b->[0]->get_key()}
+            @{$state_of{$key}->get_deps()};
+        my @more_items = reverse(map {[$_->[0]->get_key(), @stack, $key]} @deps);
+        my $n_more_items;
+        if (exists($reported{$key})) {
+            $n_more_items = scalar(@more_items);
+        }
+        else {
+            push(@report_items, @more_items);
+        }
+        $attrib_ref->{util}->event(
+            FCM::Context::Event->MAKE_BUILD_TARGET_STACK,
+            $key, scalar(@stack), $n_more_items,
+        );
+        $reported{$key} = 1;
+    }
+    $EVENT->(
+        FCM::Context::Event->MAKE_BUILD_TARGET_SELECT,
+        {map {$_ => $target_of{$_}} @target_keys},
+    );
+    # TODO: error if nothing to build?
+
+    # Checks whether properties with name-spaces are valid.
+    my @invalid_prop_ns_list;
+    while (my ($name, $prop) = each(%{$ctx->get_prop_of()})) {
+        while (my ($ns, $prop_ctx) = each(%{$prop->get_ctx_of()})) {
+            if (    !$prop_ctx->get_inherited()
+                &&  !exists($target_of{$ns})
+                &&  !exists($has_ns_in{$ns})
+            ) {
+                push(
+                    @invalid_prop_ns_list,
+                    [$ctx->get_id(), $name, $ns, $prop_ctx->get_value()],
+                );
+            }
+        }
+    }
+    if (@invalid_prop_ns_list) {
+        return $E->throw($E->MAKE_PROP_NS, \@invalid_prop_ns_list);
+    }
+
+    $EVENT->(FCM::Context::Event->MAKE_BUILD_TARGET_SELECT_TIMER, $timer->());
+
+    # Returns list of keys of top targets, and the states
+    ([map {$state_of{$_}} reverse(@target_keys)], \%state_of);
+}
+
+# Returns true if $target dependencies are done.
+sub _target_deps_are_done {
+    my ($state, $state_hash_ref, $stack_ref) = @_;
+    my @deps = map {[$_->[0]->get_key(), $_->[1]]} @{$state->get_deps()};
+    for my $k (sort grep {$state_hash_ref->{$_}->is_ready()} map {$_->[0]} @deps) {
+        if (!grep {$_->get_id() eq $k} @{$stack_ref}) {
+            push(@{$stack_ref}, $state_hash_ref->{$k});
+        }
+    }
+    my %not_done
+        = map  {@{$_}}
+          grep {!$_->[1]->is_done()}
+          map  {[$_->[0], $state_hash_ref->{$_->[0]}]}
+          @deps;
+    if (keys(%not_done)) {
+        $state->set_value($STATE->PENDING);
+        while (my ($k, $s) = each(%not_done)) {
+            $state->get_pending_for()->{$k} = $s;
+            $s->get_needed_by()->{$state->get_id()} = $state;
+        }
+        return 0;
+    }
+    return 1;
+}
+
+# Returns true if $target has failed dependencies.
+sub _target_check_failed_dep {
+    my ($state, $state_hash_ref) = @_;
+    my $target = $state->get_target();
+    for my $dep (@{$state->get_deps()}) {
+        my ($target_of_dep, $type_of_dep) = @{$dep};
+        if ($target_of_dep->is_failed()) {
+            return 1;
+        }
+        if (    exists($target_of_dep->get_status_of()->{$type_of_dep})
+            &&  $target_of_dep->get_status_of()->{$type_of_dep}
+                    eq $target->ST_FAILED
+        ) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+# Returns true if $target is out of date.
+sub _target_check_ood {
+    my ($state, $state_hash_ref, $checksum_method) = @_;
+    my $target = $state->get_target();
+    # Dependencies
+    my $rc;
+    for my $dep (@{$state->get_deps()}) {
+        my ($target_of_dep, $type_of_dep) = @{$dep};
+        if (    $target_of_dep->is_modified()
+            ||  exists($target_of_dep->get_status_of()->{$type_of_dep})
+                &&  $target_of_dep->get_status_of()->{$type_of_dep}
+                    eq $target->ST_MODIFIED
+        ) {
+            if (exists($target->get_status_of()->{$type_of_dep})) {
+                $target->get_status_of()->{$type_of_dep} = $target->ST_MODIFIED;
+                if (    $target->get_path_of_prev()
+                    &&  $target->get_path() ne $target->get_path_of_prev()
+                ) {
+                    # Inherited build, cannot just pass on a status
+                    $rc = 1;
+                }
+            }
+            else {
+                $rc = 1;
+            }
+        }
+    }
+    if ($rc || $target->get_status() eq $target->ST_OOD) {
+        return 1;
+    }
+    # Dest and properties
+    my $path_of_prev = $target->get_path_of_prev();
+    my $checksum = $target->get_checksum();
+    my $prop_hash_ref = $target->get_prop_of();
+    my $prop_of_prev_hash_ref = $target->get_prop_of_prev_of();
+    (       !$path_of_prev
+        ||  !-e $path_of_prev
+        ||  $UTIL->file_checksum($path_of_prev, $checksum_method) ne $checksum
+        ||  $UTIL->hash_cmp($prop_hash_ref, $prop_of_prev_hash_ref)
+    );
+}
+
+# Callback to prepare the target for the task.
+sub _target_prep {
+    my ($state, $ctx) = @_;
+    my $target = $state->get_target();
+    # Creates the container directory, where necessary
+    my %paths_of_dirs_set;
+    for my $t (
+        $target,
+        map {$ctx->get_target_of($_)} @{$target->get_triggers()},
+    ) {
+        $paths_of_dirs_set{dirname($t->get_path())} = 1;
+    }
+    for my $path_of_dir (keys(%paths_of_dirs_set)) {
+        if (!-d $path_of_dir) {
+            eval {mkpath($path_of_dir)};
+            if ($@) {
+                return $E->throw($E->DEST_CREATE, $path_of_dir);
+            }
+        }
+    }
+    # Put in required info
+    if ($target->get_info_of('paths')) {
+        @{$target->get_info_of('paths')} = @{$ctx->get_dests()};
+    }
+    if ($target->get_info_of('deps')) {
+        my $info_deps_ref = $target->get_info_of('deps');
+        my %set_of = map {$_ => {}} keys(%{$info_deps_ref});
+        for my $dep (@{$state->get_deps()}) {
+            my ($target_of_dep, $type) = @{$dep};
+            my $key = $target_of_dep->get_key();
+            if (exists($set_of{$type}) && !$set_of{$type}{$key}) {
+                if ($target_of_dep->get_ns() eq $target->get_ns()) {
+                    # E.g. main *.o of *.exe
+                    unshift(@{$info_deps_ref->{$type}}, $key);
+                }
+                else {
+                    push(@{$info_deps_ref->{$type}}, $key);
+                }
+                $set_of{$type}{$key} = 1;
+            }
+        }
+    }
+}
+
+# Sets state and stack when a $target has failed to update or cannot be updated
+# due to failed dependencies.
+sub _target_update_failed {
+    my ($stat_hash_ref,
+        $ctx,
+        $target,
+        $state_hash_ref,
+        $stack_ref,
+        $elapsed_time, # only defined if target update action is done
+    ) = @_;
+    my $key = $target->get_key();
+    my $state = $state_hash_ref->{$key};
+    $state->set_value($STATE->DONE);
+    # If this target is needed by other targets...
+    while (my ($k, $s) = each(%{$state->get_needed_by()})) {
+        my $pending_for_ref = $s->get_pending_for();
+        delete($pending_for_ref->{$key});
+        if (!keys(%{$pending_for_ref})) {
+            $s->set_value($STATE->DONE);
+            # Remove from stack
+            @{$stack_ref} = grep {$_->get_id() ne $k} @{$stack_ref};
+            $s->get_target()->set_status($target->ST_FAILED);
+            push(@{$s->get_target()->get_failed_by()}, $key);
+        }
+    }
+    if (defined($elapsed_time)) { # Done target update
+        my $target0 = $ctx->get_target_of()->{$target->get_key()};
+        $target0->set_info_of({}); # unset
+        $target0->set_checksum(undef);
+        $target0->set_path(undef);
+        $target0->set_prop_of_prev_of({}); # unset
+        $target0->set_path_of_prev(undef); # unset
+        $target0->set_status($target->ST_FAILED);
+        push(@{$target0->get_failed_by()}, $target->get_key());
+        ++$stat_hash_ref->{$target->get_task()}{n}{$target->ST_FAILED};
+        $stat_hash_ref->{$target->get_task()}{t} += $elapsed_time;
+    }
+    else { # No target update required
+        $target->set_path(undef);
+        $target->set_prop_of_prev_of({}); # unset
+        $target->set_path_of_prev(undef); # unset
+        $target->set_status($target->ST_FAILED);
+        for my $dep (@{$state->get_deps()}) {
+            my ($dep_target, $dep_type) = @{$dep};
+            my $dep_key = $dep_target->get_key();
+            if (    $dep_target->is_failed()
+                &&  !grep {$_ eq $dep_key} @{$target->get_failed_by()}
+            ) {
+                push(@{$target->get_failed_by()}, $dep_key);
+            }
+        }
+        ++$stat_hash_ref->{$target->get_task()}{n}{$target->ST_FAILED};
+    }
+    $EVENT->(
+        FCM::Context::Event->MAKE_BUILD_TARGET_FAIL, $target, $elapsed_time,
+    );
+}
+
+# Sets state and stack when a $target is up to date or updated successfully.
+sub _target_update_ok {
+    my ($stat_hash_ref,
+        $ctx,
+        $target,
+        $state_hash_ref,
+        $stack_ref,
+        $elapsed_time, # only defined if target update action is done
+    ) = @_;
+    my $key = $target->get_key();
+    my $state = $state_hash_ref->{$key};
+    $state->set_value($STATE->DONE);
+    # If this target is needed by other targets...
+    my @released_pending_states;
+    while (my ($k, $s) = each(%{$state->get_needed_by()})) {
+        my $pending_for_ref = $s->get_pending_for();
+        delete($pending_for_ref->{$key});
+        if ($s->is_pending() && !keys(%{$pending_for_ref})) {
+            $s->set_value($STATE->READY);
+            if (!grep {$_->get_id() eq $k} @{$stack_ref}) {
+                push(@released_pending_states, $s);
+            }
+        }
+    }
+    push(
+        @{$stack_ref},
+        sort {$a->get_id() cmp $b->get_id()} @released_pending_states,
+    );
+    if (defined($elapsed_time)) { # Done target update
+        my $target0 = $ctx->get_target_of()->{$target->get_key()};
+        $target0->set_info_of({}); # unset
+        $target0->set_checksum($target->get_checksum());
+        $target0->set_path($target->get_path());
+        $target0->set_prop_of_prev_of({}); # unset
+        $target0->set_path_of_prev(undef); # unset
+        $target0->set_status($target->get_status());
+        ++$stat_hash_ref->{$target->get_task()}{n}{$target->get_status()};
+        $stat_hash_ref->{$target->get_task()}{t} += $elapsed_time;
+    }
+    else { # No target update required
+        if ($target->get_path_of_prev()) {
+            $target->set_path($target->get_path_of_prev());
+        }
+        $target->set_prop_of_prev_of({}); # unset
+        $target->set_path_of_prev(undef); # unset
+        $target->set_status($target->ST_UNCHANGED);
+        ++$stat_hash_ref->{$target->get_task()}{n}{$target->ST_UNCHANGED};
+    }
+    $EVENT->(
+        FCM::Context::Event->MAKE_BUILD_TARGET_DONE, $target, $elapsed_time,
+    );
+}
+
+# Returns a list containing the inherited contexts with the same ID as $ctx.
+sub _i_ctx_list {
+    my ($m_ctx, $ctx) = @_;
+    grep
+        {defined()}
+    map
+        {$_->get_ctx_of($ctx->get_id())}
+    @{$m_ctx->get_inherit_ctx_list()};
+}
+
+# Returns a function that returns the previous source/target of a specified key.
+sub _prev_hash_item_getter {
+    my ($m_ctx, $ctx, $getter_ref) = @_;
+    my $p_m_ctx = $m_ctx->get_prev_ctx();
+    my %p_item_of;
+    my $ctx_id = $ctx->get_id();
+    if (defined($p_m_ctx) && defined($p_m_ctx->get_ctx_of($ctx_id))) {
+        %p_item_of = %{$getter_ref->($p_m_ctx->get_ctx_of($ctx_id))};
+    }
+    else {
+        for my $i_ctx (_i_ctx_list($m_ctx, $ctx)) {
+            %p_item_of = (%p_item_of, %{$getter_ref->($i_ctx)});
+        }
+    }
+    sub {exists($p_item_of{$_[0]}) ? $p_item_of{$_[0]} : undef};
+}
+
+# Perform final actions.
+# Archive intermediate target directories if necessary.
+sub _finally {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    if (!$m_ctx->get_option_of('archive')) {
+        return;
+    }
+    my %can_archive = map {($_ => 1)} _props(
+        $attrib_ref, 'archive-ok-target-category', $ctx);
+    opendir(my $handle, $ctx->get_dest());
+    while (my $name = readdir($handle)) {
+        if ($can_archive{$name}) {
+            my @command = (
+                qw{tar -c -z}, '-C', $ctx->get_dest(),
+                '-f', catfile($ctx->get_dest(), $name . '.tar.gz'),
+                $name,
+            );
+            my %value_of = %{$UTIL->shell_simple(\@command)};
+            if ($value_of{'rc'} == 0) {
+                rmtree(catfile($ctx->get_dest(), $name));
+            }
+        }
+    }
+    closedir($handle);
+}
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::State;
+use base qw{FCM::Class::HASH};
+
+use constant {
+    DONE       => 'DONE',       # state value
+    READY      => 'READY',      # state value
+    PENDING    => 'PENDING',    # state value
+};
+
+__PACKAGE__->class({
+    cyclic_ok   => '$',
+    deps        => '@',
+    floatables  => '%',
+    id          => '$',
+    needed_by   => '%',
+    pending_for => '%',
+    target      => 'FCM::Context::Make::Build::Target',
+    value       => {isa => '$', default => READY},
+    visited_by  => '%',
+});
+
+sub add_visitor {
+    my ($self, $dep_target, $dep_type, $is_directly_related) = @_;
+    my $dep_key = $dep_target->get_key();
+    my $dep_str = join(':', $dep_key, $dep_type);
+    # Dependency has already visited me, return cached return value
+    if (exists($self->get_visited_by()->{$dep_str})) {
+        return $self->get_visited_by()->{$dep_str};
+    }
+    # Adopt dep_target as my dependency if there is a policy to do so
+    my $target = $self->get_target();
+    my $policy = $target->get_dep_policy_of($dep_type);
+    if (    $policy
+        &&  ($policy ne $target->POLICY_FILTER_IMMEDIATE || $is_directly_related)
+        &&  (!grep {$_->[0]->get_key() eq $dep_key} @{$self->get_deps()})
+        &&  (!grep {$_ eq $dep_key} @{$target->get_triggers()})
+    ) {
+        push(@{$self->get_deps()}, [$dep_target, $dep_type]);
+    }
+    # If target is captured by me, return true.
+    # Otherwise, return false, and the target is a floatable.
+    $self->get_visited_by()->{$dep_str}
+        = ($policy && $policy eq $target->POLICY_CAPTURE);
+    if (    !$self->get_visited_by()->{$dep_str}
+        &&  !exists($self->get_floatables()->{$dep_str})
+    ) {
+        $self->get_floatables()->{$dep_str} = [$dep_target, $dep_type];
+    }
+    return $self->get_visited_by()->{$dep_str};
+}
+
+sub free_visitors {
+    my ($self) = @_;
+    %{$self->get_floatables()} = ();
+    %{$self->get_visited_by()} = ();
+}
+
+sub is_done {
+    $_[0]->{value} eq DONE;
+}
+
+sub is_pending {
+    $_[0]->{value} eq PENDING;
+}
+
+sub is_ready {
+    $_[0]->{value} eq READY;
+}
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build;
+
+=head1 DESCRIPTION
+
+Implements the build sub-system. An instance of this class is expected to be
+initialised and called by L<FCM::System::Make|FCM::System::Make>.
+
+=head1 METHODS
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=head1 ATTRIBUTES
+
+The $class->new(\%attrib) method of this class supports the following
+attributes:
+
+=over 4
+
+=item config_parser_of
+
+A HASH to map the labels in a configuration file to their parsers. (default =
+%FCM::System::Make::Build::CONFIG_PARSER_OF)
+
+=item target_select_by
+
+A HASH to map the default target selector. The keys should be "category", "key",
+"ns", or "task". (default = %FCM::System::Make::Build::TARGET_SELECT_by)
+
+=item file_type_utils
+
+An ARRAY of file type utility classes to be loaded into the file_type_util_of
+HASH. (default = @FCM::System::Make::Build::FILE_TYPE_UTILS)
+
+=item file_type_util_of
+
+A HASH to map the file type names to the utilities to manipulate the given file
+types. An values in this HASH overrides the classes in I<file_type_utils>.
+(default = determined by I<file_type_utils>)
+
+=item prop_of
+
+A HASH to map the names of the properties to their settings. Each setting
+is a 2-element ARRAY reference, where element [0] is the default setting
+and element [1] is a flag to indicate whether the property accepts a name-space
+or not. (default = %FCM::System::Make::Build::PROP_OF + values loaded from the
+file type utilities)
+
+=item util
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType.pm	(revision 5095)
@@ -0,0 +1,226 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType;
+use base qw{FCM::Class::CODE};
+
+use Text::ParseWords qw{shellwords};
+
+# Creates the class.
+__PACKAGE__->class(
+    {   id                         => '$',
+        file_ext                   => '$',
+        file_pat                   => '$',
+        file_she                   => '$',
+        source_analyse_always      => '$',
+        source_analyse_dep_of      => '%',
+        source_analyse_more        => '&',
+        source_analyse_more_init   => '&',
+        source_to_targets          => '&',
+        target_deps_filter         => '&',
+        target_file_ext_of         => '%',
+        target_file_name_option_of => '%',
+        task_class_of              => '%',
+        task_of                    => '%',
+        util                       => '&',
+    },
+    {   init => \&_init,
+        action_of => {
+            (map {my $key = $_; ($key => sub {$_[0]->{$key}})}
+                qw{
+                    id
+                    file_ext
+                    file_pat
+                    file_she
+                    source_analyse_always
+                    target_file_ext_of
+                    target_file_name_option_of
+                    task_of
+                }
+            ),
+            source_analyse      => \&_source_analyse,
+            source_analyse_deps => sub {keys(%{$_[0]->{source_analyse_dep_of}})},
+            source_to_targets   => sub {$_[0]->{source_to_targets}->(@_)},
+            target_deps_filter  => sub {$_[0]->{target_deps_filter}->(@_)},
+        },
+    },
+);
+
+# Initialises some attributes.
+sub _init {
+    my ($attrib_ref) = @_;
+    while (my ($key, $class) = each(%{$attrib_ref->{task_class_of}})) {
+        $attrib_ref->{util}->class_load($class);
+        $attrib_ref->{task_of}{$key}
+            = $class->new({util => $attrib_ref->{util}});
+    }
+}
+
+# Reads information according to the $source.
+sub _source_analyse {
+    my ($attrib_ref, $source) = @_;
+    my %no_dep_of;
+    my %dep_type_of
+        = map {($_ => 1)} keys(%{$attrib_ref->{source_analyse_dep_of}});
+    while (my $type = each(%dep_type_of)) {
+        my $key = 'no-dep.' . $type;
+        if ($source->get_prop_of($key)) {
+            for my $v (shellwords($source->get_prop_of($key))) {
+                if ($v eq '*') {
+                    delete($dep_type_of{$type});
+                }
+                else {
+                    $no_dep_of{$type}{$v} = 1;
+                }
+            }
+        }
+    }
+    if (!keys(%dep_type_of) && !$attrib_ref->{source_analyse_always}) {
+        return;
+    }
+    my $path = $source->get_path();
+    my $handle = $attrib_ref->{util}->file_load_handle($path);
+
+    my @dep_types = keys(%dep_type_of)
+        ? keys(%dep_type_of) : (_source_analyse_deps($attrib_ref));
+    my (%dep_of, %info_of, %state);
+    $attrib_ref->{source_analyse_more_init}->(\%info_of, \%state);
+    LINE:
+    while (my $line = readline($handle)) {
+        chomp($line);
+        TYPE:
+        for my $type (@dep_types) {
+            my ($item, $can_analyse_more)
+                = $attrib_ref->{source_analyse_dep_of}{$type}->($line);
+            if ($item) {
+                $dep_of{$type}{$item} = 1;
+                if ($can_analyse_more) {
+                    last TYPE;
+                }
+                else {
+                    next LINE;
+                }
+            }
+        }
+        $attrib_ref->{source_analyse_more}->($line, \%info_of, \%state);
+    }
+
+    close($handle);
+    $source->set_info_of(\%info_of);
+    while (my ($type, $hash_ref) = each(%dep_of)) {
+        while (my $item = each(%{$hash_ref})) {
+            if (!exists($no_dep_of{$type}{$item})) {
+                push(@{$source->get_deps()}, [$item, $type]);
+            }
+        }
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType;
+    my $file_type_util = FCM::System::Make::Build::FileType->new(\%attrib);
+    $file_type_util->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+An abstract class to implement the shared methods for gathering information to
+build different types of source files.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance.
+
+=item $instance->id()
+
+Returns the recommended ID of this file type.
+
+=item $instance->file_ext()
+=item $instance->file_pat()
+=item $instance->file_she()
+
+Returns the recommended file name extension, file name pattern and file she-bang
+line pattern of this file type.
+
+=item $instance->source_analyse($source)
+
+Analysis $source for dependencies and other information. Add or modify items in
+@{$source->get_deps()} and %{$source->get_info_of()}.
+
+=item $instance->source_analyse_deps()
+
+Returns a list containing the possible dependency types.
+
+=item $instance->source_analyse_always()
+
+Returns true if $instance->source_analyse($handle,\@dep_types) can read
+information other than dependencies.
+
+=item $instance->source_to_targets($source,\%prop_of)
+
+Using the information in $source, creates and returns the contexts of a list of
+suitable build targets. Where appropriate, the %prop_of should contain a mapping
+of the names of the properties used by this method and their values.
+
+=item $instance->target_deps_filter($target)
+
+This may modify @{$target->get_deps()} in place based on values in
+%{$target->get_prop_of()}. This method is normally implemented by sub-classes.
+
+=item $instance->target_file_ext_of()
+
+Returns a HASH reference containing a map between the named types of file
+extensions used by the $instance->source_to_targets($source,\%prop_of) method
+and their default values.
+
+=item $instance->target_file_name_option_of()
+
+Returns a HASH reference containing a map between the named types of files
+used by the $instance->source_to_targets($source,\%prop_of) method
+and their default settings for other file naming options.
+
+=item $instance->task_of()
+
+Returns a HASH reference containing a map between the named tasks for this file
+type and their implementation objects. Each task should have a
+$task->main($target) method to update a target and optionally a $task->prop_of()
+method to return a HASH reference containing a map between the named properties
+used by the task and their default values.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/C.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/C.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/C.pm	(revision 5095)
@@ -0,0 +1,156 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::C;
+use base qw{FCM::System::Make::Build::FileType};
+
+use FCM::Context::Make::Build; # for FCM::Context::Make::Build::Target
+use FCM::System::Make::Build::Task::Compile::C;
+use FCM::System::Make::Build::Task::Install;
+use FCM::System::Make::Build::Task::Link::C;
+use File::Basename qw{basename};
+
+# RE: file (base) name
+my $RE_FILE = qr{[\w\-+.]+}imsx;
+
+# RE: main program
+my $RE_MAIN = qr{\A\s*(?:int|void)?\s*main\b\s*\(?}msx;
+
+my %SOURCE_ANALYSE_DEP_OF = (
+    include => sub { $_[0] =~ qr{\A\#\s*include\s+"($RE_FILE)"}msx },
+    o => sub { lc($_[0]) =~ qr{\A\s*/\*\s*depends\s*on\s*:\s*($RE_FILE)}imsx },
+);
+my $TARGET = 'FCM::Context::Make::Build::Target';
+my %TASK_CLASS_OF = (
+    'compile' => 'FCM::System::Make::Build::Task::Compile::C',
+    'install' => 'FCM::System::Make::Build::Task::Install',
+    'link'    => 'FCM::System::Make::Build::Task::Link::C',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType->new({
+            id                       => 'c',
+            file_ext                 => '.c .i .m .mi',
+            source_analyse_always    => 1,
+            source_analyse_dep_of    => {%SOURCE_ANALYSE_DEP_OF},
+            source_analyse_more      => \&_source_analyse_more,
+            source_analyse_more_init => \&_source_analyse_more_init,
+            source_to_targets        => \&_source_to_targets,
+            target_file_ext_of       => {bin => '.exe', o => '.o'},
+            task_class_of            => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+sub _source_analyse_more {
+    my ($line, $info_hash_ref) = @_;
+    if (!$info_hash_ref->{main} && $line =~ $RE_MAIN) {
+        return $info_hash_ref->{main} = 1;
+    }
+    return;
+}
+
+sub _source_analyse_more_init {
+    my ($info_hash_ref) = @_;
+    $info_hash_ref->{main} = 0;
+}
+
+# Returns a list of targets for a given build source.
+sub _source_to_targets {
+    my ($attrib_ref, $source, $prop_hash_ref) = @_;
+    my $key = basename($source->get_path());
+    my ($ext, $root) = $attrib_ref->{util}->file_ext($key);
+    my %dot = %{$prop_hash_ref};
+    my @deps = @{$source->get_deps()};
+    my $key_o = lc($root) . $dot{o}; # lc for legacy
+    my @targets = (
+        $TARGET->new(
+            {   category  => $TARGET->CT_INCLUDE,
+                deps      => [@deps],
+                dep_policy_of => {'include' => $TARGET->POLICY_CAPTURE},
+                key       => $key,
+                status_of => {'include' => $TARGET->ST_UNKNOWN},
+                task      => 'install',
+            }
+        ),
+        $TARGET->new(
+            {   category      => $TARGET->CT_O,
+                deps          => [@deps],
+                dep_policy_of => {'include' => $TARGET->POLICY_CAPTURE},
+                info_of       => {paths => []},
+                key           => $key_o,
+                task          => 'compile',
+            }
+        ),
+    );
+
+    if ($source->get_info_of()->{'main'}) {
+        my @link_deps = grep {$_->[1] eq 'o'} @deps;
+        push(
+            @targets,
+            $TARGET->new(
+                {   category   => $TARGET->CT_BIN,
+                    deps       => [[$key_o, 'o'], @link_deps],
+                    dep_policy_of => {
+                        map {($_ => $TARGET->POLICY_CAPTURE)} qw{o o.special},
+                    },
+                    info_of    => {
+                        paths => [], deps => {o => [], 'o.special' => []},
+                    },
+                    key        => $root . $dot{bin},
+                    task       => 'link',
+                }
+            )
+        );
+    }
+    return @targets;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::C
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::C;
+    my $helper = FCM::System::Make::Build::FileType::C->new();
+    $helper->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+A wrapper of
+L<FCM::System::Make::Build::FileType|FCM::System::Make::Build::FileType> with
+configurations to work with C source files.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/CPP.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/CPP.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/CPP.pm	(revision 5095)
@@ -0,0 +1,92 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::CPP;
+use base qw{FCM::System::Make::Build::FileType};
+
+use FCM::Context::Make::Build; # for FCM::Context::Make::Build::Target
+use FCM::System::Make::Build::Task::Preprocess::C;
+
+# Dependency types and CODE to extract them
+my %SOURCE_ANALYSE_DEP_OF
+    = (include => sub { $_[0] =~ qr{\A\#\s*include\s+"([\w\-+.]+)"}msx });
+
+# Handler of tasks
+my %TASK_CLASS_OF
+    = (process => 'FCM::System::Make::Build::Task::Preprocess::C');
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType->new({
+            id                    => 'cpp',
+            file_ext              => '.c .m .cc .cp .cxx .cpp .CPP .c++ .C .mm .M',
+            source_analyse_dep_of => {%SOURCE_ANALYSE_DEP_OF},
+            source_to_targets     => \&_source_to_targets,
+            target_file_ext_of    => {},
+            task_class_of         => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# Returns a list of targets for a given build source.
+sub _source_to_targets {
+    my ($attrib_ref, $source) = @_;
+    my $TARGET = 'FCM::Context::Make::Build::Target';
+    $TARGET->new(
+        {   category      => $TARGET->CT_SRC,
+            deps          => [@{$source->get_deps()}],
+            dep_policy_of => {'include' => $TARGET->POLICY_CAPTURE},
+            info_of       => {paths => []},
+            key           => $source->get_ns(),
+            task          => 'process',
+        }
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::CPP
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::CPP;
+    my $helper = FCM::System::Make::Build::FileType::CPP->new();
+    $helper->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+A wrapper of
+L<FCM::System::Make::Build::FileType|FCM::System::Make::Build::FileType> with
+configurations to work with C source files for preprocessing.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/CXX.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/CXX.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/CXX.pm	(revision 5095)
@@ -0,0 +1,72 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::CXX;
+use base qw{FCM::System::Make::Build::FileType::C};
+
+use FCM::System::Make::Build::Task::Compile::CXX;
+use FCM::System::Make::Build::Task::Install;
+use FCM::System::Make::Build::Task::Link::CXX;
+
+my %TASK_CLASS_OF = (
+    'compile' => 'FCM::System::Make::Build::Task::Compile::CXX',
+    'install' => 'FCM::System::Make::Build::Task::Install',
+    'link'    => 'FCM::System::Make::Build::Task::Link::CXX',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType::C->new({
+            id            => 'cxx',
+            file_ext      => '.cc .cp .cxx .cpp .CPP .c++ .C .mm .M .mii',
+            task_class_of => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::CXX
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::CXX;
+    my $helper = FCM::System::Make::Build::FileType::CXX->new();
+    $helper->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+A wrapper of
+L<FCM::System::Make::Build::FileType::C|FCM::System::Make::Build::FileType::C>
+with configurations to work with C++ source files.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Data.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Data.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Data.pm	(revision 5095)
@@ -0,0 +1,82 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::Data;
+use base qw{FCM::System::Make::Build::FileType};
+
+use FCM::Context::Make::Build;    # for FCM::Context::Make::Build::Target
+use FCM::System::Make::Build::Task::Install;
+
+# Handler of tasks
+my %TASK_CLASS_OF = (install => 'FCM::System::Make::Build::Task::Install');
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType->new({
+            id                    => q{},
+            source_analyse_dep_of => {},
+            source_to_targets     => \&_source_to_targets,
+            target_file_ext_of    => {},
+            task_class_of         => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# Returns a list of targets for a given build source.
+sub _source_to_targets {
+    my ($attrib_ref, $source) = @_;
+    FCM::Context::Make::Build::Target->new(
+        {   category => FCM::Context::Make::Build::Target->CT_ETC,
+            key      => $source->get_ns(),
+            task     => 'install',
+        }
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::Data
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::Data;
+    my $helper = FCM::System::Make::Build::FileType::Data->new();
+    $helper->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+A class based on
+L<FCM::System::Make::Build::FileType|FCM::System::Make::Build::FileType>
+with configurations to install data files to the etc/ sub-directory of a build.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/FPP.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/FPP.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/FPP.pm	(revision 5095)
@@ -0,0 +1,67 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::FPP;
+use base qw{FCM::System::Make::Build::FileType::CPP};
+
+use FCM::System::Make::Build::Task::Preprocess::Fortran;
+
+my %TASK_CLASS_OF
+    = (process => 'FCM::System::Make::Build::Task::Preprocess::Fortran');
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType::CPP->new({
+            id            => 'fpp',
+            file_ext      => '.F90 .F95 .F .FTN .FOR',
+            task_class_of => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::FPP
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::FPP;
+    my $helper = FCM::System::Make::Build::FileType::FPP->new();
+    $helper->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+A wrapper of
+L<FCM::System::Make::Build::FileType::CPP|FCM::System::Make::Build::FileType::CPP>
+with configurations to work with Fortran source files for preprocessing.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Fortran.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Fortran.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Fortran.pm	(revision 5095)
@@ -0,0 +1,420 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::Fortran;
+use base qw{FCM::System::Make::Build::FileType};
+
+use FCM::Context::Make::Build;    # for FCM::Context::Make::Build::Target
+use FCM::System::Make::Build::Task::Compile::Fortran;
+use FCM::System::Make::Build::Task::ExtractInterface;
+use FCM::System::Make::Build::Task::Install;
+use FCM::System::Make::Build::Task::Link::Fortran;
+use File::Basename qw{basename};
+use Text::Balanced qw{extract_bracketed extract_delimited};
+
+# Recommended file extensions of this utility
+our $FILE_EXT = '.F .F90 .F95 .FOR .FTN .f .f90 .f95 .for .ftn .inc';
+
+# List of Fortran intrinsic modules
+our @INTRINSIC_MODULES = qw{
+    ieee_arithmetic
+    ieee_exceptions
+    ieee_features
+    iso_c_binding
+    iso_fortran_env
+    omp_lib
+    omp_lib_kinds
+};
+
+# Prefix for dependency name that is only applicable under OMP
+our $OMP_PREFIX = '!$';
+
+# Regular expressions
+my $RE_FILE = qr{[\w\-+.]+}imsx;
+my $RE_NAME = qr{[A-Za-z]\w*}imsx;
+my $RE_SPEC = qr{
+    character|class|complex|double\s*complex|double\s*precision|integer|
+    logical|procedure|real|type
+}imsx;
+my $RE_UNIT_BASE = qr{
+    block\s*data|
+    module(?!\s*(?:function|subroutine|procedure)\s+)|
+    program|
+}imsx;
+my $RE_UNIT_CALL = qr{subroutine|function}imsx;
+my %RE           = (
+    DEP_O     => qr{\A\s*!\s*depends\s*on\s*:\s*($RE_FILE)}imsx,
+    DEP_USE   => qr{\A\s*use\s+($RE_NAME)}imsx,
+    DEP_SUBM  => qr{\A\s*submodule\s+\(($RE_NAME)\)}imsx,
+    INCLUDE   => qr{\#?\s*include\s*}imsx,
+    OMP_SENT  => qr{\A(\s*!\$\s+)?(.*)\z}imsx,
+    UNIT_ATTR => qr{\A\s*(?:(?:(?:impure\s+)?elemental|recursive|pure)\s+)+(.*)\z}imsx,
+    UNIT_BASE => qr{\A\s*($RE_UNIT_BASE)\s+($RE_NAME)\b}imsx,
+    UNIT_SUBM => qr{\A\s*(submodule)\s*\(($RE_NAME)\)\s*($RE_NAME)\b}imsx,
+    UNIT_CALL => qr{\A\s*($RE_UNIT_CALL)\s+($RE_NAME)\b}imsx,
+    UNIT_END  => qr{\A\s*(end)(?:\s+($RE_NAME)(?:\s+($RE_NAME))?)?\b}imsx,
+    UNIT_SPEC => qr{\A\s*$RE_SPEC\b(.*)\z}imsx,
+);
+
+# Dependency types and extractors
+my %SOURCE_ANALYSE_DEP_OF = (
+    'f.module'  => \&_source_analyse_dep_module,
+    'include'   => \&_source_analyse_dep_include,
+    'o'         => sub { lc($_[0]) =~ $RE{DEP_O} }, # lc required for legacy
+    'o.special' => sub {},
+);
+# Alias
+my $TARGET = 'FCM::Context::Make::Build::Target';
+# Classes for tasks used by targets of this file type
+my %TASK_CLASS_OF = (
+    'compile'   => 'FCM::System::Make::Build::Task::Compile::Fortran',
+    'compile+'  => 'FCM::System::Make::Build::Task::Compile::Fortran::Extra',
+    'ext-iface' => 'FCM::System::Make::Build::Task::ExtractInterface',
+    'install'   => 'FCM::System::Make::Build::Task::Install',
+    'link'      => 'FCM::System::Make::Build::Task::Link::Fortran',
+);
+# Property suffices of output file extensions
+my %TARGET_EXT_OF = (
+    'bin'           => '.exe',
+    'f90-interface' => '.interface',
+    'f90-mod'       => '.mod',
+    'o'             => '.o',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType->new({
+            id                         => 'fortran',
+            file_ext                   => $FILE_EXT,
+            source_analyse_always      => 1,
+            source_analyse_dep_of      => {%SOURCE_ANALYSE_DEP_OF},
+            source_analyse_more        => \&_source_analyse_more,
+            source_analyse_more_init   => \&_source_analyse_more_init,
+            source_to_targets          => \&_source_to_targets,
+            target_deps_filter         => \&_target_deps_filter,
+            target_file_ext_of         => {%TARGET_EXT_OF},
+            target_file_name_option_of => {'f90-mod' => q{}},
+            task_class_of              => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+sub _source_analyse_more {
+    my ($line, $info_hash_ref, $state) = @_;
+
+    # End Interface
+    if ($state->{in_interface}) {
+        if ($line =~ qr{\A\s*end\s*interface\b}imsx) {
+            $state->{in_interface} = 0;
+        }
+        return 1;
+    }
+
+    # End Program Unit
+    if (@{$state->{stack}} && $line =~ qr{\A\s*end\b}imsx) {
+        my ($end, $type, $symbol) = lc($line) =~ $RE{UNIT_END};
+        if (!$end) {
+            return 1;
+        }
+        my ($top_type, $top_symbol) = @{$state->{stack}->[-1]};
+        if (!$type
+            || $top_type eq $type && (!$symbol || $top_symbol eq $symbol))
+        {
+            pop(@{$state->{stack}});
+            if ($state->{in_contains} && !@{$state->{stack}}) {
+                $state->{in_contains} = 0;
+            }
+        }
+        return 1;
+    }
+
+    # Interface/Contains
+    if ($line =~ qr{\A\s*contains\b}imsx) {
+        $state->{'in_contains'} = 1;
+        return 1;
+    }
+    if ($line =~ qr{\A\s*(?:abstract\s+)?interface\b}imsx) {
+        $state->{'in_interface'} = 1;
+        return 1;
+    }
+
+    # Program Unit
+    my ($type, $symbol, @extras) = _process_prog_unit($line);
+    if ($type) {
+        if (!@{$state->{stack}}) {
+            if ($type eq 'program') {
+                $info_hash_ref->{main} = 1;
+            }
+            $info_hash_ref->{symbols} ||= [];
+            push(@{$info_hash_ref->{symbols}}, [$type, $symbol, @extras]);
+        }
+        push(@{$state->{stack}}, [$type, $symbol]);
+        return 1;
+    }
+    return;
+}
+
+sub _source_analyse_more_init {
+    my ($info_ref, $state) = @_;
+    %{$info_ref} = (main => 0, symbols => []);
+    %{$state} = (in_contains => undef, in_interface => undef, stack => []);
+}
+
+# Reads information: extract an include dependency.
+sub _source_analyse_dep_include {
+    my ($line) = @_;
+    my ($omp_sentinel, $extracted);
+    ($omp_sentinel, $line) = $line =~ $RE{OMP_SENT};
+    ($extracted) = extract_delimited($line, q{'"}, $RE{INCLUDE});
+    if (!$extracted) {
+        return;
+    }
+    $extracted = substr($extracted, 1, length($extracted) - 2);
+    if ($omp_sentinel) {
+        $extracted = $OMP_PREFIX . $extracted;
+    }
+    $extracted;
+}
+
+# Reads information: extract a module dependency.
+sub _source_analyse_dep_module {
+    my ($line) = @_;
+    my ($omp_sentinel, $extracted, $can_analyse_more);
+    ($omp_sentinel, $line) = $line =~ $RE{OMP_SENT};
+    ($extracted) = lc($line) =~ $RE{DEP_USE};
+    if (!$extracted) {
+        ($extracted) = lc($line) =~ $RE{DEP_SUBM};
+        $can_analyse_more = 1;
+    }
+    if (!$extracted || grep {$_ eq $extracted} @INTRINSIC_MODULES) {
+        return;
+    }
+    if ($omp_sentinel) {
+        $extracted = $OMP_PREFIX . $extracted;
+    }
+    ($extracted, $can_analyse_more);
+}
+
+# Parse a statement for program unit header. Returns a list containing the type,
+# the symbol and the signature tokens of the program unit.
+sub _process_prog_unit {
+    my ($string) = @_;
+    my ($type, $symbol, $symbol_parent) = (q{}, q{}, q{});
+    ($type, $symbol) = lc($string) =~ $RE{UNIT_BASE};
+    if ($type) {
+        $type =~ s{\s*}{}gmsx;
+        return ($type, $symbol);
+    }
+    ($type, $symbol_parent, $symbol) = lc($string) =~ $RE{UNIT_SUBM};
+    if ($type) {
+        return ($type, $symbol, $symbol_parent);
+    }
+    $string =~ s/$RE{UNIT_ATTR}/$1/;
+    my ($match) = $string =~ $RE{UNIT_SPEC};
+    if ($match) {
+        $string = $match;
+        if ($string =~ qr{\A \s* \(}msx) {
+            extract_bracketed($string);
+        }
+        elsif ($string =~ qr{\A \s* \*}msx) {
+            $string =~ s{\A \s* \* \d+ \s*}{}msx;
+        }
+    }
+    ($type, $symbol) = lc($string) =~ $RE{UNIT_CALL};
+    if (!$type) {
+        return;
+    }
+    return (lc($type), lc($symbol));
+}
+
+# Returns a list of targets for a given build source.
+sub _source_to_targets {
+    my ($attrib_ref, $source, $ext_hash_ref, $option_hash_ref) = @_;
+    my $key = basename($source->get_path());
+    my $TARGET_OF = sub {
+        my ($symbol, $type) = @_;
+        if (exists($option_hash_ref->{$type})) {
+            my $is_upper = index($option_hash_ref->{$type}, 'case=upper') >= 0;
+            $symbol = $is_upper ? uc($symbol) : lc($symbol);
+        }
+        $symbol . $ext_hash_ref->{$type};
+    };
+    my @deps = map {
+        my ($k, $type) = @{$_};
+        my $ext = $attrib_ref->{util}->file_ext($k);
+          $type eq 'f.module'   ? [$TARGET_OF->($k, 'f90-mod'), 'include', 1]
+        : $type eq 'o' && !$ext ? [$TARGET_OF->($k, 'o'), $type]
+        :                         [$k, $type]
+    } @{$source->get_deps()};
+    # All source files can be used as include files
+    my @targets = (
+        $TARGET->new(
+            {   category  => $TARGET->CT_INCLUDE,
+                deps      => [@deps],
+                dep_policy_of => {'include' => $TARGET->POLICY_CAPTURE},
+                key       => $key,
+                status_of => {'include' => $TARGET->ST_UNKNOWN},
+                task      => 'install',
+            }
+        ),
+    );
+    my ($ext, $root) = $attrib_ref->{util}->file_ext($key);
+    # @{$symbols_ref} contains a list of [$type, $symbol, $symbol_parent]
+    # where $type is the program unit type
+    #       $symbol is the program unit symbol
+    #       $symbol_parent is the parent program unit symbol, e.g. name of
+    #       parent module of a submodule
+    my $symbols_ref = $source->get_info_of()->{symbols};
+    # FIXME: hard code the handling of "*.inc" files as include files
+    if (!defined($symbols_ref) || !@{$symbols_ref} || $ext eq 'inc') {
+        return @targets;
+    }
+    my $key_of_o = $TARGET_OF->($symbols_ref->[0][1], 'o');
+    my @keys_of_mod;
+    for (grep {$_->[0] eq 'module'} @{$symbols_ref}) {
+        my ($type, $symbol) = @{$_};
+        my $key_of_mod = $TARGET_OF->($symbol, 'f90-mod');
+        my @include_deps = grep {$_->[1] eq 'include'} @deps;
+        push(
+            @targets,
+            $TARGET->new(
+                {   category      => $TARGET->CT_INCLUDE,
+                    deps          => [[$key_of_o, 'o']],
+                    dep_policy_of => {
+                        'include' => $TARGET->POLICY_CAPTURE,
+                        'o'       => $TARGET->POLICY_FILTER_IMMEDIATE,
+                    },
+                    key         => $key_of_mod,
+                    task        => 'compile+',
+                }
+            )
+        );
+        push(@keys_of_mod, $key_of_mod);
+    }
+    my @symbol_parents = map {
+        scalar(@{$_}) > 2 ? $TARGET_OF->($_->[2], 'o') : ();
+    } @{$symbols_ref};
+    push(
+        @targets,
+        $TARGET->new(
+            {   category      => $TARGET->CT_O,
+                deps          => [@deps],
+                dep_policy_of => {'include' => $TARGET->POLICY_CAPTURE},
+                info_of       => {paths => [], parents => \@symbol_parents},
+                key           => $key_of_o,
+                task          => 'compile',
+                triggers      => \@keys_of_mod,
+            }
+        ),
+    );
+    if (grep {$_->[0] eq 'subroutine' || $_->[0] eq 'function'} @{$symbols_ref}) {
+        my $target_key = $root . $ext_hash_ref->{'f90-interface'};
+        push(
+            @targets,
+            $TARGET->new(
+                {   category      => $TARGET->CT_INCLUDE,
+                    deps          => [[$key_of_o, 'o'], grep {exists($_->[2])} @deps],
+                    dep_policy_of => {
+                        'include' => $TARGET->POLICY_FILTER_IMMEDIATE,
+                    },
+                    key           => $target_key,
+                    task          => 'ext-iface',
+                }
+            )
+        );
+    }
+    if ($source->get_info_of()->{main}) {
+        my @link_deps = grep {$_->[1] eq 'o' || $_->[1] eq 'o.special'} @deps;
+        push(
+            @targets,
+            $TARGET->new(
+                {   category      => $TARGET->CT_BIN,
+                    deps          => [[$key_of_o, 'o'], @link_deps],
+                    dep_policy_of => {
+                        'o'         => $TARGET->POLICY_CAPTURE,
+                        'o.special' => $TARGET->POLICY_CAPTURE,
+                    },
+                    info_of       => {
+                        paths => [], deps => {o => [], 'o.special' => []},
+                    },
+                    key           => $root . $ext_hash_ref->{bin},
+                    task          => 'link',
+                }
+            )
+        );
+    }
+    return @targets;
+}
+
+# If target's fc.flag-omp property is empty, remove !$OMP dependencies.
+# Otherwise, remove !$OMP sentinels from the dependencies.
+sub _target_deps_filter {
+    my ($attrib_ref, $target) = @_;
+    if ($target->get_prop_of()->{'fc.flag-omp'}) {
+        for my $dep_ref (@{$target->get_deps()}) {
+            if (index($dep_ref->[0], $OMP_PREFIX) == 0) {
+                substr($dep_ref->[0], 0, length($OMP_PREFIX), q{});
+            }
+        }
+    }
+    else {
+        $target->set_deps(
+            [grep {index($_->[0], $OMP_PREFIX) == -1} @{$target->get_deps()}],
+        );
+    }
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::Fortran
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::Fortran;
+    my $file_type_util = FCM::System::Make::Build::FileType::Fortran->new();
+
+    $file_type_util->source_analyse($source);
+
+    my @targets = $file_type_util->source_to_targets($m_ctx, $ctx, $source);
+
+=head1 DESCRIPTION
+
+A wrapper of
+L<FCM::System::Make::Build::FileType|FCM::System::Make::Build::FileType> with
+configurations to work with Fortran source files.
+
+=head1 TODO
+
+Combine the code with FCM::System::Make::Build::Task::ExtractInterface.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/H.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/H.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/H.pm	(revision 5095)
@@ -0,0 +1,131 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::H;
+use base qw{FCM::System::Make::Build::FileType};
+
+use FCM::Context::Make::Build;    # for FCM::Context::Make::Build::Target
+use FCM::System::Make::Build::Task::Install;
+use File::Basename qw{basename};
+
+# RE: file (base) name
+my $RE_FILE = qr{[\w\-+.]+}imsx;
+
+# Dependency types and CODE to extract them
+my %SOURCE_ANALYSE_DEP_OF = (
+    include => sub { $_[0] =~ qr{\A\#\s*include\s+"($RE_FILE)"}msx },
+
+    # Note: handle ! as a comment, for *.h files containing Fortran source
+    o => sub {
+        $_[0] =~ qr{\A\s*(?:!|/\*)\s*depends\s*on\s*:\s*($RE_FILE)}imsx;
+    },
+);
+
+# Alias
+my $TARGET = 'FCM::Context::Make::Build::Target';
+
+# Handler of tasks
+my %TASK_CLASS_OF = (install => 'FCM::System::Make::Build::Task::Install');
+
+# Property suffices of output file extensions
+my %TARGET_EXT_OF = ('o' => '.o');
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType->new({
+            id                    => 'h',
+            file_ext              => '.h',
+            source_analyse_dep_of => {%SOURCE_ANALYSE_DEP_OF},
+            source_to_targets     => \&_source_to_targets,
+            target_file_ext_of    => {%TARGET_EXT_OF},
+            task_class_of         => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# Returns a list of targets for a given build source.
+sub _source_to_targets {
+    my ($attrib_ref, $source, $prop_hash_ref) = @_;
+    my %dot = %{$prop_hash_ref};
+    my $key = basename($source->get_path());
+    my @deps = map {
+        my $ext = $attrib_ref->{util}->file_ext($_->[0]);
+        $_->[1] eq 'o' && !$ext ? [lc($_->[0]) . $dot{o}, $_->[1]] : $_;
+    } @{$source->get_deps()};
+    $TARGET->new(
+        {   category => $TARGET->CT_INCLUDE,
+            deps     => [@deps],
+            dep_policy_of => {'include' => $TARGET->POLICY_CAPTURE},
+            key      => $key,
+            status_of=> {'include' => $TARGET->ST_UNKNOWN},
+            task     => 'install',
+        }
+    );
+}
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::HPP;
+use base qw{FCM::System::Make::Build::FileType::H};
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build::FileType::H->new({
+            source_analyse_dep_of => {include => $SOURCE_ANALYSE_DEP_OF{include}},
+            target_file_ext_of    => {},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::H
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::H;
+    my $helper = FCM::System::Make::Build::FileType::H->new();
+    $helper->source_analyse($handle);
+
+    my $helper = FCM::System::Make::Build::FileType::HPP->new();
+    $helper->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+A wrapper of
+L<FCM::System::Make::Build::FileType|FCM::System::Make::Build::FileType> with
+configurations to work with C or Fortran preprocessor header files.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/NS.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/NS.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/NS.pm	(revision 5095)
@@ -0,0 +1,210 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+#-------------------------------------------------------------------------------
+
+package FCM::System::Make::Build::FileType::NS;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Make::Build;    # for FCM::Context::Make::Build::Target
+use FCM::System::Make::Build::Task::Archive;
+use FCM::System::Make::Build::Task::Install;
+use File::Spec::Functions qw{catfile};
+
+my $ID = '/';
+
+my %TARGET_FILE_EXT_OF = (a => '.a', etc => '.etc');
+
+my %TASK_CLASS_OF = (
+    'archive' => 'FCM::System::Make::Build::Task::Archive',
+    'install' => 'FCM::System::Make::Build::Task::Install',
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   id                 => {isa => '$', default => $ID},
+        target_file_ext_of => {isa => '%', default => {%TARGET_FILE_EXT_OF}},
+        target_file_name_option_of => '%',
+        task_class_of      => {isa => '%', default => {%TASK_CLASS_OF}},
+        shared_util_of     => '%',
+        task_of            => '%',
+        util               => '&',
+    },
+    {   init => \&_init,
+        action_of => {
+            (map {my $key = $_; ($key => sub {$_[0]->{$key}})}
+                qw{id target_file_ext_of target_file_name_option_of task_of}
+            ),
+            ns_targets_deps => sub {('o')},
+            ns_targets      => \&_ns_targets,
+        },
+    },
+);
+
+# Initialises some attributes.
+sub _init {
+    my ($attrib_ref) = @_;
+    while (my ($key, $class) = each(%{$attrib_ref->{task_class_of}})) {
+        $attrib_ref->{util}->class_load($class);
+        $attrib_ref->{task_of}{$key}
+            = $class->new({util => $attrib_ref->{util}});
+    }
+}
+
+# Returns a list of targets for a given build source.
+sub _ns_targets {
+    my ($attrib_ref, $targets_ref, $prop_hash_ref) = @_;
+    my %target_of;
+    TARGET:
+    for my $target (@{$targets_ref}) {
+        my @ns_targets;
+        for (
+            [sub {!$_[0]->get_type()}          , \&_ns_target_new_etc],
+            [sub {$_[0]->get_category() eq 'o'}, \&_ns_target_new_lib],
+        ) {
+            my ($test, $new) = @{$_};
+            if ($test->($target)) {
+                my $ns_iter = $attrib_ref->{util}->ns_iter(
+                    $target->get_ns(), $attrib_ref->{util}->NS_ITER_UP,
+                );
+                $ns_iter->(); # discard
+                while (defined(my $ns = $ns_iter->())) {
+                    my $ns_target = $new->($ns, $prop_hash_ref);
+                    my $key = $ns_target->get_key();
+                    if (!exists($target_of{$key})) {
+                        $target_of{$key} = $ns_target;
+                    }
+                    push(
+                        @{$target_of{$key}->get_deps()},
+                        [$target->get_key(), $target->get_category()],
+                    );
+                }
+                next TARGET;
+            }
+        }
+    }
+    values(%target_of);
+}
+
+# Returns a new etc target for building data files in a namespace.
+sub _ns_target_new_etc {
+    my ($ns, $prop_hash_ref) = @_;
+    my $DOT_ETC = $prop_hash_ref->{etc};
+    my $TARGET = 'FCM::Context::Make::Build::Target';
+    $TARGET->new(
+        {   category      => $TARGET->CT_ETC,
+            dep_policy_of => {'etc' => $TARGET->POLICY_CAPTURE},
+            key           => ($ns ? catfile($ns, $DOT_ETC) : $DOT_ETC),
+            ns            => $ns,
+            task          => 'install',
+        }
+    );
+}
+
+# Returns a new archive target for building an object library for a namespace.
+sub _ns_target_new_lib {
+    my ($ns, $prop_hash_ref) = @_;
+    my $NAME = 'libo' . $prop_hash_ref->{a}; # FIXME: libo hard-coded
+    my $TARGET = 'FCM::Context::Make::Build::Target';
+    $TARGET->new(
+        {   category      => $TARGET->CT_LIB,
+            dep_policy_of => {'o' => $TARGET->POLICY_CAPTURE},
+            info_of       => {paths => [], deps => {o => []}},
+            key           => ($ns ? catfile($ns, $NAME) : $NAME),
+            ns            => $ns,
+            task          => 'archive',
+        }
+    );
+}
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::NS
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::NS;
+    my $file_type_util = FCM::System::Make::Build::FileType->new(\%attrib);
+    $file_type_util->ns_targets($m_ctx, $ctx, @targets);
+
+=head1 DESCRIPTION
+
+Generates name space level targets.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance.
+
+=item $instance->id()
+
+Returns the recommended ID of this file type.
+
+=item $instance->ns_targets(\@targets,\%prop_of)
+
+Using the information in the original list of targets, creates and returns the
+contexts of a list of extra targets based on the name spaces of the original
+list. In the current settings, a target with no type (i.e. a data file target)
+will generate a C<.etc> target for the container name spaces; a target in the
+C<o> category will generate a C<libo.a> target for the container name spaces.
+
+=item $instance->ns_targets_deps()
+
+Returns a list of dependency types used by
+$instance->ns_targets(\@targets,\%prop_of).
+
+=item $instance->target_file_ext_of()
+
+Returns a HASH reference containing a map between the named types of file
+extensions used by the $instance->ns_targets(\@targets,\%prop_of) method
+and their default values.
+
+=item $instance->target_file_name_option_of()
+
+Returns a HASH reference containing a map between the named types of files
+used by the $instance->source_to_targets($source,\%prop_of) method
+and their default settings for other file naming options.
+
+=item $instance->task_of()
+
+Returns a HASH reference containing a map between the named tasks for this file
+type and their implementation objects. Each task should have a
+$task->main($target) method to update a target and optionally a $task->prop_of()
+method to return a HASH reference containing a map between the named properties
+used by the task and their default values.
+
+=back
+
+=head1 TODO
+
+The configuration in this module is a bit hard coded. It can do with a refactor.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Script.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Script.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/FileType/Script.pm	(revision 5095)
@@ -0,0 +1,100 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::FileType::Script;
+use base qw{FCM::System::Make::Build::FileType};
+
+use FCM::Context::Make::Build;    # for FCM::Context::Make::Build::Target
+use FCM::System::Make::Build::Task::Install;
+use File::Basename qw{basename};
+
+# RE: file (base) name
+my $RE_FILE = qr{[\w\-+.]+}imsx;
+
+# Dependency types and CODE to extract them
+my %SOURCE_ANALYSE_DEP_OF
+    = (bin => sub { $_[0] =~ qr{\A\s*(?:\#|;)\s*calls\s*:\s*($RE_FILE)}imsx });
+
+# Alias
+my $TARGET = 'FCM::Context::Make::Build::Target';
+
+# Handler of tasks
+my %TASK_CLASS_OF = (install => 'FCM::System::Make::Build::Task::Install');
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    $attrib_ref->{dest_keys} ||= [$TARGET->CT_INCLUDE, 'o', 'o.special'];
+    my $SOURCE_TO_TARGETS
+        = sub {_source_to_targets($attrib_ref->{dest_keys}, @_)};
+    bless(
+        FCM::System::Make::Build::FileType->new({
+            id                    => 'script',
+            file_she              => q{}, # Value not used, for file type match
+            file_ext              => q{},
+            source_analyse_dep_of => {%SOURCE_ANALYSE_DEP_OF},
+            source_to_targets     => \&_source_to_targets,
+            task_class_of         => {%TASK_CLASS_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# Returns a list of targets for a given build source.
+sub _source_to_targets {
+    my ($attrib_ref, $source, $prop_hash_ref) = @_;
+    my $key = basename($source->get_path());
+    $TARGET->new(
+        {   category      => $TARGET->CT_BIN,
+            deps          => [@{$source->get_deps()}],
+            dep_policy_of => {'bin', $TARGET->POLICY_CAPTURE},
+            key           => $key,
+            task          => 'install',
+        }
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::FileType::Script
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::FileType::Script;
+    my $helper = FCM::System::Make::Build::FileType::Script->new();
+    $helper->source_analyse($handle);
+
+=head1 DESCRIPTION
+
+A wrapper of
+L<FCM::System::Make::Build::FileType|FCM::System::Make::Build::FileType>
+with configurations to work with some UKMO script files.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Archive.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Archive.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Archive.pm	(revision 5095)
@@ -0,0 +1,133 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Archive;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+use FCM::System::Exception;
+use File::Spec::Functions qw{abs2rel catfile};
+use List::Util qw{first};
+use Text::ParseWords qw{shellwords};
+
+our %PROP_OF = (ar => 'ar', 'ar.flags' => 'rs');
+my $E = 'FCM::System::Exception';
+
+__PACKAGE__->class(
+    {prop_of => {isa => '%', default => {%PROP_OF}}, util => '&'},
+    {action_of => {main => \&_main, prop_of => sub {\%PROP_OF}}},
+);
+
+sub _main {
+    my ($attrib_ref, $target) = @_;
+    # Selects the correct dependent objects
+    my @paths = @{$target->get_info_of('paths')};
+    my %dep_keys_of = %{$target->get_info_of('deps')};
+    my @paths_of_o = ();
+    my $abs2rel_func
+        = sub {index($_[0], $paths[0]) == 0 ? abs2rel($_[0], $paths[0]) : $_[0]};
+    while (my ($type, $key_list_ref) = each(%dep_keys_of)) {
+        for my $key (@{$key_list_ref}) {
+            my $path = first {-e} map {catfile($_, 'o', $key)} @paths;
+            if ($path) {
+                push(@paths_of_o, $abs2rel_func->($path));
+            }
+        }
+    }
+    my @command_list = (
+        (map {shellwords($target->get_prop_of($_))} qw{ar ar.flags}),
+        $target->get_path(),
+        @paths_of_o,
+    );
+    my %value_of = %{$attrib_ref->{util}->shell_simple(\@command_list)};
+    if ($value_of{rc}) {
+        return $E->throw(
+            $E->SHELL, {command_list => \@command_list, %value_of}, $value_of{e},
+        );
+    }
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->MAKE_BUILD_SHELL_OUT, @value_of{qw{o e}},
+    );
+    $target;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Archive
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Archive;
+    my $build_task = FCM::System::Make::Build::Task::Archive->new(\%attrib);
+    $build_task->main($target);
+
+=head1 DESCRIPTION
+
+Invokes the archive to create the target archive library.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance. %attrib should contain:
+
+=over 4
+
+=item {prop_of}
+
+A HASH that maps the property names (used by this task) to their default values.
+
+=item {util}
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $instance->main($target)
+
+Invokes the "ar" command to create the $target object archive. It uses the
+$target->get_info_of('deps')->{o} ARRAY. All "o" dependency items are placed in
+the archive.
+
+=item $instance->prop_of()
+
+Returns the HASH that maps the property names (used by this task) to their
+default values.
+
+=back
+
+=head1 CONSTANTS
+
+=item %FCM::System::Make::Build::Task::Archive::PROP_OF
+
+A map containing the property names and their default values.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile.pm	(revision 5095)
@@ -0,0 +1,143 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+use FCM::System::Exception;
+my $E = 'FCM::System::Exception';
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Compile;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+use FCM::System::Make::Build::Task::Share qw{_props_to_opts};
+use File::Spec::Functions qw{abs2rel catfile};
+use Text::ParseWords qw{shellwords};
+
+__PACKAGE__->class(
+    {name => '$', prop_of => '&', util => '&'},
+    {action_of => {main => \&_main, prop_of => \&_prop_of}},
+);
+
+sub _main {
+    my ($attrib_ref, $target) = @_;
+    my $NAME  = $attrib_ref->{name};
+    my $P     = sub {scalar($target->get_prop_of($_[0]))};
+    my @paths = @{$target->get_info_of('paths')};
+    my $abs2rel_func
+        = sub {index($_[0], $paths[0]) == 0 ? abs2rel($_[0], $paths[0]) : $_[0]};
+    my @include_paths
+        = map {catfile(($_ eq $paths[0] ? q{.} : $_), 'include')} @paths;
+    my %opt_of = (
+        c   => $P->($NAME . '.flag-compile'),
+        D   => $P->($NAME . '.flag-define'),
+        I   => $P->($NAME . '.flag-include'),
+        M   => $P->($NAME . '.flag-module'),    # FIXME
+        o   => $P->($NAME . '.flag-output'),
+    );
+    my @command_list = (
+        shellwords($P->($NAME)),
+        _props_to_opts($opt_of{o}, $abs2rel_func->($target->get_path())),
+        $opt_of{c},
+        _props_to_opts($opt_of{D}, shellwords($P->($NAME .  '.defs'))),
+        _props_to_opts($opt_of{I}, @include_paths),
+        _props_to_opts($opt_of{I}, shellwords($P->($NAME .  '.include-paths'))),
+        _props_to_opts($opt_of{M}, @include_paths),
+        shellwords($P->($NAME . '.flag-omp')),
+        shellwords($P->($NAME . '.flags')),
+        $target->get_path_of_source(),
+    );
+    my %value_of = %{$attrib_ref->{util}->shell_simple(\@command_list)};
+    if ($value_of{rc}) {
+        return $E->throw(
+            $E->SHELL, {command_list => \@command_list, %value_of}, $value_of{e},
+        );
+    }
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->MAKE_BUILD_SHELL_OUT, @value_of{qw{o e}},
+    );
+    $target;
+}
+
+sub _prop_of {
+    my ($attrib_ref) = @_;
+    $attrib_ref->{prop_of}->(@_);
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Compile
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Compile;
+    my $build_task = FCM::System::Make::Build::Task::Compile->new(\%attrib);
+    $build_task->main($target);
+
+=head1 DESCRIPTION
+
+Invokes the compiler command on the source of a target to generate the path of
+the target.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance. %attrib should contain:
+
+=over 4
+
+=item {name}
+
+The property name of the compiler command.
+
+=item {prop_of}
+
+A CODE to implement the $instance->prop_of($target) method.
+
+=item {util}
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $instance->main($target)
+
+Invokes the compiler command in a shell to compile the source path of the
+$target into an object file in the path of the $target.
+
+=item $instance->prop_of($target)
+
+Returns the HASH that maps the property names (used by this task) to their
+default values.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/C.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/C.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/C.pm	(revision 5095)
@@ -0,0 +1,70 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Compile::C;
+use base qw{FCM::System::Make::Build::Task::Compile};
+
+our %PROP_OF = (
+    'cc'               => 'gcc',
+    'cc.defs'          => '',
+    'cc.flags'         => '',
+    'cc.flag-compile'  => '-c',
+    'cc.flag-define'   => '-D%s',
+    'cc.flag-include'  => '-I%s',
+    'cc.flag-omp'      => '',
+    'cc.flag-output'   => '-o%s',
+    'cc.include-paths' => '',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'cc', prop_of => sub {return {%PROP_OF}}, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Compile::C
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Compile::C;
+    my $task = FCM::System::Make::Build::Task::Compile::C->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Compile> to compile a C source into an
+object.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/CXX.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/CXX.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/CXX.pm	(revision 5095)
@@ -0,0 +1,70 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Compile::CXX;
+use base qw{FCM::System::Make::Build::Task::Compile};
+
+our %PROP_OF = (
+    'cxx'               => 'g++',
+    'cxx.defs'          => '',
+    'cxx.flags'         => '',
+    'cxx.flag-compile'  => '-c',
+    'cxx.flag-define'   => '-D%s',
+    'cxx.flag-include'  => '-I%s',
+    'cxx.flag-omp'      => '',
+    'cxx.flag-output'   => '-o%s',
+    'cxx.include-paths' => '',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'cxx', prop_of => sub {return {%PROP_OF}}, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Compile::CXX
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Compile::CXX;
+    my $task = FCM::System::Make::Build::Task::Compile::CXX->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Compile> to compile a C++ source into an
+object.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/Fortran.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/Fortran.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Compile/Fortran.pm	(revision 5095)
@@ -0,0 +1,114 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Compile::Fortran;
+use base qw{FCM::System::Make::Build::Task::Compile};
+
+our %PROP_NO_PREPROCESS_OF = (
+    'fc'               => 'gfortran',
+    'fc.flags'         => '',
+    'fc.flag-compile'  => '-c',
+    'fc.flag-include'  => '-I%s',
+    'fc.flag-module'   => '',
+    'fc.flag-omp'      => '',
+    'fc.flag-output'   => '-o%s',
+    'fc.include-paths' => '',
+);
+
+our %PROP_OF = (
+    %PROP_NO_PREPROCESS_OF,
+    'fc.defs'          => '',
+    'fc.flag-define'   => '-D%s',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'fc', prop_of => \&_prop_of, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+sub _prop_of {
+    my ($attrib_ref, $target) = @_;
+    if (!defined($target)) {
+        return {%PROP_OF};
+    }
+    my $file_ext = $attrib_ref->{util}->file_ext($target->get_path_of_source());
+    (lc($file_ext) eq $file_ext) ? {%PROP_NO_PREPROCESS_OF} : {%PROP_OF};
+}
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Compile::Fortran::Extra;
+use base qw{FCM::Class::CODE};
+
+use FCM::System::Exception;
+use File::Basename qw{basename};
+
+my $E = 'FCM::System::Exception';
+
+our %PROP_OF = %FCM::System::Make::Build::Task::Compile::Fortran::PROP_OF;
+
+__PACKAGE__->class(
+    {prop_of => {isa => '%', default => {%PROP_OF}}},
+    {action_of => {main => \&_main, prop_of => sub {\%PROP_OF}}},
+);
+
+sub _main {
+    my ($attrib_ref, $target) = @_;
+    my ($source, $dest) = (basename($target->get_key()), $target->get_path());
+    if (!-e $source) {
+        return;
+    }
+    rename($source, $dest) || return $E->throw($E->COPY, [$source, $dest], $!);
+    $target;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Compile::Fortran
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Compile::Fortran;
+    my $task = FCM::System::Make::Build::Task::Compile::Fortran->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Compile> to compile a Fortran source into
+an object.
+
+The module also provides the
+FCM::System::Make::Build::Task::Compile::Fortran::Extra class to deal with the
+module definition file generated by the compiler in the working directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/ExtractInterface.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/ExtractInterface.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/ExtractInterface.pm	(revision 5095)
@@ -0,0 +1,536 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::ExtractInterface;
+use base qw{FCM::Class::CODE};
+
+use FCM::System::Exception;
+use Text::Balanced qw{extract_bracketed extract_delimited};
+use Text::ParseWords qw{shellwords};
+
+# Alias
+my $E = 'FCM::System::Exception';
+
+# Regular expressions
+my $RE_ATTR = qr{
+    allocatable|dimension|external|intent|optional|parameter|pointer|save|target
+}imsx;
+my $RE_FILE = qr{[\w\-+.]+}imsx;
+my $RE_NAME = qr{[A-Za-z]\w*}imsx;
+my $RE_SPEC = qr{
+    character|class|complex|double\s*complex|double\s*precision|integer|
+    logical|procedure|real|type
+}imsx;
+my $RE_UNIT_BASE = qr{block\s*data|module|program|submodule}imsx;
+my $RE_UNIT_CALL = qr{function|subroutine}imsx;
+my $RE_UNIT      = qr{$RE_UNIT_BASE|$RE_UNIT_CALL}msx;
+my %RE           = (
+    COMMENT     => qr{\A\s*(?:!|\z)}msx,
+    COMMENT_END => qr{\A([^'"]*?)\s*!.*\z}msx,
+    CONT        => qr{\A(.*)&\s*\z}msx,
+    CONT_LEAD   => qr{\A(\s*&)(.*)\z}msx,
+    INCLUDE     => qr{(?:\#|\s*)include\s*}imsx,
+    NAME_COMP   => qr{\b($RE_NAME)(?:\s*\%\s*$RE_NAME)*\b}msx,
+    NAME_LEAD   => qr{\A\s*$RE_NAME\s*}msx,
+    NAME_LIST   => qr{\A(?:.*?)\s*,\s*($RE_NAME)\b(.*)\z}msx,
+    QUOTE       => qr{\A[^'"]*(['"])}msx,
+    TYPE_ATTR   => qr{\A\s*($RE_ATTR)\b}msx,
+    TYPE_SPEC   => qr{\A\s*($RE_SPEC)\b}msx,
+    UNIT_ATTR   => qr{\A\s*(?:(?:(?:impure\s+)?elemental|recursive|pure)\s+)+(.*)\z}imsx,
+    UNIT_BASE   => qr{\A\s*($RE_UNIT_BASE)\s+($RE_NAME)\s*\z}imsx,
+    UNIT_CALL   => qr{\A\s*($RE_UNIT_CALL)\s+($RE_NAME)\b}imsx,
+    UNIT_END  => qr{\A\s*(end)(?:\s+($RE_NAME)(?:\s+($RE_NAME))?)?\s*\z}imsx,
+    UNIT_SPEC => qr{\A\s*$RE_SPEC\b(.*)\z}imsx,
+);
+
+# Keywords in type declaration statements
+my %TYPE_DECL_KEYWORD_SET = map { ($_, 1) } qw{
+    allocatable
+    asynchronous
+    contiguous
+    dimension
+    external
+    in
+    inout
+    intent
+    kind
+    len
+    optional
+    out
+    parameter
+    pointer
+    save
+    target
+    value
+    volatile
+};
+
+__PACKAGE__->class({util => '&'}, {action_of => {main => \&_main}});
+
+sub _main {
+    my ($attrib_ref, $target) = @_;
+    my $handle
+        = $attrib_ref->{util}->file_load_handle($target->get_path_of_source());
+    eval {
+        $attrib_ref->{util}->file_save(
+            $target->get_path(),
+            [   map { s{\s+}{ }gmsx; s{\s+\z}{\n}msx; $_ }
+                    map { @{$_->{lines}} }
+                    @{_reduce_to_interface(_extract_statements($handle))}
+            ],
+        );
+    };
+    if ($@) {
+        my $e = $@;
+        if ($E->caught($e) && $e->get_code() eq $E->BUILD_SOURCE_SYN) {
+            unshift(@{$e->get_ctx()}, $target->get_path_of_source());
+        }
+        die($e);
+    }
+    close($handle);
+    $target;
+}
+
+# Reads $handle for the next Fortran statement, handling continuations.
+sub _extract_statements {
+    my ($handle) = @_;
+    my $context = {signature_token_set_of => {}, statements => []};
+    my $state = {
+        in_contains  => undef,
+        in_interface => undef,
+        in_quote     => undef,
+        in_type      => undef,
+        stack        => [],
+    };
+    my $NEW_STATEMENT = sub {
+        {   name        => q{},
+            lines       => [],
+            line_number => 0,
+            symbol      => q{},
+            type        => q{},
+            value       => q{},
+        };
+    };
+    my $statement;
+LINE:
+    while (my $line = readline($handle)) {
+        if (!defined($statement)) {
+            $statement = $NEW_STATEMENT->();
+        }
+        my $value = $line;
+        chomp($value);
+        if (!$statement->{line_number} && index($value, '#') == 0) {
+            $statement->{line_number} = $.;
+            $statement->{name}        = 'cpp';
+        }
+        if ($statement->{name} eq 'cpp') {
+            push(@{$statement->{lines}}, $line);
+            $statement->{value} .= $value;
+            if (rindex($value, '\\') != length($value) - 1) {
+                #push(@{$context->{statements}}, $statement);
+                $statement = undef;
+            }
+            next LINE;
+        }
+        if ($value =~ $RE{COMMENT}) {
+            next LINE;
+        }
+        if (!$statement->{line_number}) {
+            $statement->{line_number} = $.;
+        }
+        my ($cont_head, $cont_tail);
+        if ($statement->{line_number} != $.) {    # is a continuation
+            ($cont_head, $cont_tail) = $value =~ $RE{CONT_LEAD};
+            if ($cont_head) {
+                $value = $cont_tail;
+            }
+        }
+        my ($head, $tail) = (q{}, $value);
+        if ($state->{in_quote} && index($value, $state->{in_quote}) >= 0) {
+            my $index = index($value, $state->{in_quote});
+            $head = substr($value, 0, $index + 1);
+            $tail
+                = length($value) > $index + 1
+                ? substr($value, $index + 2)
+                : q{};
+            $state->{in_quote} = undef;
+        }
+        if (!$state->{in_quote}) {
+            while ($tail) {
+                if (index($tail, q{!}) >= 0) {
+                    if (!($tail =~ s/$RE{COMMENT_END}/$1/)) {
+                        ($head, $tail, $state->{in_quote})
+                            = _extract_statement_quote($head, $tail);
+                    }
+                }
+                else {
+                    while (index($tail, q{'}) > 0
+                        || index($tail, q{"}) > 0)
+                    {
+                        ($head, $tail, $state->{in_quote})
+                            = _extract_statement_quote($head, $tail);
+                    }
+                    $head .= $tail;
+                    $tail = q{};
+                }
+            }
+        }
+        $cont_head ||= q{};
+        push(@{$statement->{lines}}, $cont_head . $head . $tail . "\n");
+        $statement->{value} .= $head . $tail;
+        if (!($statement->{value} =~ s/$RE{CONT}/$1/)) {
+            $statement->{value} =~ s{\s+\z}{}msx;
+            if (_process($statement, $context, $state)) {
+                push(@{$context->{statements}}, $statement);
+            }
+            $statement = undef;
+        }
+    }
+    return $context;
+}
+
+# Helper, removes a quoted string from $tail.
+sub _extract_statement_quote {
+    my ($head, $tail) = @_;
+    my ($extracted, $remainder, $prefix)
+        = extract_delimited($tail, q{'"}, qr{[^'"]*}msx, q{});
+    if ($extracted) {
+        return ($head . $prefix . $extracted, $remainder);
+    }
+    else {
+        my ($quote) = $tail =~ $RE{QUOTE};
+        return ($head . $tail, q{}, $quote);
+    }
+}
+
+# Read a statement and put attributes into $statement
+sub _process {
+    my ($statement, $context, $state) = @_;
+    my $name;
+
+    # End Interface
+    if ($state->{in_interface}) {
+        if ($statement->{value} =~ qr{\A\s*end\s*interface\b}imsx) {
+            $state->{in_interface} = 0;
+        }
+        return;
+    }
+
+    # End Program Unit
+    if (@{$state->{stack}} && $statement->{value} =~ qr{\A\s*end\b}imsx) {
+        my ($end, $type, $symbol) = lc($statement->{value}) =~ $RE{UNIT_END};
+        if (!$end) {
+            return;
+        }
+        my ($top_type, $top_symbol) = @{$state->{stack}->[-1]};
+        if (!$type
+            || $top_type eq $type && (!$symbol || $top_symbol eq $symbol))
+        {
+            pop(@{$state->{stack}});
+            if ($state->{in_contains} && !@{$state->{stack}}) {
+                $state->{in_contains} = 0;
+            }
+            if (!$state->{in_contains}) {
+                $statement->{name}   = $top_type;
+                $statement->{symbol} = $top_symbol;
+                $statement->{type}   = 'end';
+                return $statement;
+            }
+        }
+        return;
+    }
+
+    # Interface/Contains
+    if ($statement->{value} =~ qr{\A\s*contains\b}imsx) {
+        $state->{'in_contains'} = 1;
+        return;
+    }
+    if ($statement->{value} =~ qr{\A\s*(?:abstract\s+)?interface\b}imsx) {
+        $state->{'in_interface'} = 1;
+        return;
+    }
+
+    # Program Unit
+    my ($type, $symbol, @tokens) = _process_prog_unit($statement->{value});
+    if ($type) {
+        push(@{$state->{stack}}, [$type, $symbol]);
+        if ($state->{in_contains}) {
+            return;
+        }
+        $statement->{name}   = lc($type);
+        $statement->{type}   = 'signature';
+        $statement->{symbol} = lc($symbol);
+        $context->{signature_token_set_of}{$symbol}
+            = {map { (lc($_) => 1) } @tokens};
+        return $statement;
+    }
+    if ($state->{in_contains}) {
+        return;
+    }
+
+    # Use
+    if ($statement->{value} =~ qr{\A\s*(use)\b}imsx) {
+        $statement->{name} = 'use';
+        $statement->{type} = 'use';
+        return $statement;
+    }
+
+    # Type Declarations
+    ($name) = $statement->{value} =~ $RE{TYPE_SPEC};
+    if ($name) {
+        $name =~ s{\s}{}gmsx;
+        $statement->{name} = lc($name);
+        $statement->{type} = 'type';
+        return $statement;
+    }
+
+    # Attribute Statements
+    ($name) = $statement->{value} =~ $RE{TYPE_ATTR};
+    if ($name) {
+        $statement->{name} = lc($name);
+        $statement->{type} = 'attr';
+    }
+}
+
+# Parse a statement for program unit header. Returns a list containing the type,
+# the symbol and the signature tokens of the program unit.
+sub _process_prog_unit {
+    my ($string) = @_;
+    my ($type, $symbol, @args) = (q{}, q{});
+    ($type, $symbol) = $string =~ $RE{UNIT_BASE};
+    if ($type) {
+        $type = lc($type);
+        $type =~ s{\s*}{}gmsx;
+        return ($type, $symbol);
+    }
+    $string =~ s/$RE{UNIT_ATTR}/$1/;
+    my ($match) = $string =~ $RE{UNIT_SPEC};
+    if ($match) {
+        $string = $match;
+        extract_bracketed($string);
+    }
+    ($type, $symbol) = lc($string) =~ $RE{UNIT_CALL};
+    if (!$type) {
+        return;
+    }
+    my $extracted = extract_bracketed($string, q{()}, qr{[^(]*}msx);
+
+    # Get arguments/keywords from SUBROUTINE/FUNCTION
+    if ($extracted) {
+        $extracted =~ s{\s}{}gmsx;
+        @args = split(q{,}, substr($extracted, 1, length($extracted) - 2));
+        if ($type eq 'function') {
+            my $result = extract_bracketed($string, q{()}, qr{[^(]*}msx);
+            if ($result) {
+                $result =~ s{\A\(\s*(.*?)\s*\)\z}{$1}msx;    # remove braces
+                push(@args, $result);
+            }
+            else {
+                push(@args, $symbol);
+            }
+        }
+    }
+    return (lc($type), lc($symbol), map { lc($_) } @args);
+}
+
+# Reduces the list of statements to contain only the interface block.
+sub _reduce_to_interface {
+    my ($context) = @_;
+    my (%token_set, @interface_statements);
+STATEMENT:
+    for my $statement (reverse(@{$context->{statements}})) {
+        if ($statement->{type} eq 'end'
+            && grep { $_ eq $statement->{name} } qw{subroutine function})
+        {
+            push(@interface_statements, $statement);
+            %token_set
+                = %{$context->{signature_token_set_of}{$statement->{symbol}}};
+            next STATEMENT;
+        }
+        if ($statement->{type} eq 'signature'
+            && grep { $_ eq $statement->{name} } qw{subroutine function})
+        {
+            push(@interface_statements, $statement);
+            %token_set = ();
+            next STATEMENT;
+        }
+        if ($statement->{type} eq 'use') {
+            my ($head, $tail)
+                = split(qr{,\s*only\s*:\s*}msx, lc($statement->{value}), 2);
+            if ($tail) {
+                my @imports = map { [split(qr{\s*=>\s*}msx, $_, 2)] }
+                    split(qr{\s*,\s*}msx, $tail);
+                my @useful_imports
+                    = grep { exists($token_set{$_->[0]}) } @imports;
+                if (!@useful_imports) {
+                    next STATEMENT;
+                }
+                if (@imports != @useful_imports) {
+                    my @token_strings
+                        = map { $_->[0] . ($_->[1] ? ' => ' . $_->[1] : q{}) }
+                        @useful_imports;
+                    my ($last, @rest) = reverse(@token_strings);
+                    my @token_lines
+                        = (reverse(map { $_ . q{,&} } @rest), $last);
+                    push(
+                        @interface_statements,
+                        {   lines => [
+                                sprintf("%s, only:&\n", $head),
+                                (map { sprintf(" & %s\n", $_) } @token_lines),
+                            ]
+                        },
+                    );
+                    next STATEMENT;
+                }
+            }
+            push(@interface_statements, $statement);
+            next STATEMENT;
+        }
+        if ($statement->{type} eq 'attr') {
+            my ($spec, @tokens) = ($statement->{value} =~ /$RE{NAME_COMP}/g);
+            if (grep { exists($token_set{lc($_)}) } @tokens) {
+                for my $token (@tokens) {
+                    $token_set{$token} = 1;
+                }
+                push(@interface_statements, $statement);
+                next STATEMENT;
+            }
+        }
+        if ($statement->{type} eq 'type') {
+            my ($variable_string, $spec_string)
+                = reverse(split('::', lc($statement->{value}), 2));
+            if ($spec_string) {
+                $spec_string =~ s{$RE{NAME_LEAD}}{}msx;
+            }
+            else {
+                $variable_string =~ s{$RE{NAME_LEAD}}{}msx;
+                $spec_string = extract_bracketed($variable_string, '()',
+                    qr{[\s\*]*}msx);
+            }
+            my $tail = q{,} . lc($variable_string);
+            my @tokens;
+            while ($tail) {
+                if ($tail =~ qr{\A\s*['"]}msx) {
+                    my $old_tail = $tail;
+                    extract_delimited($tail, q{'"}, qr{\A[^'"]*}msx, q{});
+                    if ($old_tail eq $tail) {
+                        return $E->throw(
+                            $E->BUILD_SOURCE_SYN, [$statement->{line_number}]);
+                    }
+                }
+                elsif ($tail =~ qr{\A\s*\(}msx) {
+                    my $old_tail = $tail;
+                    extract_bracketed($tail, '()', qr{\A[^(]*}msx);
+                    if ($old_tail eq $tail) {
+                        return $E->throw(
+                            $E->BUILD_SOURCE_SYN, [$statement->{line_number}]);
+                    }
+                }
+                else {
+                    my $token;
+                    ($token, $tail) = $tail =~ $RE{NAME_LIST};
+                    if ($token && $token_set{$token}) {
+                        @tokens = ($variable_string =~ /$RE{NAME_COMP}/g);
+                        $tail = q{};
+                    }
+                }
+            }
+            if (@tokens && $spec_string) {
+                my @spec_tokens = (lc($spec_string) =~ /$RE{NAME_COMP}/g);
+                push(
+                    @tokens,
+                    (   grep { !exists($TYPE_DECL_KEYWORD_SET{$_}) }
+                            @spec_tokens
+                    ),
+                );
+            }
+            if (grep { exists($token_set{$_}) } @tokens) {
+                for my $token (@tokens) {
+                    $token_set{$token} = 1;
+                }
+                push(@interface_statements, $statement);
+                next STATEMENT;
+            }
+        }
+    }
+    if (!@interface_statements) {
+        return [];
+    }
+    [   {lines => ["interface\n"]},
+        reverse(@interface_statements),
+        {lines => ["end interface\n"]},
+    ];
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::ExtractInterface
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::ExtractInterface;
+    my $task = FCM::System::Make::Build::Task::ExtractInterface->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Extracts the calling interfaces of top level functions and subroutines in the
+Fortran source file of the target.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance. %attrib should contain:
+
+=over 4
+
+=item {util}
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $instance->main($target)
+
+Extracts the calling interfaces of top level functions and subroutines in the
+Fortran source file of the target, and writes the results to the path of the
+target.
+
+=back
+
+=head1 ACKNOWLEDGEMENT
+
+This module is inspired by the logic developed by the European Centre
+for Medium-Range Weather Forecasts (ECMWF).
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Install.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Install.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Install.pm	(revision 5095)
@@ -0,0 +1,90 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Install;
+use base qw{FCM::Class::CODE};
+
+use FCM::System::Exception;
+use File::Copy qw{copy};
+
+my $E = 'FCM::System::Exception';
+
+__PACKAGE__->class({util => '&'}, {action_of => {main => \&_main}});
+
+sub _main {
+    my ($attrib_ref, $target) = @_;
+    my ($source, $dest) = ($target->get_path_of_source(), $target->get_path());
+    if ($source) {
+        copy($source, $dest) || return $E->throw($E->COPY, [$source, $dest], $!);
+        chmod((stat($source))[2] & oct(7777), $dest)
+            || return $E->throw($E->DEST_CREATE, $dest, $!);
+    }
+    else {
+        $attrib_ref->{util}->file_save($dest, q{});
+    }
+    $target;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Install
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Install;
+    my $build_task = FCM::System::Make::Build::Task::Install->new(\%attrib);
+    $build_task->main( $target);
+
+=head1 DESCRIPTION
+
+Copies the source of the target to its path.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance. %attrib should contain:
+
+=over 4
+
+=item {util}
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $instance->main($target)
+
+Copies the source of the target to its path.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link.pm	(revision 5095)
@@ -0,0 +1,202 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Link;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+use FCM::System::Exception;
+use FCM::System::Make::Build::Task::Archive;
+use FCM::System::Make::Build::Task::Share qw{_props_to_opts};
+use File::Basename qw{basename};
+use File::Path qw{mkpath rmtree};
+use File::Spec::Functions qw{abs2rel catfile};
+use File::Temp qw{tempdir};
+use List::Util qw{first};
+use Text::ParseWords qw{shellwords};
+
+my $E = 'FCM::System::Exception';
+
+our %PROP_OF = (
+    %FCM::System::Make::Build::Task::Archive::PROP_OF,
+    'ld' => '',
+    'link-without-ar' => '',
+    'keep-lib-o' => '',
+);
+
+__PACKAGE__->class(
+    {name => '$', prop_of => '%', util => '&'},
+    {action_of => {main => \&_main, prop_of => sub {$_[0]->{prop_of}}}},
+);
+
+sub _main {
+    my ($attrib_ref, $target) = @_;
+    my $NAME  = $attrib_ref->{name};
+    my $P     = sub {$target->get_prop_of($_[0])};
+    # Selects the correct dependent objects
+    my @paths = @{$target->get_info_of('paths')};
+    my %dep_keys_of = %{$target->get_info_of('deps')};
+    my %paths_of = (o => [], 'o.special' => []);
+    my $abs2rel_func
+        = sub {index($_[0], $paths[0]) == 0 ? abs2rel($_[0], $paths[0]) : $_[0]};
+    while (my ($type, $key_list_ref) = each(%dep_keys_of)) {
+        for my $key (@{$key_list_ref}) {
+            my $path = first {-e} map {catfile($_, 'o', $key)} @paths;
+            if ($path) {
+                push(@{$paths_of{$type}}, $abs2rel_func->($path));
+            }
+        }
+    }
+    my $path_of_main_o = shift(@{$paths_of{o}});
+    my @command_list_refs;
+    my %opt_of = (
+        o => $P->($NAME . '.flag-output'),
+        L => $P->($NAME . '.flag-lib-path'),
+        l => $P->($NAME . '.flag-lib'),
+    );
+    # Create an intermediate archive library
+    # (when linking multiple objects and "link-without-ar" is not set)
+    my $link_with_ar = @{$paths_of{o}} && !$P->('link-without-ar');
+    my ($keep_lib_o, $lib_o_dir, $lib_o, @o_args);
+    if ($link_with_ar) {
+        # Archive (when linking multiple objects)
+        $keep_lib_o = $P->('keep-lib-o');
+        if ($keep_lib_o) {
+            $lib_o_dir = $target->CT_LIB;
+            mkpath($lib_o_dir);
+        }
+        else {
+            $lib_o_dir = tempdir(CLEANUP => 1);
+        }
+        my $root = (
+            $attrib_ref->{util}->file_ext(basename($target->get_key())))[1];
+        $lib_o = catfile($lib_o_dir, "lib$root.a");
+        push(@command_list_refs, [
+            shellwords($P->('ar')),
+            shellwords($P->('ar.flags')),
+            $lib_o,
+            @{$paths_of{o}},
+        ]);
+        @o_args = (
+            _props_to_opts($opt_of{L}, $lib_o_dir),
+            _props_to_opts($opt_of{l}, $root),
+        );
+    }
+    else {
+        @o_args = @{$paths_of{o}};
+    }
+    # Link
+    push(@command_list_refs, [
+        ($P->('ld') ? shellwords($P->('ld')) : shellwords($P->($NAME))),
+        _props_to_opts($opt_of{o}, $abs2rel_func->($target->get_path())),
+        $path_of_main_o,
+        @{$paths_of{'o.special'}},
+        @o_args,
+        _props_to_opts($opt_of{L}, shellwords($P->($NAME .  '.lib-paths'))),
+        _props_to_opts($opt_of{l}, shellwords($P->($NAME .  '.libs'))),
+        shellwords($P->($NAME . '.flag-omp')),
+        shellwords($P->($NAME . '.flags-ld')),
+    ]);
+    for my $command_list_ref (@command_list_refs) {
+        my %value_of = %{$attrib_ref->{util}->shell_simple($command_list_ref)};
+        if ($value_of{rc}) {
+            return $E->throw(
+                $E->SHELL,
+                {command_list => $command_list_ref, %value_of},
+                $value_of{e},
+            );
+        }
+        $attrib_ref->{util}->event(
+            FCM::Context::Event->MAKE_BUILD_SHELL_OUT, @value_of{qw{o e}},
+        );
+    }
+    if ($link_with_ar && !$keep_lib_o) {
+        unlink($lib_o);
+        rmtree($lib_o_dir);
+    }
+    $target;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Link
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Link;
+    my $build_task = FCM::System::Make::Build::Task::Link->new(\%attrib);
+    $build_task->main($target);
+
+=head1 DESCRIPTION
+
+Invokes the linker to create the target executable.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance. %attrib should contain:
+
+=over 4
+
+=item {name}
+
+The property name of the linker.
+
+=item {prop_of}
+
+A HASH to map the property names (used by this task) to their default values.
+
+=item {util}
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $instance->main($target)
+
+Invokes the linker to create the $target executable. It uses the
+$target->get_info_of('deps')->{o} ARRAY and
+$target->get_info_of('deps')->{"o.special"} ARRAY as dependencies. The first
+type "o" dependency item is expected to be the object file containing the main
+program. All other "o" dependency items are placed in a temporary archive
+before invoking the linker command. The main object and "o.special" dependency
+items are entered into the command line of the linker to produce the
+executable.
+
+=item $instance->prop_of()
+
+Returns the HASH that maps the property names (used by this task) to their
+default values.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/C.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/C.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/C.pm	(revision 5095)
@@ -0,0 +1,72 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Link::C;
+use base qw{FCM::System::Make::Build::Task::Link};
+
+use FCM::System::Make::Build::Task::Compile::C;
+
+our %PROP_OF = (
+    %FCM::System::Make::Build::Task::Link::PROP_OF,
+    (   map {$_ => $FCM::System::Make::Build::Task::Compile::C::PROP_OF{$_}}
+        qw{cc cc.flag-omp cc.flag-output}
+    ),
+    'cc.flags-ld'      => '',
+    'cc.flag-lib'      => '-l%s',
+    'cc.flag-lib-path' => '-L%s',
+    'cc.libs'          => '',
+    'cc.lib-paths'     => '',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'cc', prop_of => {%PROP_OF}, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Link::C
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Link::C;
+    my $task = FCM::System::Make::Build::Task::Link::C->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Link> to link a C object into an
+executable.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/CXX.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/CXX.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/CXX.pm	(revision 5095)
@@ -0,0 +1,72 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Link::CXX;
+use base qw{FCM::System::Make::Build::Task::Link};
+
+use FCM::System::Make::Build::Task::Compile::CXX;
+
+our %PROP_OF = (
+    %FCM::System::Make::Build::Task::Link::PROP_OF,
+    (   map {$_ => $FCM::System::Make::Build::Task::Compile::CXX::PROP_OF{$_}}
+        qw{cxx cxx.flag-omp cxx.flag-output}
+    ),
+    'cxx.flags-ld'      => '',
+    'cxx.flag-lib'      => '-l%s',
+    'cxx.flag-lib-path' => '-L%s',
+    'cxx.libs'          => '',
+    'cxx.lib-paths'     => '',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'cxx', prop_of => {%PROP_OF}, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Link::CXX
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Link::CXX;
+    my $task = FCM::System::Make::Build::Task::Link::CXX->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Link> to link a C++ object into an
+executable.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/Fortran.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/Fortran.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Link/Fortran.pm	(revision 5095)
@@ -0,0 +1,72 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Link::Fortran;
+use base qw{FCM::System::Make::Build::Task::Link};
+
+use FCM::System::Make::Build::Task::Compile::Fortran;
+
+our %PROP_OF = (
+    %FCM::System::Make::Build::Task::Link::PROP_OF,
+    (   map {$_ => $FCM::System::Make::Build::Task::Compile::Fortran::PROP_OF{$_}}
+        qw{fc fc.flag-omp fc.flag-output}
+    ),
+    'fc.flags-ld'      => '',
+    'fc.flag-lib'      => '-l%s',
+    'fc.flag-lib-path' => '-L%s',
+    'fc.libs'          => '',
+    'fc.lib-paths'     => '',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'fc', prop_of => {%PROP_OF}, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Link::Fortran
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Link::Fortran;
+    my $task = FCM::System::Make::Build::Task::Link::Fortran->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Link> to link a Fortran object into an
+executable.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess.pm	(revision 5095)
@@ -0,0 +1,131 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Preprocess;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+use FCM::System::Exception;
+use FCM::System::Make::Build::Task::Share qw{_props_to_opts};
+use File::Spec::Functions qw{catfile};
+use Text::ParseWords qw{shellwords};
+
+use FCM::System::Exception;
+
+my $E = 'FCM::System::Exception';
+
+__PACKAGE__->class(
+    {name => '$', prop_of => '%', util => '&'},
+    {action_of => {main => \&_main, prop_of => sub {$_[0]->{prop_of}}}},
+);
+
+sub _main {
+    my ($attrib_ref, $target) = @_;
+    my $NAME = $attrib_ref->{name};
+    my $P     = sub {$target->get_prop_of($_[0])};
+    my @paths = @{$target->get_info_of('paths')};
+    my @include_paths
+        = map {catfile(($_ eq $paths[0] ? q{.} : $_), 'include')} @paths;
+    my %opt_of = (
+        D => $P->($NAME . '.flag-define'),
+        I => $P->($NAME . '.flag-include'),
+    );
+    my @command = (
+        shellwords($P->($NAME)),
+        shellwords($P->($NAME . '.flags')),
+        _props_to_opts($opt_of{D}, shellwords($P->($NAME .  '.defs'))),
+        _props_to_opts($opt_of{I}, @include_paths),
+        _props_to_opts($opt_of{I}, shellwords($P->($NAME .  '.include-paths'))),
+        $target->get_path_of_source(),
+    );
+    my %value_of = %{$attrib_ref->{util}->shell_simple(\@command)};
+    if ($value_of{rc}) {
+        return $E->throw(
+            $E->SHELL, {command_list => \@command, %value_of}, $value_of{e},
+        );
+    }
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->MAKE_BUILD_SHELL_OUT, undef, $value_of{e},
+    );
+    $value_of{o} ||= q{};
+    $attrib_ref->{util}->file_save($target->get_path(), $value_of{o});
+    $target;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Preprocess
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Preprocess;
+    my $build_task = FCM::System::Make::Build::Task::Preprocess->new(\%attrib);
+    $build_task->main($target);
+
+=head1 DESCRIPTION
+
+Invokes the preprocessor on the target source to generate the target.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Creates and returns a new instance. %attrib should contain:
+
+=over 4
+
+=item {name}
+
+The property name of the preprocessor.
+
+=item {prop_of}
+
+A HASH to map the property names and their default values.
+
+=item {util}
+
+An instance of L<FCM::Util|FCM::Util>.
+
+=back
+
+=item $instance->main($target)
+
+Invokes the preprocessor shell command on the target source to generate the
+target.
+
+=item $instance->prop_of()
+
+Returns the HASH that maps the property names (used by this task) to their
+default values.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess/C.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess/C.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess/C.pm	(revision 5095)
@@ -0,0 +1,66 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Preprocess::C;
+use base qw{FCM::System::Make::Build::Task::Preprocess};
+
+our %PROP_OF = (
+    'cpp'               => 'cpp',
+    'cpp.defs'          => '',
+    'cpp.flags'         => '',
+    'cpp.flag-define'   => '-D%s',
+    'cpp.flag-include'  => '-I%s',
+    'cpp.include-paths' => '',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'cpp', prop_of => {%PROP_OF}, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Preprocess::C
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Preprocess::C;
+    my $task = FCM::System::Make::Build::Task::Preprocess::C->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Preprocess> to preprocess a C source.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess/Fortran.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess/Fortran.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Preprocess/Fortran.pm	(revision 5095)
@@ -0,0 +1,67 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Preprocess::Fortran;
+use base qw{FCM::System::Make::Build::Task::Preprocess};
+
+our %PROP_OF = (
+    'fpp'               => 'cpp',
+    'fpp.defs'          => '',
+    'fpp.flags'         => '-P -traditional',
+    'fpp.flag-define'   => '-D%s',
+    'fpp.flag-include'  => '-I%s',
+    'fpp.include-paths' => '',
+);
+
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        $class->SUPER::new(
+            {name => 'fpp', prop_of => {%PROP_OF}, %{$attrib_ref}},
+        ),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Preprocess::Fortran
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Preprocess::Fortran;
+    my $task = FCM::System::Make::Build::Task::Preprocess::Fortran->new(\%attrib);
+    $task->main($target);
+
+=head1 DESCRIPTION
+
+Wraps L<FCM::System::Make::Build::Task::Preprocess> to preprocess a Fortran
+source.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Share.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Share.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Build/Task/Share.pm	(revision 5095)
@@ -0,0 +1,94 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Build::Task::Share;
+use base qw{Exporter};
+
+use Text::ParseWords qw{shellwords};
+
+our @EXPORT = qw{_props_to_opts};
+
+sub _props_to_opts {
+    # $opt_value should be an sprintf format with one %s.
+    my ($opt_value, @props) = @_;
+    if (!$opt_value) {
+        return;
+    }
+    my @opt_values = shellwords($opt_value);
+    my $index = -1;
+    I:
+    for my $i (0 .. $#opt_values) {
+        if (index($opt_values[$i], '%s') >= 0) {
+            $index = $i;
+            last I;
+        }
+    }
+    if ($index == -1) {
+        return (@opt_values, @props);
+    }
+    my @return;
+    for my $prop (@props) {
+        push(@return, @opt_values[0 .. $index - 1]);
+        push(@return, sprintf($opt_values[$index], $prop));
+        push(@return, @opt_values[$index + 1 .. $#opt_values]);
+    }
+    return @return;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Build::Task::Share
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Build::Task::Share
+
+=head1 DESCRIPTION
+
+Provides common "local" functions for a make build task.
+
+=head1 FUNCTIONS
+
+The following functions are automatically exported by this module.
+
+=over 4
+
+=item _props_to_opts($opt_value, @props)
+
+Expect $opt_value to be an sprintf format containing one %s, and @props is a
+list of values. Return a list that can be used in a shell command. E.g.:
+
+    _props_to_opts('-D%s', 'HELLO="greetings"', 'WORLD="mars and venus"')
+    # => ('-DHELLO="greetings"', '-DWORLD="mars and venus"')
+    
+    _props_to_opts('-I %s', '/path/1', '/path/2')
+    # => ('-I', '/path/1', '-I', '/path/2')
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Extract.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Extract.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Extract.pm	(revision 5095)
@@ -0,0 +1,1312 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Extract;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::ConfigEntry;
+use FCM::Context::Event;
+use FCM::Context::Make::Extract;
+use FCM::Context::Locator;
+use FCM::Context::Task;
+use FCM::System::Exception;
+use FCM::System::Make::Share::Subsystem;
+use File::Basename qw{dirname};
+use File::Compare qw{compare};
+use File::Copy qw{copy};
+use File::Path qw{mkpath rmtree};
+use File::Spec::Functions qw{abs2rel catfile tmpdir};
+use File::Temp;
+use List::Util qw{first};
+use Storable qw{dclone};
+
+# Aliases
+our $UTIL;
+my $E = 'FCM::System::Exception';
+
+# Configuration parser map: label to action
+our %CONFIG_PARSER_OF = (
+    'location'  => \&_config_parse_location,
+    'ns'        => \&_config_parse_ns_list,
+    'path-excl' => _config_parse_path_func(
+        sub {$_->get_path_excl()}, sub {$_->set_path_excl(@_)}, '@',
+    ),
+    'path-incl' => _config_parse_path_func(
+        sub {$_->get_path_incl()}, sub {$_->set_path_incl(@_)}, '@',
+    ),
+    'path-root' => _config_parse_path_func(
+        sub {$_->get_path_root()}, sub {$_->set_path_root(@_)},
+    ),
+);
+
+# Properties from FCM::Util
+our @UTIL_PROP_KEYS = qw{diff3 diff3.flags};
+
+# Creates the class.
+__PACKAGE__->class(
+    {   config_parser_of => {isa => '%', default => {%CONFIG_PARSER_OF}},
+        prop_of          => '%',
+        shared_util_of   => '%',
+        util             => '&',
+    },
+    {   init => \&_init,
+        action_of => {
+            config_parse              => \&_config_parse,
+            config_parse_class_prop   => \&_config_parse_class_prop,
+            config_parse_inherit_hook => \&_config_parse_inherit_hook,
+            config_unparse            => \&_config_unparse,
+            config_unparse_class_prop => \&_config_unparse_class_prop,
+            ctx                       => \&_ctx,
+            ctx_load_hook             => \&_ctx_load_hook,
+            main                      => \&_main,
+        },
+    },
+);
+
+# Initialises the helpers of the class.
+sub _init {
+    my ($attrib_ref) = @_;
+    for my $util_prop_key (@UTIL_PROP_KEYS) {
+        my $prop = $attrib_ref->{util}->external_cfg_get($util_prop_key);
+        $attrib_ref->{prop_of}{$util_prop_key} = [$prop];
+    }
+}
+
+# Reads the extract.location declaration from a config entry.
+sub _config_parse_location {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    if (!@{$entry->get_ns_list()}) {
+        return $E->throw($E->CONFIG_NS, $entry);
+    }
+    my %PARSER_OF = (
+        'base'    => \&_config_parse_location_base,
+        'diff'    => \&_config_parse_location_diff,
+        'primary' => \&_config_parse_location_primary,
+    );
+    my %modifier_of = %{$entry->get_modifier_of()};
+    if (!grep {exists($modifier_of{$_})} keys(%PARSER_OF)) {
+        $modifier_of{'base'} = 1;
+    }
+    for my $key (grep {exists($modifier_of{$_})} keys(%PARSER_OF)) {
+        $PARSER_OF{$key}->($attrib_ref, $ctx, $entry);
+    }
+}
+
+# Reads the extract.location{base} declaration from a config entry.
+sub _config_parse_location_base {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    my %option;
+    if (exists($entry->get_modifier_of()->{'type'})) {
+        %option = ('type' => $entry->get_modifier_of()->{'type'});
+    }
+    for my $ns (@{$entry->get_ns_list()}) {
+        if (!exists($ctx->get_project_of()->{$ns})) {
+            $ctx->get_project_of()->{$ns} = $ctx->CTX_PROJECT->new({ns => $ns});
+        }
+        my $project = $ctx->get_project_of()->{$ns};
+        if ($project->get_inherited()) {
+            if (!$entry->get_value()) {
+                return $E->throw($E->CONFIG_VALUE, $entry);
+            }
+            my $locator = FCM::Context::Locator->new(
+                $entry->get_value(), \%option,
+            );
+            if ($project->get_locator()) {
+                $attrib_ref->{util}->loc_rel2abs(
+                    $locator,
+                    $project->get_locator(),
+                );
+            }
+            $attrib_ref->{util}->loc_as_invariant($locator);
+            my $i_locator = $project->get_trees()->[0]->get_locator();
+            if ($locator->get_value() ne $i_locator->get_value()) {
+                return $E->throw($E->CONFIG_CONFLICT, $entry);
+            }
+        }
+        else {
+            if (    !exists($project->get_trees()->[0])
+                ||  !defined($project->get_trees()->[0])
+            ) {
+                $project->get_trees()->[0]
+                    = $ctx->CTX_TREE->new({key => 0, ns => $ns});
+            }
+            if ($entry->get_value()) {
+                my $locator = FCM::Context::Locator->new(
+                    $entry->get_value(), \%option,
+                );
+                $project->get_trees()->[0]->set_locator($locator);
+            }
+            else {
+                $project->get_trees()->[0] = undef;
+            }
+        }
+    }
+}
+
+# Reads the extract.location{diff} declaration from a config entry.
+sub _config_parse_location_diff {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    my %option;
+    if (exists($entry->get_modifier_of()->{'type'})) {
+        %option = ('type' => $entry->get_modifier_of()->{'type'});
+    }
+    for my $ns (@{$entry->get_ns_list()}) {
+        if (!exists($ctx->get_project_of()->{$ns})) {
+            $ctx->get_project_of()->{$ns} = $ctx->CTX_PROJECT->new({ns => $ns});
+        }
+        my $project = $ctx->get_project_of()->{$ns};
+        my ($base, @diffs) = @{$project->get_trees()};
+        @diffs = grep {
+                $_->get_inherited()
+            ||      $option{type}
+                &&  $_->get_locator()->get_type()
+                &&  $option{type} ne $_->get_locator()->get_type()
+        } @diffs;
+        for my $value ($entry->get_values()) {
+            if (!$value) {
+                return $E->throw($E->CONFIG_VALUE, $entry);
+            }
+            push(
+                @diffs,
+                $ctx->CTX_TREE->new({
+                    key     => scalar(@diffs) + 1,
+                    locator => FCM::Context::Locator->new($value, \%option),
+                    ns      => $ns,
+                }),
+            );
+        }
+        @{$project->get_trees()} = ($base, @diffs);
+    }
+}
+
+# Reads the extract.location{primary} declaration from a config entry.
+sub _config_parse_location_primary {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    my %option;
+    if (exists($entry->get_modifier_of()->{'type'})) {
+        %option = ('type' => $entry->get_modifier_of()->{'type'});
+    }
+    for my $ns (@{$entry->get_ns_list()}) {
+        if (!exists($ctx->get_project_of()->{$ns})) {
+            $ctx->get_project_of()->{$ns} = $ctx->CTX_PROJECT->new({ns => $ns});
+        }
+        my $project = $ctx->get_project_of()->{$ns};
+        if ($entry->get_value()) {
+            my $locator = FCM::Context::Locator->new(
+                $entry->get_value(), \%option,
+            );
+            $attrib_ref->{util}->loc_as_normalised($locator);
+            if ($project->get_inherited()) {
+                my $project_locator = $project->get_locator();
+                if ($project_locator->get_value() ne $locator->get_value()) {
+                    return $E->throw($E->CONFIG_CONFLICT, $entry);
+                }
+            }
+            else {
+                $project->set_locator($locator);
+            }
+        }
+        else {
+            $project->set_locator(undef);
+        }
+    }
+}
+
+# Reads the extract.ns declaration from a config entry.
+sub _config_parse_ns_list {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    @{$ctx->get_ns_list()} = $entry->get_values();
+}
+
+# Returns a function to parse extract.path-*.
+sub _config_parse_path_func {
+    my ($getter, $setter, $isa) = @_;
+    $isa ||= '$';
+    sub {
+        my ($attrib_ref, $ctx, $entry) = @_;
+        my @ns_list
+            = @{$entry->get_ns_list()} ? @{$entry->get_ns_list()}
+            :                            @{$ctx->get_ns_list()}
+            ;
+        for my $ns (@ns_list) {
+            if (!exists($ctx->get_project_of()->{$ns})) {
+                $ctx->get_project_of()->{$ns}
+                    = $ctx->CTX_PROJECT->new({ns => $ns});
+            }
+            my $project = $ctx->get_project_of()->{$ns};
+            my $value = $entry->get_value();
+            if ($isa eq '@') {
+                $value = [map {$_ eq q{/} ? q{} : $_} $entry->get_values()];
+            }
+            local($_) = $project;
+            if ($_->get_inherited()) {
+                my $old = $getter->();
+                my $new = $value;
+                if ($isa eq '@') {
+                    $old = _config_unparse_join(@{$old});
+                    $new = _config_unparse_join(@{$new});
+                }
+                if ($old ne $new) {
+                    return $E->throw($E->CONFIG_CONFLICT, $entry);
+                }
+            }
+            else {
+                $setter->($value);
+            }
+        }
+    };
+}
+
+# A hook command for the "inherit/use" declaration.
+sub _config_parse_inherit_hook {
+    my ($attrib_ref, $ctx, $i_ctx) = @_;
+    @{$ctx->get_ns_list()} = @{$i_ctx->get_ns_list()};
+    while (my ($ns, $i_project) = each(%{$i_ctx->get_project_of()})) {
+        my $project = dclone($i_project);
+        $project->set_inherited(1);
+        for my $tree (@{$project->get_trees()}) {
+            $tree->set_inherited(1);
+        }
+        $ctx->get_project_of()->{$ns} = $project;
+    }
+    _config_parse_inherit_hook_prop($attrib_ref, $ctx, $i_ctx);
+}
+
+# Turns a context into a list of configuration entries.
+sub _config_unparse {
+    my ($attrib_ref, $ctx) = @_;
+    my %LABEL_OF
+        = map {($_ => $ctx->get_id() . q{.} . $_)} keys(%CONFIG_PARSER_OF);
+    my @entries = (
+        FCM::Context::ConfigEntry->new({
+            label => $LABEL_OF{ns},
+            value => _config_unparse_join(@{$ctx->get_ns_list()}),
+        }),
+    );
+    for my $p_ns (sort keys(%{$ctx->get_project_of()})) {
+        my $project = $ctx->get_project_of($p_ns);
+        my ($base, @diffs) = @{$project->get_trees()};
+        if (!$project->get_inherited()) {
+            if (defined($project->get_locator())) {
+                my $locator = $project->get_locator();
+                my %modifier_of = (primary => 1, type => $locator->get_type());
+                push(
+                    @entries,
+                    FCM::Context::ConfigEntry->new({
+                        label       => $LABEL_OF{location},
+                        modifier_of => \%modifier_of,
+                        ns_list     => [$p_ns],
+                        value       => $locator->get_value(),
+                    }),
+                );
+            }
+            if (@{$project->get_path_excl()}) {
+                my @values = map {$_ ? $_ : q{/}} @{$project->get_path_excl()};
+                push(
+                    @entries,
+                    FCM::Context::ConfigEntry->new({
+                        label       => $LABEL_OF{'path-excl'},
+                        ns_list     => [$p_ns],
+                        value       => _config_unparse_join(@values),
+                    }),
+                );
+            }
+            if (@{$project->get_path_incl()}) {
+                my @values = map {$_ ? $_ : q{/}} @{$project->get_path_incl()};
+                push(
+                    @entries,
+                    FCM::Context::ConfigEntry->new({
+                        label       => $LABEL_OF{'path-incl'},
+                        ns_list     => [$p_ns],
+                        value       => _config_unparse_join(@values),
+                    }),
+                );
+            }
+            if ($project->get_path_root()) {
+                push(
+                    @entries,
+                    FCM::Context::ConfigEntry->new({
+                        label      => $LABEL_OF{'path-root'},
+                        ns_list    => [$p_ns],
+                        value      => $project->get_path_root(),
+                    }),
+                );
+            }
+            my $value = $base->get_locator()->get_value();
+            push(
+                @entries,
+                FCM::Context::ConfigEntry->new({
+                    label       => $LABEL_OF{'location'},
+                    modifier_of => {type => $base->get_locator()->get_type()},
+                    ns_list     => [$p_ns],
+                    value       => $value,
+                }),
+            );
+        }
+        @diffs = grep {!$_->get_inherited()} @diffs;
+        if (@diffs) {
+            my %type_set = map {($_->get_locator()->get_type() => 1)} @diffs;
+            for my $type (sort(keys(%type_set))) {
+                my $value = _config_unparse_join(
+                    map  {$_->get_locator()->get_value()}
+                    grep {$_->get_locator()->get_type() eq $type}
+                    @diffs
+                );
+                push(
+                    @entries,
+                    FCM::Context::ConfigEntry->new({
+                        label       => $LABEL_OF{'location'},
+                        modifier_of => {diff => 1, type => $type},
+                        ns_list     => [$p_ns],
+                        value       => $value,
+                    }),
+                );
+            }
+        }
+    }
+    push(@entries, _config_unparse_prop($attrib_ref, $ctx));
+    return @entries;
+}
+
+# Returns a new context.
+sub _ctx {
+    my ($attrib_ref, $id_of_class, $id) = @_;
+    FCM::Context::Make::Extract->new({id => $id, id_of_class => $id_of_class});
+}
+
+# Hook when loading a previous ctx.
+sub _ctx_load_hook {
+    my ($attrib_ref, $old_m_ctx, $old_ctx, $old_m_dest, $old_dest) = @_;
+    my $path_mod_func = sub {
+        my ($get_func, $set_func) = @_;
+        my $path = $get_func->();
+        if (!defined($path)) {
+            return;
+        }
+        my $rel_path = abs2rel($path, $old_m_dest);
+        if (index($rel_path, '..') != 0) {
+            $set_func->(catfile($old_m_ctx->get_dest(), $rel_path));
+        }
+    };
+    while (my ($ns, $project) = each(%{$old_ctx->get_project_of()})) {
+        $path_mod_func->(
+            sub {$project->get_cache()},
+            sub {$project->set_cache(@_)},
+        );
+        for my $tree (@{$project->get_trees()}) {
+            $path_mod_func->(
+                sub {$tree->get_cache()},
+                sub {$tree->set_cache(@_)},
+            );
+            for my $source (@{$tree->get_sources()}) {
+                $path_mod_func->(
+                    sub {$source->get_cache()},
+                    sub {$source->set_cache(@_)},
+                );
+            }
+        }
+    }
+    while (my ($key, $target) = each(%{$old_ctx->get_target_of()})) {
+        $path_mod_func->(
+            sub {$target->get_path()},
+            sub {$target->set_path(@_)},
+        );
+        $path_mod_func->(
+            sub {$target->get_dests()->[0]},
+            sub {$target->get_dests()->[0] = $_[0]},
+        );
+        while (my ($ns, $source) = each(%{$target->get_source_of()})) {
+            $path_mod_func->(
+                sub {$source->get_cache()},
+                sub {$source->set_cache(@_)},
+            );
+        }
+    }
+}
+
+# The main function of this class.
+sub _main {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    local($UTIL) = $attrib_ref->{util};
+    for my $function (
+        \&_elaborate_ctx_of_project,
+        \&_elaborate_ctx_of_target,
+        \&_extract_incremental,
+        \&_project_tree_caches_update,
+        \&_symlink_handle,
+        \&_targets_update,
+    ) {
+        $function->($attrib_ref, $m_ctx, $ctx);
+    }
+}
+
+# Elaborates the context: project and tree.
+sub _elaborate_ctx_of_project {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+
+    # Reports projects that are not used
+    my @bad_ns_list;
+    while (my ($p_ns, $project) = each(%{$ctx->get_project_of()})) {
+        if (    !$project->get_inherited()
+            &&  !grep {$_ eq $p_ns} @{$ctx->get_ns_list()}
+        ) {
+            push(@bad_ns_list, $p_ns);
+        }
+    }
+    if (@bad_ns_list) {
+        return $E->throw($E->EXTRACT_NS, \@bad_ns_list);
+    }
+
+    # Determines a list of new trees
+    my $prev_m_ctx = $m_ctx->get_prev_ctx();
+    my $prev_ctx
+        = defined($prev_m_ctx) ? $prev_m_ctx->get_ctx_of($ctx->get_id())
+        :                          undef
+        ;
+    my @trees; # list of new trees
+    for my $p_ns (@{$ctx->get_ns_list()}) {
+        # Ensures the project settings are defined
+        if (!exists($ctx->get_project_of()->{$p_ns})) {
+            $ctx->get_project_of()->{$p_ns}
+                = $ctx->CTX_PROJECT->new({ns => $p_ns});
+        }
+        my $project = $ctx->get_project_of()->{$p_ns};
+        
+        # Determine the root location of the project, if possible
+        if (defined($project->get_locator())) {
+            $UTIL->loc_as_normalised($project->get_locator());
+        }
+        else {
+            my $uri = $UTIL->loc_kw_prefix() . ':' . $p_ns;
+            my $locator = FCM::Context::Locator->new($uri);
+            local($@);
+            eval {$UTIL->loc_as_normalised($locator)};
+            if (!$@) {
+                $project->set_locator($locator);
+            }
+        }
+        # Ensures base tree is defined
+        if (!@{$project->get_trees()} || !defined($project->get_trees()->[0])) {
+            if (!defined($project->get_locator())) {
+                return $E->throw($E->EXTRACT_LOC_BASE, $p_ns);
+            }
+            my $head_locator = $UTIL->loc_trunk_at_head($project->get_locator());
+            my $locator
+                = $head_locator ? $head_locator
+                :                 dclone($project->get_locator())
+                ;
+            $project->get_trees()->[0] = $ctx->CTX_TREE->new(
+                {key => 0, locator => $locator, ns => $p_ns},
+            );
+        }
+        # Determine whether there is a usable previous extract
+        my %path_excl = map {($_, 1)} @{$project->get_path_excl()};
+        my %path_incl = map {($_, 1)} @{$project->get_path_incl()};
+        my $path_root = $project->get_path_root();
+        my ($can_use_prev, $prev_project);
+        if (defined($prev_ctx) && defined($prev_ctx->get_project_of($p_ns))) {
+            $prev_project = $prev_ctx->get_project_of($p_ns);
+            my %prev_path_excl = map {($_, 1)} @{$prev_project->get_path_excl()};
+            my %prev_path_incl = map {($_, 1)} @{$prev_project->get_path_incl()};
+            my $prev_path_root = $prev_project->get_path_root();
+            $can_use_prev
+                =  $prev_ctx->get_status() eq $m_ctx->ST_OK
+                && !$UTIL->hash_cmp(\%path_excl, \%prev_path_excl, 1)
+                && !$UTIL->hash_cmp(\%path_incl, \%prev_path_incl, 1)
+                && $path_root eq $prev_path_root
+                ;
+        }
+        # Tree locators as invariant
+        TREE:
+        for my $tree (grep {!$_->get_inherited()} @{$project->get_trees()}) {
+            my $tree_locator = $tree->get_locator();
+            # Ensures that the tree locator is an absolute path
+            if (defined($project->get_locator())) {
+                $UTIL->loc_rel2abs($tree_locator, $project->get_locator());
+            }
+            # Determines invariant form of the locator of the project tree.
+            $UTIL->loc_as_invariant($tree_locator);
+        }
+        # Remove diff trees that are the same as the base tree
+        my ($base_tree, @old_diff_trees) = @{$project->get_trees()};
+        my $base_value = $base_tree->get_locator()->get_value();
+        my @new_diff_trees;
+        TREE:
+        for my $tree (@old_diff_trees) {
+            if ($base_value ne $tree->get_locator()->get_value()) {
+                push(@new_diff_trees, $tree);
+                $tree->set_key(scalar(@new_diff_trees)); # reset key (index)
+            }
+        }
+        $project->set_trees([$base_tree, @new_diff_trees]);
+        # Determine the new trees
+        TREE:
+        for my $tree (grep {!$_->get_inherited()} @{$project->get_trees()}) {
+            my $tree_locator = $tree->get_locator();
+            if (    $can_use_prev
+                &&  $tree_locator->get_value_level() >= $tree_locator->L_INVARIANT
+            ) {
+                my $prev_tree = first {
+                    $tree_locator->get_value() eq $_->get_locator()->get_value()
+                } @{$prev_project->get_trees()};
+                if ($prev_tree) {
+                    my $prev_tree_locator = $prev_tree->get_locator();
+                    $tree->set_sources($prev_tree->get_sources());
+                    if ($tree->get_key() || !$prev_tree->get_key()) {
+                        # Only safe to re-use cache if both are base trees
+                        # or for diff tree with an unchanged base tree
+                        $tree->set_cache($prev_tree->get_cache());
+                    }
+                    next TREE;
+                }
+                if (!$tree->get_key()) { # base tree changed
+                    $can_use_prev = 0;
+                }
+            }
+            push(@trees, $tree); # new tree
+        }
+    }
+
+    # Obtain source info for each new tree, using the task runner
+    if (@trees) {
+        my $timer = $UTIL->timer();
+        my $n_jobs = $m_ctx->get_option_of('jobs');
+        if ($n_jobs && $n_jobs > scalar(@trees)) {
+            $n_jobs = scalar(@trees);
+        }
+        my $elapse_tasks = 0;
+        my $runner = $UTIL->task_runner(
+            sub {_elaborate_ctx_of_project_tree($attrib_ref, $m_ctx, $ctx, @_)},
+            $n_jobs,
+        );
+        my $n = eval {
+            $runner->main(
+                # get
+                sub {
+                    if (!@trees) {
+                        return;
+                    }
+                    my $tree = shift(@trees);
+                    my $id = join(':', $tree->get_ns(), $tree->get_key());
+                    FCM::Context::Task->new({ctx => $tree, id => $id});
+                },
+                # put
+                sub {
+                    my ($task) = @_;
+                    if ($task->get_state() eq $task->ST_FAILED) {
+                        die($task->get_error());
+                    }
+                    my $ns = $task->get_ctx()->get_ns();
+                    my $key = $task->get_ctx()->get_key();
+                    my $project = $ctx->get_project_of()->{$ns};
+                    my $tree = $project->get_trees()->[$key];
+                    $tree->set_locator($task->get_ctx()->get_locator());
+                    $tree->set_sources($task->get_ctx()->get_sources());
+                    $elapse_tasks += $task->get_elapse();
+                },
+            );
+        };
+        my $e = $@;
+        $runner->destroy();
+        if ($e) {
+            die($e);
+        }
+        $UTIL->event(
+            FCM::Context::Event->MAKE_EXTRACT_RUNNER_SUMMARY,
+            'tree-sources-info-get', $n, $timer->(), $elapse_tasks,
+        );
+    }
+    $UTIL->event(
+        FCM::Context::Event->MAKE_EXTRACT_PROJECT_TREE,
+        {   map {($_ => [
+                map {$_->get_locator()}
+                    @{$ctx->get_project_of()->{$_}->get_trees()}
+            ])}
+            sort keys(%{$ctx->get_project_of()})
+        },
+    );
+}
+
+# Elaborates the context: new tree in a project.
+sub _elaborate_ctx_of_project_tree {
+    my ($attrib_ref, $m_ctx, $ctx, $tree) = @_;
+    my $project = $ctx->get_project_of()->{$tree->get_ns()};
+    my $path_root = $project->get_path_root();
+    # TODO: support regular expression or wildcards?
+    my %path_incl = map {($_ => 1)} @{$project->get_path_incl()};
+    my %path_excl = map {($_ => 1)} @{$project->get_path_excl()};
+    $UTIL->loc_find(
+        $tree->get_locator(),
+        sub {
+            my ($locator, $locator_attrib_ref) = @_;
+            if ($locator_attrib_ref->{is_dir}) {
+                return;
+            }
+            my $ns_in_tree = $locator_attrib_ref->{ns};
+            my $ns = $ns_in_tree;
+            if ($path_root) {
+                if ($path_root ne $UTIL->ns_common($path_root, $ns)) {
+                    return;
+                }
+                $ns = $ns eq $path_root ? q{}
+                    :                     substr($ns, length($path_root) + 1)
+                    ;
+            }
+            my $ns_iter_ref = $UTIL->ns_iter($ns, $UTIL->NS_ITER_UP);
+            NS:
+            while (defined(my $head = $ns_iter_ref->())) {
+                if (exists($path_incl{$head})) {
+                    last NS;
+                }
+                if (exists($path_excl{$head})) {
+                    return;
+                }
+            }
+            push(
+                @{$tree->get_sources()},
+                $ctx->CTX_SOURCE->new({
+                    key_of_tree => $tree->get_key(),
+                    locator     => $locator,
+                    ns          => $UTIL->ns_cat($tree->get_ns(), $ns),
+                    ns_in_tree  => $ns_in_tree,
+                }),
+            );
+        },
+    );
+    $tree;
+}
+
+# Elaborates the context: target.
+sub _elaborate_ctx_of_target {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    # Works out the extract sources and targets
+    my $DEST = $attrib_ref->{shared_util_of}{dest};
+    my $ns_sep = $UTIL->ns_sep();
+    while (my ($p_ns, $project) = each(%{$ctx->get_project_of()})) {
+        my ($tree_base, @trees) = @{$project->get_trees()};
+        # Sources from the base tree
+        for my $source (@{$tree_base->get_sources()}) {
+            my $ns = $source->get_ns();
+            my @paths = split($ns_sep, $ns);
+            my $dest_list_ref = $DEST->paths(
+                $m_ctx, 'target', $ctx->get_id(), @paths
+            );
+            $ctx->get_target_of()->{$ns} = $ctx->CTX_TARGET->new({
+                dests     => $dest_list_ref,
+                ns        => $ns,
+                source_of => {$tree_base->get_key() => $source},
+            });
+        }
+        my %sources_in_base
+            = map {($_->get_ns() => $_)} @{$tree_base->get_sources()};
+        # Sources from the diff trees
+        for my $tree (@trees) {
+            my $key = $tree->get_key();
+            my %sources_deleted = %sources_in_base;
+            # Handles new/modified sources
+            for my $source (@{$tree->get_sources()}) {
+                my $ns = $source->get_ns();
+                delete($sources_deleted{$ns});
+                if (exists($ctx->get_target_of()->{$ns})) {
+                    my $target = $ctx->get_target_of()->{$ns};
+                    my $base_source = $target->get_source_of()->{0};
+                    if (    $base_source->get_locator()
+                        &&  _source_eq($base_source, $source)
+                    ) {
+                        $source->set_status($source->ST_UNCHANGED);
+                    }
+                    else {
+                        # Source modified by diff tree
+                        $target->get_source_of()->{$key} = $source;
+                    }
+                }
+                else {
+                    # Source added by diff tree
+                    my @paths = split($ns_sep, $ns);
+                    my $dest_list_ref = $DEST->paths(
+                        $m_ctx, 'target', $ctx->get_id(), @paths,
+                    );
+                    $ctx->get_target_of()->{$ns} = $ctx->CTX_TARGET->new({
+                        dests     => $dest_list_ref,
+                        ns        => $ns,
+                        source_of => {
+                            0 => $ctx->CTX_SOURCE->new({
+                                key_of_tree => 0,
+                                status      => $ctx->CTX_SOURCE->ST_MISSING,
+                            }),
+                            $key => $source,
+                        },
+                    });
+                }
+            }
+            # Handle deleted sources
+            while (my ($ns) = each(%sources_deleted)) {
+                my $target = $ctx->get_target_of()->{$ns};
+                $target->get_source_of()->{$key} = $ctx->CTX_SOURCE->new({
+                    key_of_tree => $key,
+                    ns          => $ns,
+                    status      => $ctx->CTX_SOURCE->ST_MISSING,
+                });
+            }
+        }
+    }
+}
+
+# Extract: compare with previous extract.
+sub _extract_incremental {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my $prev_m_ctx = $m_ctx->get_prev_ctx();
+    my $prev_ctx
+        = defined($prev_m_ctx) ? $prev_m_ctx->get_ctx_of($ctx->get_id())
+        :                          undef
+        ;
+    if (!defined($prev_ctx)) {
+        return;
+    }
+    my %deleted = map {($_ => 1)} keys(%{$prev_ctx->get_target_of()});
+    # Compares the sources in each target
+    TARGET:
+    while (my ($ns, $target) = each(%{$ctx->get_target_of()})) {
+        delete($deleted{$ns});
+        if (!exists($prev_ctx->get_target_of()->{$ns})) {
+            next TARGET;
+        }
+        my $prev_target = $prev_ctx->get_target_of()->{$ns};
+        my %prev_source_of = %{$prev_target->get_source_of()};
+        my %source_of = %{$target->get_source_of()};
+        if (keys(%prev_source_of) != keys(%source_of)) {
+            next TARGET;
+        }
+        while (my ($key_of_tree, $source) = each(%source_of)) {
+            if (!exists($prev_source_of{$key_of_tree})) {
+                next TARGET;
+            }
+            my $prev_source = $prev_source_of{$key_of_tree};
+            if (   $prev_source->get_status() ne $source->get_status()
+                || !$source->is_missing() && !_source_eq($prev_source, $source)
+            ) {
+                next TARGET;
+            }
+        }
+        $target->set_status_of_source($prev_target->get_status_of_source());
+        if ($prev_target->is_ok()) {
+            $target->set_path($prev_target->get_path());
+            $target->set_status($target->ST_UNCHANGED);
+        }
+    }
+    # Creates a dummy target for each deleted target
+    my $ns_sep = $UTIL->ns_sep();
+    while (my $ns = each(%deleted)) {
+        my $target = $prev_ctx->get_target_of($ns);
+        if ($target->get_status() ne $target->ST_DELETED) {
+            my @paths = split($ns_sep, $ns);
+            my $dest_list_ref = $attrib_ref->{shared_util_of}{dest}->paths(
+                $m_ctx, 'target', $ctx->get_id(), @paths,
+            );
+            $ctx->get_target_of()->{$ns}
+                = $ctx->CTX_TARGET->new({dests => $dest_list_ref, ns => $ns});
+        }
+    }
+}
+
+# Updates the project tree caches.
+sub _project_tree_caches_update {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    # If previous cache in .tar.gz, extract it
+    my $cache_tar_gz = $attrib_ref->{shared_util_of}{dest}->path(
+        $m_ctx, 'sys-cache', $ctx->get_id() . '.tar.gz',
+    );
+    if (-f $cache_tar_gz) {
+        my @command = (
+            qw{tar -x -z}, '-C', dirname($cache_tar_gz), '-f', $cache_tar_gz,
+        );
+        my %value_of = %{$UTIL->shell_simple(\@command)};
+        if ($value_of{'rc'} == 0) {
+            unlink($cache_tar_gz);
+        }
+    }
+    # Start the parallel task runner to do project tree caches update
+    my $timer = $UTIL->timer();
+    my $n_jobs = $m_ctx->get_option_of('jobs');
+    my $n_trees = scalar(
+        grep {!$_->get_cache()}
+        map {@{$_->get_trees()}}
+        values(%{$ctx->get_project_of()})
+    );
+    if ($n_trees == 0) {
+        return;
+    }
+    if ($n_jobs && $n_jobs > $n_trees) {
+        $n_jobs = $n_trees;
+    }
+    my $elapse_tasks = 0;
+    my @args = ($attrib_ref, $m_ctx, $ctx);
+    my $runner = $UTIL->task_runner(
+        sub {_project_tree_cache_update_by_export(@args, @_)},
+        $n_jobs,
+    );
+    my $n = eval {
+        $runner->main(
+            _project_tree_cache_update_get_func(@args),
+            _project_tree_cache_update_put_func(@args, \$elapse_tasks),
+        );
+    };
+    my $e = $@;
+    $runner->destroy();
+    if ($e) {
+        _finally($attrib_ref, $m_ctx, $ctx);
+        die($e);
+    }
+    $UTIL->event(
+        FCM::Context::Event->MAKE_EXTRACT_RUNNER_SUMMARY,
+        'tree-cache-export', $n, $timer->(), $elapse_tasks,
+    );
+}
+
+# Updates the source cache for a project tree by exporting it.
+sub _project_tree_cache_update_by_export {
+    my ($attrib_ref, $m_ctx, $ctx, $tree) = @_;
+    my $cache = $tree->get_cache();
+    # Exports the smallest common tree
+    my $root_ns;
+    SOURCE:
+    for my $source (@{$tree->get_sources()}) {
+        if ($source->is_unchanged()) {
+            next SOURCE;
+        }
+        if (!defined($root_ns)) {
+            $root_ns = $source->get_ns_in_tree();
+            next SOURCE;
+        }
+        $root_ns = $UTIL->ns_common(
+            $root_ns, $source->get_ns_in_tree(),
+        );
+        if (!$root_ns) {
+            last SOURCE;
+        }
+    }
+    if (!defined($root_ns)) {
+        return;
+    }
+    my $cache_ns = $root_ns ? catfile($cache, $root_ns) : $cache;
+    my $locator_ns = $UTIL->loc_cat(
+        $tree->get_locator(), split($UTIL->ns_sep(), $root_ns),
+    );
+    eval{
+        mkpath(dirname($cache_ns));
+        $UTIL->loc_export($locator_ns, $cache_ns);
+    };
+    if (my $e = $@ || !-e $cache_ns && !-l $cache_ns) {
+        return $E->throw($E->DEST_CREATE, $cache_ns, $e);
+    }
+}
+
+# Generates an iterator for each tree requiring cache update.
+sub _project_tree_cache_update_get_func {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my @trees = map {@{$_->get_trees()}} values(%{$ctx->get_project_of()});
+    sub {
+        while (my $tree = shift(@trees)) {
+            if (!$tree->get_cache()) {
+                if ($UTIL->loc_export_ok($tree->get_locator())) {
+                    my $cache = $attrib_ref->{shared_util_of}{dest}->path(
+                        $m_ctx,
+                        'sys-cache',
+                        $ctx->get_id(),
+                        $tree->get_ns(),
+                        $tree->get_key(),
+                    );
+                    $tree->set_cache($cache);
+                    rmtree($cache);
+                    mkpath(dirname($cache));
+                    my $id = $tree->get_ns() . '/' . $tree->get_key();
+                    return FCM::Context::Task->new({ctx => $tree, id  => $id});
+                }
+                else {
+                    $tree->set_cache($tree->get_locator()->get_value());
+                    _project_tree_cache_update_sources(
+                        $attrib_ref, $m_ctx, $ctx, $tree,
+                    );
+                }
+            }
+        }
+        return;
+    };
+}
+
+# Generates a callback when a tree has a cache.
+sub _project_tree_cache_update_put_func {
+    my ($attrib_ref, $m_ctx, $ctx, $elapse_tasks_ref) = @_;
+    sub {
+        my ($task) = @_;
+        if ($task->get_state() eq $task->ST_FAILED) {
+            die($task->get_error());
+        }
+        my $ns = $task->get_ctx()->get_ns();
+        my $key = $task->get_ctx()->get_key();
+        my $tree = $ctx->get_project_of()->{$ns}->get_trees()->[$key];
+        _project_tree_cache_update_sources($attrib_ref, $m_ctx, $ctx, $tree);
+        ${$elapse_tasks_ref} += $task->get_elapse();
+    };
+}
+
+# Sets the caches of individual project tree sources.
+sub _project_tree_cache_update_sources {
+    my ($attrib_ref, $m_ctx, $ctx, $tree) = @_;
+    for my $source (@{$tree->get_sources()}) {
+        my $cache = catfile(
+            $tree->get_cache(),
+            split($UTIL->ns_sep(), $source->get_ns_in_tree()),
+        );
+        $source->set_cache($cache);
+    }
+}
+
+# Handles symbolic links.
+sub _symlink_handle {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    TARGET:
+    while (my ($ns, $target) = each(%{$ctx->get_target_of()})) {
+        if ($target->is_unchanged()) {
+            next TARGET;
+        }
+        my $source_hash_ref = $target->get_source_of();
+        # Remove sources that are symbolic links
+        while (my ($key, $source) = each(%{$source_hash_ref})) {
+            if ($source->get_cache() && -l $source->get_cache()) {
+                delete($source_hash_ref->{$key});
+                $UTIL->event(
+                    FCM::Context::Event->MAKE_EXTRACT_SYMLINK, $source,
+                );
+            }
+        }
+        # It is OK to have a target with no sources, but a target must have a
+        # base source if it has at least one diff source.
+        if (    keys(%{$source_hash_ref})
+            &&  !exists($source_hash_ref->{0})
+        ) {
+            $source_hash_ref->{0} = $ctx->CTX_SOURCE->new(
+                {key_of_tree => 0, status => $ctx->CTX_SOURCE->ST_MISSING},
+            );
+        }
+    }
+}
+
+# Updates the targets.
+sub _targets_update {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my %basket_of = (status => {}, status_of_source => {});
+    eval {
+        while (my ($ns, $target) = each(%{$ctx->get_target_of()})) {
+            if ($target->get_status() eq $target->ST_UNKNOWN) {
+                my %source_of = %{$target->get_source_of()};
+                my $handler
+                    = keys(%source_of) ? \&_target_update
+                    :                    \&_target_delete
+                    ;
+                $handler->($attrib_ref, $m_ctx, $ctx, $target);
+                my $base = delete($source_of{0});
+                my @diffs = grep {!$_->is_unchanged()} values(%source_of);
+                $target->set_status_of_source(
+                      !keys(%{$target->get_source_of()}) ? $target->ST_UNKNOWN
+                    : $base->is_missing()                ? $target->ST_ADDED
+                    : (grep {$_->is_missing()} @diffs)   ? $target->ST_DELETED
+                    : scalar(@diffs) > 1                 ? $target->ST_MERGED
+                    : scalar(@diffs)                     ? $target->ST_MODIFIED
+                    :                                      $target->ST_UNCHANGED
+                );
+                $UTIL->event(
+                    FCM::Context::Event->MAKE_EXTRACT_TARGET, $target,
+                );
+            }
+            $basket_of{status}{$target->get_status()}++;
+            $basket_of{status_of_source}{$target->get_status_of_source()}++;
+        }
+    };
+    if (my $e = $@) {
+        _finally($attrib_ref, $m_ctx, $ctx);
+        die($e);
+    }
+    $UTIL->event(
+        FCM::Context::Event->MAKE_EXTRACT_TARGET_SUMMARY, \%basket_of,
+    );
+    _finally($attrib_ref, $m_ctx, $ctx);
+}
+
+# Updates a deleted target.
+sub _target_delete {
+    my ($attrib_ref, $m_ctx, $ctx, $target) = @_;
+    my ($dest, @inherited_dests) = @{$target->get_dests()};
+    if (-f $dest) {
+        unlink($dest) || return $E->throw($E->DEST_CLEAN, $dest, $!);
+        $target->set_status($target->ST_DELETED);
+    }
+    for my $inherited_dest (@inherited_dests) {
+        if (-f $inherited_dest) {
+            $target->set_status($target->ST_O_DELETED);
+            return;
+        }
+    }
+}
+
+# Updates a normal target.
+sub _target_update {
+    my ($attrib_ref, $m_ctx, $ctx, $target) = @_;
+    my %source_of = %{$target->get_source_of()};
+    my $source_of_base = delete($source_of{0});
+    # Either missing source in a diff-tree
+    # Or     missing source in base-tree and no diff-trees
+    if (    (grep {$_->is_missing()} values(%source_of))
+        ||  $source_of_base->is_missing() && !keys(%source_of)
+    ) {
+        return _target_delete($attrib_ref, $m_ctx, $ctx, $target);
+    }
+    my $path = _target_update_source($attrib_ref, $m_ctx, $ctx, $target);
+    # Note: $path may be a File::Temp object.
+    my ($is_diff, $is_diff_in_perms, $is_in_prev, $rc) = (1, 1, undef, 1);
+    DEST:
+    for my $i (0 .. @{$target->get_dests()} - 1) {
+        my $dest = $target->get_dests()->[$i];
+        if (-f $dest) {
+            $is_in_prev = $i;
+            ($is_diff_in_perms, $is_diff) = _compare("$path", $dest);
+            last DEST;
+        }
+    }
+    if (!$is_diff && !$is_diff_in_perms) {
+        $target->set_path($target->get_dests()->[$is_in_prev]);
+        $target->set_status($target->ST_UNCHANGED);
+        return; # up to date
+    }
+    my $dest = $target->get_dests()->[0];
+    if ($is_diff) {
+        my $dest_dir = dirname($dest);
+        if (!-d $dest_dir) {
+            eval {mkpath($dest_dir)};
+            if (my $e = $@) {
+                return $E->throw($E->DEST_CREATE, $dest_dir, $e);
+            }
+        }
+        copy("$path", $dest)
+            || return $E->throw($E->COPY, ["$path", $dest], $!);
+    }
+    chmod((stat("$path"))[2] & oct(7777), $dest)
+        || return $E->throw($E->DEST_CREATE, $dest, $!);
+    $target->set_path($target->get_dests()->[0]);
+    $target->set_status(
+          $is_in_prev          ? $target->ST_O_ADDED
+        : defined($is_in_prev) ? $target->ST_MODIFIED
+        :                        $target->ST_ADDED
+    );
+}
+
+# Returns the source path that is to be used to update a target.
+sub _target_update_source {
+    my ($attrib_ref, $m_ctx, $ctx, $target) = @_;
+    my %source_of = %{$target->get_source_of()};
+    my $path_of_base = delete($source_of{0})->get_cache();
+    my @keys_and_paths;
+    while (my ($key, $source) = each(%source_of)) {
+        my $path = $source->get_cache();
+        if (!$path_of_base || _compare($path_of_base, $path)) {
+            if (!grep {!_compare($_->[1], $path)} @keys_and_paths) {
+                push(@keys_and_paths, [$key, $path]);
+            }
+        }
+        else {
+            $source->set_status($source->ST_UNCHANGED);
+        }
+    }
+    my @args = (
+        $m_ctx, $ctx, $target, $path_of_base,
+        (sort {$a->[0] <=> $b->[0]} @keys_and_paths),
+    );
+    return (
+          @keys_and_paths == 0 ? $path_of_base
+        : @keys_and_paths == 1 ? $keys_and_paths[0][1]
+        :                        _target_update_source_merge($attrib_ref, @args)
+    );
+}
+
+# Merges changes in contents of paths in @keys_and_paths against content in
+# $path_of_base.
+sub _target_update_source_merge {
+    my ($attrib_ref, $m_ctx, $ctx, $target, $path_of_base, @keys_and_paths) = @_;
+    if (!$path_of_base) {
+        $path_of_base = File::Temp->new();
+        if (!defined($path_of_base) || !close($path_of_base)) {
+            return $E->throw($E->DEST_CREATE, tmpdir(), $!);
+        }
+    }
+    my ($key_of_mine, $path_of_mine) = @{shift(@keys_and_paths)};
+    my @keys_done = ($key_of_mine);
+    while (my $key_and_path = shift(@keys_and_paths)) {
+        my ($key, $path) = @{$key_and_path};
+        my @command = (
+            (map {_props($attrib_ref, $_, $ctx)} qw{diff3 diff3.flags}),
+            "$path_of_mine", "$path_of_base", $path,
+        );
+        my %value_of = %{$UTIL->shell_simple(\@command)};
+        if ($value_of{rc} && $value_of{rc} == 1) {
+            # Write conflict output to .fcm-make/extract/conflict/$NS
+            my $file = $attrib_ref->{shared_util_of}{dest}->path(
+                $m_ctx, 'sys', $ctx->get_id(), 'merge',
+                $target->get_ns() . '.diff',
+            );
+            $UTIL->file_save($file, $value_of{o});
+            return $E->throw($E->EXTRACT_MERGE, {
+                'target'    => $target,
+                'output'    => $file,
+                'keys_done' => \@keys_done,
+                'key'       => $key,
+                'keys_left' => [map {$_->[0]} @keys_and_paths],
+            });
+        }
+        elsif ($value_of{rc}) {
+            return $E->throw(
+                $E->SHELL, {command_list => \@command, %value_of}, $value_of{e},
+            );
+        }
+        my $perm = (stat("$path_of_mine"))[2] & 07777 | (stat($path))[2] & 07777;
+        for my $action (
+            sub {$path_of_mine = File::Temp->new()},
+            sub {print({$path_of_mine} $value_of{o})},
+            sub {close($path_of_mine)},
+            sub {chmod($perm, "$path_of_mine")},
+        ) {
+            $action->() || return $E->throw($E->DEST_CREATE, "$path_of_mine", $!);
+        }
+        push(@keys_done, $key);
+    }
+    return $path_of_mine;
+}
+
+# Perform final actions.
+# Archive cache directory if necessary.
+sub _finally {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    if (!$m_ctx->get_option_of('archive')) {
+        return;
+    }
+    my $cache = $attrib_ref->{shared_util_of}{dest}->path(
+        $m_ctx, 'sys-cache', $ctx->get_id(),
+    );
+    if (-d $cache) {
+        my @command = (
+            qw{tar -c -z}, '-C', dirname($cache), '-f', $cache . '.tar.gz',
+            $ctx->get_id(),
+        );
+        my %value_of = %{$UTIL->shell_simple(\@command)};
+        if ($value_of{'rc'} == 0) {
+            rmtree($cache);
+        }
+    }
+}
+
+# In scalar context, returns true if the contents or permissions of 2 paths
+# differ. In array context, returns ($is_diff_in_perms, $is_diff_in_content).
+sub _compare {
+    my ($path1, $path2) = @_;
+    my $is_diff_in_perms = (stat($path1))[2] != (stat($path2))[2];
+    wantarray()
+        ? ($is_diff_in_perms, compare($path1, $path2))
+        : ($is_diff_in_perms || compare($path1, $path2))
+    ;
+}
+
+# Returns true if two sources are the same or if their latest modified revisions
+# are the same.
+sub _source_eq {
+    my ($source1, $source2) = @_;
+    my ($locator1, $locator2) = map {$_->get_locator()} ($source1, $source2);
+    # Compares their value + mtime or their last modified revision
+            $locator1->get_value() eq $locator2->get_value()
+        &&  defined($locator1->get_last_mod_time())
+        &&  defined($locator2->get_last_mod_time())
+        &&  $locator1->get_last_mod_time() eq $locator2->get_last_mod_time()
+    ||      defined($locator1->get_last_mod_rev())
+        &&  defined($locator2->get_last_mod_rev())
+        &&  $locator1->get_last_mod_rev() eq $locator2->get_last_mod_rev()
+    ;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Extract
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Extract;
+    my $extract = FCM::System::Make::Extract->new(\%attrib);
+    $extract->($m_ctx, $ctx);
+
+=head1 DESCRIPTION
+
+Implements the extract sub-system. An instance of this class is expected to be
+initialised and called by L<FCM::System::Make|FCM::System::Make>.
+
+=head1 METHODS
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=head1 ATTRIBUTES
+
+The $class->new(\%attrib) method of this class supports the following
+attributes:
+
+=over 4
+
+=item config_parser_of
+
+A HASH to map the labels in a configuration file to their parsers. (default =
+%FCM::System::Make::Extract::CONFIG_PARSER_OF)
+
+=item prop_of
+
+A HASH to map the names of the properties to their settings. Each setting
+is a 2-element ARRAY reference, where element [0] is the default setting
+and element [1] is a flag to indicate whether the property accepts a name-space
+or not. (default = %FCM::System::Make::Extract::PROP_OF)
+
+=item shared_util_of
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=item util
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=back
+
+=head1 TODO
+
+Handle alternate method of merge (e.g. Algorithm::Merge).
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Mirror.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Mirror.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Mirror.pm	(revision 5095)
@@ -0,0 +1,439 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Mirror;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::ConfigEntry;
+use FCM::Context::Event;
+use FCM::Context::Make;
+use FCM::Context::Make::Mirror;
+use FCM::Context::Make::Share::Property;
+use FCM::System::Make::Share::Subsystem;
+use File::Basename qw{dirname};
+use File::Path qw{mkpath};
+use File::Spec::Functions qw{abs2rel file_name_is_absolute rel2abs};
+use POSIX qw{strftime};
+use Storable qw{dclone};
+use Sys::Hostname qw{hostname};
+use Text::ParseWords qw{shellwords};
+
+# Alias
+my $E = 'FCM::System::Exception';
+
+# Configuration parser label to action map
+our %CONFIG_PARSER_OF = (
+    'target' => \&_config_parse_target,
+);
+
+# Default properties
+our %PROP_OF = (
+    'config-file.name'  => [q{}],
+    'config-file.steps' => [q{}],
+    'no-config-file'    => [q{}],
+);
+
+# Properties from FCM::Util
+our @UTIL_PROP_KEYS = qw{ssh ssh.flags rsync rsync.flags};
+
+# Creates the class.
+__PACKAGE__->class(
+    {   config_parser_of => {isa => '%', default => {%CONFIG_PARSER_OF}},
+        prop_of          => {isa => '%', default => {%PROP_OF}},
+        shared_util_of   => '%',
+        util             => '&',
+    },
+    {   init => \&_init,
+        action_of => {
+            config_parse              => \&_config_parse,
+            config_parse_class_prop   => \&_config_parse_class_prop,
+            config_parse_inherit_hook => \&_config_parse_inherit_hook,
+            config_unparse            => \&_config_unparse,
+            config_unparse_class_prop => \&_config_unparse_class_prop,
+            ctx                       => \&_ctx,
+            main                      => \&_main,
+        },
+    },
+);
+
+# Initialises the helpers of the class.
+sub _init {
+    my ($attrib_ref) = @_;
+    for my $util_prop_key (@UTIL_PROP_KEYS) {
+        my $prop = $attrib_ref->{util}->external_cfg_get($util_prop_key);
+        $attrib_ref->{prop_of}{$util_prop_key} = [$prop];
+    }
+}
+
+# Reads the mirror.target declaration from a config entry.
+sub _config_parse_target {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    my $value = $entry->get_value();
+    # Note: it is easier to parse the value in reverse because a target may look
+    # like "path", "machine:path" or "logname@machine:path"
+    my ($path, $auth) = reverse(split(':', $value, 2));
+    my ($machine, $logname) = $auth ? reverse(split('@', $auth, 2)) : ();
+    if (!$path || ($logname && !$machine)) {
+        return $E->throw($E->CONFIG_VALUE, $entry);
+    }
+    $ctx->set_target_logname($logname);
+    $ctx->set_target_machine($machine);
+    $ctx->set_target_path($path);
+}
+
+# A hook command for the "inherit/use" declaration (extract).
+sub _config_parse_inherit_hook {
+    my ($attrib_ref, $ctx, $i_ctx) = @_;
+    $ctx->set_target_machine($i_ctx->get_target_machine());
+    _config_parse_inherit_hook_prop($attrib_ref, $ctx, $i_ctx);
+}
+
+# Turns a context into a list of configuration entries.
+sub _config_unparse {
+    my ($attrib_ref, $ctx) = @_;
+    (   (   $ctx->get_target_path()
+            ? FCM::Context::ConfigEntry->new({
+                label => $ctx->get_id() . q{.} . 'target',
+                value => (_target_and_authority($ctx))[0],
+            })
+            : ()
+        ),
+        _config_unparse_prop($attrib_ref, $ctx),
+    );
+}
+
+# Returns a new context.
+sub _ctx {
+    my ($attrib_ref, $id_of_class, $id) = @_;
+    FCM::Context::Make::Mirror->new({id => $id, id_of_class => $id_of_class});
+}
+
+# The main function.
+sub _main {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    if (!$ctx->get_target_path()) {
+        return $E->throw($E->MIRROR_NULL);
+    }
+    my $do_config_file = !_prop($attrib_ref, 'no-config-file', $ctx);
+    my @sources;
+    my @bad_step_list;
+    for my $step (@{$m_ctx->get_steps()}) {
+        my $ctx = $m_ctx->get_ctx_of($step);
+        if (    $ctx->get_status() eq $m_ctx->ST_OK
+            &&  $ctx->can('get_dest')
+            &&  -e $ctx->get_dest()
+        ) {
+            if ($do_config_file && !$ctx->can('MIRROR')) {
+                push(@bad_step_list, $step);
+            }
+            else {
+                push(@sources, $ctx->get_dest());
+            }
+        }
+    }
+    if (@bad_step_list) {
+        return $E->throw($E->MIRROR_SOURCE, \@bad_step_list);
+    }
+    for my $action (
+        \&_mirror_mkdir,
+        (@sources        ? \&_mirror             : ()),
+        ($do_config_file ? \&_mirror_config_file : ()),
+        \&_mirror_orig_config_file,
+    ) {
+        $action->($attrib_ref, $m_ctx, $ctx, \@sources);
+    }
+}
+
+# Creates a configuration file at the destination.
+sub _mirror_config_file {
+    my ($attrib_ref, $m_ctx, $ctx, $sources_ref) = @_;
+    my ($target) = _target_and_authority($ctx);
+    my $mirror_m_ctx = FCM::Context::Make->new({dest => '$HERE'});
+    $mirror_m_ctx->set_name(_prop($attrib_ref, 'config-file.name', $ctx));
+    my %no_inherit_from;
+    if (@{$m_ctx->get_inherit_ctx_list()}) {
+        # Inherited destinations
+        for my $i_m_ctx (@{$m_ctx->get_inherit_ctx_list()}) {
+            my $i_ctx = $i_m_ctx->get_ctx_of($ctx->get_id());
+            if (defined($i_ctx)) {
+                push(
+                    @{$mirror_m_ctx->get_inherit_ctx_list()},
+                    FCM::Context::Make->new({dest => $i_ctx->get_target_path()}),
+                );
+            }
+        }
+        # Completed steps, from which the targets can be sourced
+        DONE_STEP:
+        for my $step (@{$m_ctx->get_steps()}) {
+            my $step_ctx = $m_ctx->get_ctx_of($step);
+            if (    !defined($step_ctx)
+                ||  $step_ctx->get_status() ne $m_ctx->ST_OK
+                ||  !$step_ctx->can('get_target_of')
+            ) {
+                next DONE_STEP;
+            }
+            while (my ($key, $target) = each(%{$step_ctx->get_target_of()})) {
+                if (!$target->is_ok()) {
+                    $no_inherit_from{$target->get_ns()} = 1;
+                }
+            }
+        }
+    }
+    # Steps to include in the configuration file
+    for my $step (_props($attrib_ref, 'config-file.steps', $ctx)) {
+        my $step_ctx = $m_ctx->get_ctx_of($step);
+        if (    !defined($step_ctx)
+            ||  $step_ctx->get_status() ne $m_ctx->ST_UNKNOWN
+        ) {
+            return $E->throw(
+                $E->MAKE_PROP_VALUE,
+                [[$ctx->get_id(), 'config-file.steps', q{}, $step]],
+            );
+        }
+        push(@{$mirror_m_ctx->get_steps()}, $step);
+        $mirror_m_ctx->get_ctx_of()->{$step} = dclone($step_ctx);
+        my $mirror_ctx = $mirror_m_ctx->get_ctx_of()->{$step};
+        if ($mirror_ctx->can('get_input_source_of')) {
+            %{$mirror_ctx->get_input_source_of()} = (
+                q{} => [map {abs2rel($_, $m_ctx->get_dest())} @{$sources_ref}],
+            );
+        }
+        if (keys(%no_inherit_from)) {
+            my @no_inherit_from_ns_list = sort keys(%no_inherit_from);
+            push(
+                @no_inherit_from_ns_list,
+                _props($attrib_ref, 'no-inherit-source', $mirror_ctx),
+            );
+            my $prop_value = FCM::Context::Make::Share::Property::Value->new({
+                value => join(
+                    q{ },
+                    map {s{['"\s]}{\\$1}gmsx; $_} @no_inherit_from_ns_list,
+                ),
+            });
+            my $prop = FCM::Context::Make::Share::Property->new({
+                id => 'no-inherit-source',
+                ctx_of => {q{} => $prop_value},
+            });
+            $mirror_ctx->get_prop_of()->{'no-inherit-source'} = $prop;
+        }
+    }
+    # Saves the configuration file
+    my @lines = map {$_ . "\n"}
+        $attrib_ref->{shared_util_of}{config}->unparse($mirror_m_ctx);
+    my $DEST_UTIL = $attrib_ref->{shared_util_of}{dest};
+    my $path = $DEST_UTIL->path(
+        {   'dest' => $ctx->get_dest(),
+            'name' => _prop($attrib_ref, 'config-file.name', $ctx),
+        },
+        'config',
+    );
+    $attrib_ref->{util}->file_save($path, \@lines);
+    _mirror(
+        $attrib_ref, $m_ctx, $ctx,
+        [$path], $DEST_UTIL->path(
+            {   'dest' => $target,
+                'name' => _prop($attrib_ref, 'config-file.name', $ctx),
+            },
+            'config',
+        ),
+    );
+}
+
+# Creates mirror destination.
+sub _mirror_mkdir {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my ($target, $authority, $path) = _target_and_authority($ctx);
+    eval {
+        if ($authority) {
+            my @ssh
+                = (_shell_cmd_list($attrib_ref, 'ssh', $ctx), $authority);
+            if (!file_name_is_absolute($path)) {
+                my $value_hash_ref = _shell($attrib_ref, [@ssh, 'pwd']);
+                my $path_root = $value_hash_ref->{'o'};
+                chomp($path_root);
+                $ctx->set_target_path(rel2abs($path, $path_root));
+            }
+            _shell($attrib_ref, [@ssh, 'mkdir', '-p', $path]);
+        }
+        else {
+            if (!file_name_is_absolute($path)) {
+                $ctx->set_target_path(rel2abs($path));
+            }
+            if (!-d $path) {
+                mkpath($path);
+            }
+        }
+    };
+    if (my $e = $@) {
+        return $E->throw($E->MIRROR_TARGET, $target, $e);
+    }
+    1;
+}
+
+# Mirror original configuration (by unparsing $m_ctx).
+sub _mirror_orig_config_file {
+    my ($attrib_ref, $m_ctx, $ctx) = @_;
+    my @lines = (
+        "# Original fcm make configuration.\n",
+        sprintf("# Generated by %s@%s at %s.\n",
+            scalar(getpwuid($<)),
+            hostname(),
+            strftime("%Y-%m-%dT%H:%M:%S%z", localtime()),
+        ),
+        map {$_ . "\n"} $attrib_ref->{shared_util_of}{config}->unparse($m_ctx),
+    );
+    my $DEST_UTIL = $attrib_ref->{shared_util_of}{dest};
+    my $path = $DEST_UTIL->path(
+        {   'dest' => $ctx->get_dest(),
+            'name' => _prop($attrib_ref, 'config-file.name', $ctx),
+        },
+        'config-orig',
+    );
+    $attrib_ref->{util}->file_save($path, \@lines);
+    my ($target) = _target_and_authority($ctx);
+    _mirror(
+        $attrib_ref, $m_ctx, $ctx,
+        [$path],
+        $DEST_UTIL->path(
+            {   'dest' => $target,
+                'name' => _prop($attrib_ref, 'config-file.name', $ctx),
+            },
+            'config-orig',
+        ),
+    );
+}
+
+# Mirrors.
+sub _mirror {
+    my ($attrib_ref, $m_ctx, $ctx, $sources_ref, $target) = @_;
+    $target ||= (_target_and_authority($ctx))[0];
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->MAKE_MIRROR, $target, @{$sources_ref},
+    );
+    eval {
+        _shell(
+            $attrib_ref,
+            [   _shell_cmd_list($attrib_ref, 'rsync', $ctx),
+                @{$sources_ref},
+                $target,
+            ],
+        );
+    };
+    if (my $e = $@) {
+        return $E->throw($E->MIRROR, [$target, @{$sources_ref}], $e);
+    }
+    1;
+}
+
+# Invokes a known shell command.
+sub _shell {
+    my ($attrib_ref, $command_list_ref) = @_;
+    my $value_hash_ref = $attrib_ref->{util}->shell_simple($command_list_ref);
+    if ($value_hash_ref->{rc}) {
+        return $E->throw(
+            $E->SHELL,
+            {command_list => $command_list_ref, %{$value_hash_ref}},
+            $value_hash_ref->{e},
+        );
+    }
+    $value_hash_ref;
+}
+
+# Returns a shell command and its flags from a named property.
+sub _shell_cmd_list {
+    my ($attrib_ref, $id, $ctx) = @_;
+    map {_props($attrib_ref, $_, $ctx)} ($id, $id . '.flags');
+}
+
+# Returns the authority and the target.
+sub _target_and_authority {
+    my ($ctx) = @_;
+    my $logname = $ctx->get_target_logname();
+    my $machine = $ctx->get_target_machine();
+    my $path    = $ctx->get_target_path();
+    my $authority
+        = $logname && $machine ? $logname . '@' . $machine
+        : $logname             ? $logname . '@' . 'localhost'
+        : $machine             ?                  $machine
+        :                        undef
+        ;
+    my $target = $authority ? $authority . ':' . $path : $path;
+    ($target, $authority, $path);
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Mirror
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Mirror;
+    my $subsystem = FCM::System::Make::Mirror->new(\%attrib);
+    $subsystem->main($m_ctx, $ctx);
+
+=head1 DESCRIPTION
+
+Implements the mirror sub-system. An instance of this class is expected to be
+initialised and called by L<FCM::System::Make|FCM::System::Make>.
+
+=head1 METHODS
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=head1 ATTRIBUTES
+
+The $class->new(\%attrib) method of this class supports the following
+attributes:
+
+=over 4
+
+=item config_parser_of
+
+A HASH to map the labels in a configuration file to their parsers. (default =
+%FCM::System::Make::Mirror::CONFIG_PARSER_OF)
+
+=item prop_of
+
+A HASH to map the names of the properties to their settings. Each setting
+is a 2-element ARRAY reference, where element [0] is the default setting
+and element [1] is a flag to indicate whether the property accepts a name-space
+or not. (default = %FCM::System::Make::Mirror::PROP_OF)
+
+=item shared_util_of
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=item util
+
+See L<FCM::System::Make|FCM::System::Make> for detail.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Preprocess.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Preprocess.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Preprocess.pm	(revision 5095)
@@ -0,0 +1,91 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Preprocess;
+use base qw{FCM::System::Make::Build};
+
+use FCM::System::Make::Build::FileType::CPP;
+use FCM::System::Make::Build::FileType::FPP;
+use FCM::System::Make::Build::FileType::H  ;
+
+# Default target selection
+our %TARGET_SELECT_BY = (
+    'category' => {},
+    'key'      => {},
+    'task'     => {q{} => {'process' => 1}},
+);
+
+# Classes for working with typed source files
+our @FILE_TYPE_UTILS = (
+    'FCM::System::Make::Build::FileType::FPP',
+    'FCM::System::Make::Build::FileType::CPP',
+    'FCM::System::Make::Build::FileType::HPP',
+);
+
+# Default properties
+my %PROP_OF = (
+    'ignore-missing-dep-ns'      => [q{}, undef],
+    'no-step-source'             => [q{}, undef],
+    'no-inherit-source'          => [q{}, undef],
+    'no-inherit-target-category' => [q{}, undef],
+);
+
+# Returns an instance of FCM::System::Make::Build;
+sub new {
+    my ($class, $attrib_ref) = @_;
+    bless(
+        FCM::System::Make::Build->new({
+            target_select_by => {%TARGET_SELECT_BY},
+            file_type_utils  => [@FILE_TYPE_UTILS],
+            prop_of          => {%PROP_OF},
+            %{$attrib_ref},
+        }),
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Preprocess
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Preprocess;
+    my $system = FCM::System::Make::Preprocess->new(\%attrib);
+    $system->main(\%option_of, @args);
+
+=head1 DESCRIPTION
+
+A wrapper of L<FCM::System::Make::Build|FCM::System::Make::Build> with
+configuration to trigger the preprocessing of Fortran and C source files.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
+
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Config.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Config.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Config.pm	(revision 5095)
@@ -0,0 +1,431 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+#-------------------------------------------------------------------------------
+
+package FCM::System::Make::Share::Config;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd};
+use FCM::Context::ConfigEntry;
+use FCM::Context::Locator;
+use FCM::System::Exception;
+use File::Spec::Functions qw{file_name_is_absolute};
+use File::Temp;
+use Text::ParseWords qw{shellwords};
+use Scalar::Util qw{blessed};
+
+# Alias to class name
+my $E = 'FCM::System::Exception';
+
+# Configuration parser label to action map
+my %CONFIG_PARSER_OF = (
+    'description'     => \&_parse_description,
+    'dest'            => \&_parse_dest,
+    'name'            => \&_parse_name,
+    'require-version' => \&_parse_require_version,
+    'step.class'      => \&_parse_step_class,
+    'steps'           => \&_parse_steps,
+    'use'             => \&_parse_use,
+);
+
+__PACKAGE__->class(
+    {shared_util_of => '%', subsystem_of => '%', util => '&'},
+    {action_of => {parse => \&_parse, unparse => \&_unparse}},
+);
+
+# Get configuration file entries from an iterator, and use the entries to
+# populate the context of the current make.
+sub _parse {
+    my ($attrib_ref, $entry_callback_ref, $m_ctx, @args) = @_;
+    my $DEST_UTIL = $attrib_ref->{shared_util_of}{dest};
+    my $dir = $m_ctx->get_option_of('directory')
+        ? $m_ctx->get_option_of('directory') : cwd();
+    my $dir_locator = FCM::Context::Locator->new($dir);
+    my @config_file_paths = $m_ctx->get_option_of('config-file-path')
+        ? @{$m_ctx->get_option_of('config-file-path')} : ();
+    my @config_file_path_locators
+        = map {FCM::Context::Locator->new($_)} @config_file_paths;
+    my @config_file_names = $m_ctx->get_option_of('config-file')
+        ? @{$m_ctx->get_option_of('config-file')} : (undef);
+    my @config_reader_refs;
+    for my $config_file_name (@config_file_names) {
+        my $is_specified_name = 1;
+        if (!defined($config_file_name)) {
+            $config_file_name = $DEST_UTIL->path(
+                {'name' => $m_ctx->get_name()}, 'config');
+            $is_specified_name = 0;
+        }
+        if (    $attrib_ref->{util}->uri_match($config_file_name)
+            ||  file_name_is_absolute($config_file_name)
+        ) {
+            push(@config_reader_refs, _get_config_reader(
+                $attrib_ref, $config_file_name, [@config_file_path_locators],
+            ));
+        }
+        else {  # $config_file_name is relative
+            my $config_reader_ref;
+            HEAD:
+            for my $head_locator ($dir_locator, @config_file_path_locators) {
+                my $locator = $attrib_ref->{util}->loc_cat(
+                    $head_locator, $config_file_name,
+                );
+                if ($attrib_ref->{util}->loc_exists($locator)) {
+                    $config_reader_ref = _get_config_reader(
+                        $attrib_ref, $locator, [@config_file_path_locators],
+                    );
+                    last HEAD;
+                }
+            }
+            if (defined($config_reader_ref)) {
+                push(@config_reader_refs, $config_reader_ref);
+            }
+            elsif ($is_specified_name) {
+                return $E->throw($E->MAKE_CFG_FILE, $config_file_name);
+            }
+        }
+    }
+    if (!@config_reader_refs) {
+        my $config_file_name = $DEST_UTIL->path(
+            {'dest' => $dir_locator->get_value(), 'name' => $m_ctx->get_name()},
+            'config',
+        );
+        if (-f $config_file_name) {
+            push(@config_reader_refs, _get_config_reader(
+                $attrib_ref, $config_file_name, [@config_file_path_locators],
+            ));
+        }
+    }
+    my $args_config_handle;
+    if (@args) {
+        $args_config_handle = File::Temp->new(
+            SUFFIX   => '-fcm-make-args.cfg',
+            TEMPLATE => 'XXXXXX',
+            TMPDIR   => 1,
+        );
+        for my $arg (@args) {
+            print($args_config_handle "$arg\n");
+        }
+        $args_config_handle->seek(0, 0);
+        push(@config_reader_refs, _get_config_reader(
+            $attrib_ref,
+            $args_config_handle->filename(),
+            [@config_file_path_locators],
+        ));
+    }
+    if (!@config_reader_refs) {
+        return $E->throw($E->MAKE_CFG);
+    }
+    my $entry_iter_ref = sub {
+        while (@config_reader_refs) {
+            my $entry = $config_reader_refs[0]->();
+            if (defined($entry)) {
+                return $entry;
+            }
+            shift(@config_reader_refs);
+        }
+        return undef;
+    };
+    my @unknown_entries;
+    while (defined(my $entry = $entry_iter_ref->())) {
+        if (defined($entry_callback_ref)) {
+            $entry_callback_ref->($entry);
+        }
+        if (exists($CONFIG_PARSER_OF{$entry->get_label()})) {
+            $CONFIG_PARSER_OF{$entry->get_label()}->(
+                $attrib_ref, $m_ctx, $entry,
+            );
+        }
+        else {
+            my ($id, $label) = split(qr{\.}msx, $entry->get_label(), 2);
+            if (    $label
+                &&  $label eq 'prop'
+                &&  exists($entry->get_modifier_of()->{'class'})
+                &&  exists($attrib_ref->{subsystem_of}{$id})
+            ) {
+                my $subsystem = $attrib_ref->{subsystem_of}{$id};
+                if (!$subsystem->config_parse_class_prop($entry, $label)) {
+                    push(@unknown_entries, $entry);
+                }
+            }
+            else {
+                my $ctx = $m_ctx->get_ctx_of($id);
+                if (    !defined($ctx)
+                    &&  exists($attrib_ref->{subsystem_of}{$id})
+                ) {
+                    $ctx = $attrib_ref->{subsystem_of}{$id}->ctx($id, $id);
+                    $m_ctx->get_ctx_of()->{$id} = $ctx;
+                }
+                my $rc;
+                if (defined($ctx)) {
+                    my $id_of_class = $ctx->get_id_of_class();
+                    my $subsystem = $attrib_ref->{subsystem_of}{$id_of_class};
+                    $rc = $subsystem->config_parse($ctx, $entry, $label);
+                }
+                if (!$rc) {
+                    push(@unknown_entries, $entry);
+                }
+            }
+        }
+    }
+    if (defined($args_config_handle)) {
+        $args_config_handle->close();
+    }
+    if (@unknown_entries) {
+        return $E->throw($E->CONFIG_UNKNOWN, \@unknown_entries);
+    }
+    $m_ctx;
+}
+
+# Returns a config reader.
+sub _get_config_reader {
+    my ($attrib_ref, $locator, $config_file_path_locators_ref) = @_;
+    if (!blessed($locator)) {
+        $locator = FCM::Context::Locator->new($locator);
+    }
+    $attrib_ref->{util}->config_reader(
+        $locator,
+        {   event_level   => $attrib_ref->{util}->util_of_report()->LOW,
+            include_paths => $config_file_path_locators_ref,
+        },
+    );
+}
+
+# Reads the "description" declaration from a config entry.
+sub _parse_description {
+    my ($attrib_ref, $m_ctx, $entry) = @_;
+    $m_ctx->set_description($entry->get_value());
+}
+
+# Reads the "dest" declaration from a config entry.
+sub _parse_dest {
+    my ($attrib_ref, $m_ctx, $entry) = @_;
+    $m_ctx->set_dest($entry->get_value());
+}
+
+# Reads the "name" declaration from a config entry.
+sub _parse_name {
+    my ($attrib_ref, $m_ctx, $entry) = @_;
+    $m_ctx->set_name($entry->get_value());
+}
+
+# Parse "require-version" declaration from a config entry.
+sub _parse_require_version {
+    my ($attrib_ref, $m_ctx, $entry) = @_;
+    my ($version) = shellwords($attrib_ref->{util}->version());
+    my ($min_version, $max_version) = shellwords($entry->get_value());
+    if (    $min_version gt $version
+        ||  defined($max_version) && $version gt $max_version
+    ) {
+        return $E->throw($E->CONFIG_VERSION, $entry, $version);
+    }
+}
+
+# Reads the step.class declaration from a config entry.
+sub _parse_step_class {
+    my ($attrib_ref, $m_ctx, $entry) = @_;
+    my $id_of_class = $entry->get_value();
+    if (!exists($attrib_ref->{subsystem_of}{$id_of_class})) {
+        return $E->throw($E->CONFIG_VALUE, $entry);
+    }
+    my $subsystem = $attrib_ref->{subsystem_of}{$id_of_class};
+    for my $id (@{$entry->get_ns_list()}) {
+        if (!defined($m_ctx->get_ctx_of($id))) {
+            $m_ctx->get_ctx_of()->{$id} = $subsystem->ctx($id_of_class, $id);
+        }
+    }
+}
+
+# Reads the steps declaration from a config entry.
+sub _parse_steps {
+    my ($attrib_ref, $m_ctx, $entry) = @_;
+    my @steps = $entry->get_values();
+    $m_ctx->set_steps(\@steps);
+    for my $id (@steps) {
+        if (!defined($m_ctx->get_ctx_of($id))) {
+            if (!exists($attrib_ref->{subsystem_of}{$id})) {
+                return $E->throw($E->CONFIG_VALUE, $entry);
+            }
+            my $subsystem = $attrib_ref->{subsystem_of}{$id};
+            $m_ctx->get_ctx_of()->{$id} = $subsystem->ctx($id, $id);
+        }
+    }
+}
+
+# Reads the use declaration.
+sub _parse_use {
+    my ($attrib_ref, $m_ctx, $entry) = @_;
+    my $DEST = $attrib_ref->{shared_util_of}{dest};
+    my $inherit_ctx_list_ref = $m_ctx->get_inherit_ctx_list();
+    for my $value ($entry->get_values()) {
+        $value = $attrib_ref->{util}->file_tilde_expand($value);
+        my $i_m_ctx = eval {$DEST->ctx_load($m_ctx, $value)};
+        if (my $e = $@) {
+            return $E->throw($E->CONFIG_VALUE, $entry, $e);
+        }
+        if (!defined($i_m_ctx) || $i_m_ctx->get_status() != $i_m_ctx->ST_OK) {
+            return $E->throw($E->CONFIG_INHERIT, $entry);
+        }
+        push(@{$m_ctx->get_inherit_ctx_list()}, $i_m_ctx);
+        while (my ($id, $i_ctx) = each(%{$i_m_ctx->get_ctx_of()})) {
+            my $id_of_class = $i_ctx->get_id_of_class();
+            if (exists($attrib_ref->{subsystem_of}{$id_of_class})) {
+                my $subsystem = $attrib_ref->{subsystem_of}{$id_of_class};
+                if (!defined($m_ctx->get_ctx_of($id))) {
+                    $m_ctx->get_ctx_of()->{$id}
+                        = $subsystem->ctx($id_of_class, $id);
+                }
+                if ($subsystem->can('config_parse_inherit_hook')) {
+                    $subsystem->config_parse_inherit_hook(
+                        $m_ctx->get_ctx_of($id), $i_ctx,
+                    );
+                }
+            }
+        }
+        if (!@{$m_ctx->get_steps()}) {
+            $m_ctx->set_steps([@{$i_m_ctx->get_steps()}]);
+        }
+    }
+}
+
+# Turns the context back into a config.
+sub _unparse {
+    my ($attrib_ref, $m_ctx) = @_;
+    my %subsystem_of = map {
+        my $id = $m_ctx->get_ctx_of()->{$_}->get_id_of_class();
+        ($id, $attrib_ref->{subsystem_of}->{$id});
+    } @{$m_ctx->get_steps()};
+    map {$_->as_string()} (
+        (   map {   FCM::Context::ConfigEntry->new({
+                        label   => 'step.class',
+                        ns_list => [$_->get_id()],
+                        value   => $_->get_id_of_class(),
+                    });
+                }
+            grep {$_->get_id() ne $_->get_id_of_class()}
+            values(%{$m_ctx->get_ctx_of()})
+        ),
+        (   map {   my ($action_ref, $label) = @{$_};
+                    my $value = $action_ref->($attrib_ref, $m_ctx);
+                    defined($value)
+                        ? FCM::Context::ConfigEntry->new(
+                            {label => $label, value => $value},
+                        )
+                        : ()
+                    ;
+                }
+            (   [sub {$m_ctx->get_name()}       , 'name'        ],
+                [\&_unparse_use                 , 'use'         ],
+                [\&_unparse_steps               , 'steps'       ],
+                [sub {$m_ctx->get_dest()}       , 'dest'        ],
+                [sub {$m_ctx->get_description()}, 'description' ],
+            ),
+        ),
+        (   map {   my $id = $_;
+                    $subsystem_of{$id}->config_unparse_class_prop($id);
+            }
+            sort keys(%subsystem_of)
+        ),
+        (   map {   my $ctx = $m_ctx->get_ctx_of()->{$_};
+                    my $id_of_class = $ctx->get_id_of_class();
+                    $subsystem_of{$id_of_class}->config_unparse($ctx);
+                }
+            @{$m_ctx->get_steps()}
+        ),
+    );
+}
+
+# Serializes a list of words.
+sub _unparse_join {
+    join(q{ }, map {s{(["'\s])}{\\$1}xms; $_} grep {defined()} @_);
+}
+
+# The value of "steps" declaration from the context.
+sub _unparse_steps {
+    my ($attrib_ref, $m_ctx) = @_;
+    if (!@{$m_ctx->get_steps()}) {
+        return;
+    }
+    _unparse_join(@{$m_ctx->get_steps()});
+}
+
+# The value of "use" declaration from the context.
+sub _unparse_use {
+    my ($attrib_ref, $m_ctx) = @_;
+    if (!@{$m_ctx->get_inherit_ctx_list()}) {
+        return;
+    }
+    my @i_ctx_list = @{$m_ctx->get_inherit_ctx_list()};
+    _unparse_join(map {$_->get_dest()} @i_ctx_list);
+}
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Share::Config
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Share::Config;
+    my $instance = FCM::System::Make::Share::Config->new(\%attrib);
+    my $ok = $instance->parse($m_ctx, $entry_iter_ref);
+    my @entries = $instance->unparse($m_ctx);
+
+=head1 DESCRIPTION
+
+A helper class for (un)parsing make config entries into the make context.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. The allowed elements for %attrib are:
+
+=over 4
+
+=item {shared_util_of}{dest}
+
+A helper object for manipulating the destination in a make context. Expects an
+instance of L<FCM::System::Make::Share::Dest|FCM::System::Make::Share::Dest>.
+
+=back
+
+=item $instance->parse($m_ctx, $entry_iter_ref)
+
+Parses entries returned by the $entry_iter_ref iterator into the $m_ctx.
+Throws a variety of L<FCM::System::Exception|FCM::System::Exception> if some
+data in the configuration file is incorrectly set.
+
+=item $instance->unparse($m_ctx)
+
+Turns $m_ctx back into a list of configuration entries.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Dest.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Dest.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Dest.pm	(revision 5095)
@@ -0,0 +1,503 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Share::Dest;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd};
+use FCM::Context::Event;
+use FCM::System::Exception;
+use File::Basename qw{dirname};
+use File::Path qw{mkpath rmtree};
+use File::Spec::Functions qw{catfile rel2abs};
+use File::Temp;
+use IO::Uncompress::Gunzip qw{gunzip};
+use IO::Compress::Gzip qw{gzip};
+use Scalar::Util qw{blessed reftype};
+use Storable qw{fd_retrieve nstore_fd};
+use Sys::Hostname qw{hostname};
+
+# The relative paths for locating files in a destination
+our %PATH_OF = (
+    'config'                        => 'fcm-make%s.cfg',
+    'config-orig'                   => 'fcm-make%s.cfg.orig',
+    'sys'                           => '.fcm-make%s',
+    'sys-cache'                     => '.fcm-make%s/cache',
+    'sys-config-as-parsed'          => '.fcm-make%s/config-as-parsed.cfg',
+    'sys-config-as-parsed-symlink'  => 'fcm-make%s-as-parsed.cfg',
+    'sys-config-on-success'         => '.fcm-make%s/config-on-success.cfg',
+    'sys-config-on-success-symlink' => 'fcm-make%s-on-success.cfg',
+    'sys-ctx-uncompressed'          => '.fcm-make%s/ctx',
+    'sys-ctx'                       => '.fcm-make%s/ctx.gz',
+    'sys-log'                       => '.fcm-make%s/log',
+    'sys-log-symlink'               => 'fcm-make%s.log',
+    'sys-lock'                      => 'fcm-make%s.lock',
+    'sys-lock-info'                 => 'fcm-make%s.lock/info.txt',
+    'target'                        => '',
+);
+
+# Aliases to exception classes
+my $E = 'FCM::System::Exception';
+# List of actions
+my %ACTION_OF = (
+    ctx_load  => \&_ctx_load,
+    dest_done => \&_dest_done,
+    dest_init => \&_dest_init,
+    path      => \&_path,
+    paths     => \&_paths,
+    path_of   => sub {$_[0]->{'path_of'}{$_[1]}},
+    save      => \&_save,
+    tidy      => \&_tidy,
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   path_of        => {isa => '%', default => {%PATH_OF}},
+        shared_util_of => '%',
+        subsystem_of   => '%',
+        util           => '&',
+    },
+    {action_of => \%ACTION_OF},
+);
+
+# Loads a storable context from a path.
+sub _ctx_load {
+    my ($attrib_ref, $m_ctx, $from) = @_;
+    my $path;
+    my $dest;
+    if ($from) {
+        NAME:
+        for my $name ($m_ctx->get_name(), undef) {
+            $path = _path(
+                $attrib_ref, {'dest' => $from, 'name' => $name}, 'sys-ctx');
+
+            if (-f $path) {
+                $dest = $from;
+                last NAME;
+            }
+        }
+    }
+    else {
+        $path = _path($attrib_ref, $m_ctx, 'sys-ctx');
+        $dest = $m_ctx->get_dest();
+    }
+    my $old_m_ctx = eval {
+        my $handle = File::Temp->new('TMPDIR' => 1);
+        # Open the file here to work around permission problems with file ACLs
+        open(my $path_handle, '<', $path) || die($!);
+        gunzip($path_handle, $handle) || die($!);
+        $handle->seek(0, 0);
+        fd_retrieve($handle);
+    };
+    if (my $e = $@) {
+        return $E->throw($E->CACHE_LOAD, $path, $e);
+    }
+    if (    !$old_m_ctx
+        ||  !$old_m_ctx->isa(blessed($m_ctx))
+        ||  (       defined($old_m_ctx->get_name())
+                &&  $old_m_ctx->get_name() ne $m_ctx->get_name()
+            )
+    ) {
+        return $E->throw($E->CACHE_TYPE, $path);
+    }
+    my $new_m_dest = rel2abs($dest);
+    if ($new_m_dest ne $old_m_ctx->get_dest()) {
+        my $old_m_dest = $old_m_ctx->get_dest();
+        $old_m_ctx->set_dest($new_m_dest);
+        $old_m_ctx->set_dest_lock(undef);
+        SUBSYSTEM:
+        while (my ($id, $old_ctx) = each(%{$old_m_ctx->get_ctx_of()})) {
+            my $id_of_class = $old_ctx->get_id_of_class();
+            if (exists($attrib_ref->{'subsystem_of'}{$id_of_class})) {
+                my $subsystem = $attrib_ref->{'subsystem_of'}{$id_of_class};
+                if (!$old_ctx->can('set_dest')) {
+                    next SUBSYSTEM;
+                }
+                my $old_dest = $old_ctx->get_dest();
+                $old_ctx->set_dest(_path(
+                    $attrib_ref,
+                    {'dest' => $new_m_dest, 'name' => $m_ctx->get_name()},
+                    'target',
+                    $old_ctx->get_id(),
+                ));
+                if ($subsystem->can('ctx_load_hook')) {
+                    $subsystem->ctx_load_hook(
+                        $old_m_ctx, $old_ctx, $old_m_dest, $old_dest);
+                }
+            }
+        }
+    }
+    return $old_m_ctx;
+}
+
+# Finalises the destination of a make context.
+sub _dest_done {
+    my ($attrib_ref, $m_ctx) = @_;
+    if (!$m_ctx->get_dest()) {
+        return;
+    }
+    my $dest = _path($attrib_ref, $m_ctx, 'sys-ctx-uncompressed');
+    my $dest_parent = dirname($dest);
+    my $dest_lock = $m_ctx->get_dest_lock();
+    $m_ctx->set_dest_lock(undef);
+    if (-d $dest_parent) {
+        eval {
+            my $handle = File::Temp->new('TMPDIR' => 1);
+            nstore_fd($m_ctx, $handle) || die($!);
+            $handle->seek(0, 0) || die($!);
+            gzip($handle, _path($attrib_ref, $m_ctx, 'sys-ctx')) || die($!);
+        };
+        if (my $e = $@) {
+            return $E->throw($E->DEST_CREATE, $dest, $e);
+        }
+    }
+    my %ctx_of = %{$m_ctx->get_ctx_of()};
+    for my $path (
+        _path($attrib_ref, $m_ctx, 'sys'),
+        (map {_path($attrib_ref, $m_ctx, 'target', $_)} keys(%ctx_of)),
+    ) {
+        _tidy($attrib_ref, $path);
+    }
+    if ($dest_lock) {
+        rmtree($dest_lock);
+    }
+}
+
+# Initialises the destination of a make context.
+sub _dest_init {
+    my ($attrib_ref, $m_ctx) = @_;
+    my %OPTION_OF = %{$m_ctx->get_option_of()};
+    # Select destination
+    my $dest
+        = $OPTION_OF{directory} ? $OPTION_OF{directory}
+        : $m_ctx->get_dest()    ? $m_ctx->get_dest()
+        :                         cwd()
+        ;
+    $m_ctx->set_dest(rel2abs($dest));
+    # Check lock
+    my $lock = _path($attrib_ref, $m_ctx, 'sys-lock');
+    if (!$OPTION_OF{'ignore-lock'} && -e $lock) {
+        return $E->throw($E->DEST_LOCKED, $lock);
+    }
+    # Creates the lock (and the destination), if necessary
+    if (!-e $lock) {
+        eval {mkpath($lock)};
+        if (my $e = $@) {
+            return $E->throw($E->DEST_CREATE, $lock, $e);
+        }
+        my $lock_info = scalar(getpwuid($<)) . '@' . hostname() .  ':' . $$;
+        _save($attrib_ref, $lock_info, $m_ctx, 'sys-lock-info');
+    }
+    $m_ctx->set_dest_lock($lock);
+    # Cleans items created by previous make, if necessary
+    for my $path (
+        _path($attrib_ref, $m_ctx, 'sys-config-as-parsed-symlink'),
+        _path($attrib_ref, $m_ctx, 'sys-config-on-success-symlink'),
+        _path($attrib_ref, $m_ctx, 'sys-config-on-success'),
+        _path($attrib_ref, $m_ctx, 'sys-log-symlink'),
+    ) {
+        eval {rmtree($path)};
+        if (my $e = $@) {
+            return $E->throw($E->DEST_CLEAN, $path, $e);
+        }
+    }
+    if ($OPTION_OF{new}) {
+        my @steps = @{$m_ctx->get_steps()};
+        for my $path (
+            _path($attrib_ref, $m_ctx, 'sys'),
+            (map {_path($attrib_ref, $m_ctx, 'target', $_)} @steps),
+        ) {
+            eval {rmtree($path)};
+            if (my $e = $@) {
+                return $E->throw($E->DEST_CLEAN, $path, $e);
+            }
+        }
+    }
+    # Loads context of previous make, if possible
+    my $prev_m_ctx = eval {_ctx_load($attrib_ref, $m_ctx)};
+    if (my $e = $@) {
+        if (    !$E->caught($e)
+            ||  !grep {$_ eq $e->get_code()} ($E->CACHE_LOAD, $E->CACHE_TYPE)
+        ) {
+            die($e);
+        }
+        $@ = undef;
+    }
+    if (defined($prev_m_ctx)) {
+        $m_ctx->set_prev_ctx($prev_m_ctx);
+    }
+    else {
+        # Creates the system directory
+        my $sys_dir_path = _path($attrib_ref, $m_ctx, 'sys');
+        eval {mkpath($sys_dir_path)};
+        if (my $e = $@) {
+            return $E->throw($E->DEST_CREATE, $sys_dir_path, $e);
+        }
+    }
+    # Diagnostic
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->MAKE_DEST,
+        $m_ctx, join('@', scalar(getpwuid($<)), hostname()),
+    );
+    1;
+}
+
+# Returns the path of a named item relative to the context destination.
+sub _path {
+    my ($attrib_ref, $m_ctx, $key, @paths) = @_;
+    my %ctx = reftype($m_ctx) && reftype($m_ctx) eq 'HASH'
+        ? %{$m_ctx} : ('dest' => $m_ctx, 'name' => q{});
+    $ctx{'dest'} ||= q{};
+    $ctx{'name'} ||= q{};
+    my $path_of_key = $attrib_ref->{path_of}{$key};
+    catfile(
+        ($ctx{'dest'} ? $ctx{'dest'} : ()),
+        split(
+            q{/},
+            ($path_of_key ? sprintf($path_of_key, $ctx{'name'}) : $path_of_key),
+        ),
+        @paths,
+    );
+}
+
+# Returns an ARRAY reference containing the search paths of a named item
+# relative to the destinations of the context and its inherited contexts.
+sub _paths {
+    my ($attrib_ref, $m_ctx, $key, @paths) = @_;
+    my @dests;
+    my @ctx_list = ($m_ctx);
+    # Adds destinations from inherited contexts recursively
+    # Note: if A inherits from B and C, B from B1 and B2, and C from C1 and C2,
+    #       the search path will be A, C, C2, C1, B, B2, B1.
+    while (my $current_ctx = pop(@ctx_list)) {
+        push(@ctx_list, @{$current_ctx->get_inherit_ctx_list()});
+        push(@dests, _path($attrib_ref, $current_ctx, $key, @paths));
+    }
+    return \@dests;
+}
+
+# Saves $item in a path given by _path($attrib_ref, $m_ctx, $key, @paths).
+sub _save {
+    my ($attrib_ref, $item, $m_ctx, $key, @paths) = @_;
+    my $path = _path($attrib_ref, $m_ctx, $key, @paths);
+    my @contents
+        = (ref($item) && ref($item) eq 'ARRAY') ? (map {$_ . "\n"} @{$item})
+        :                                         ($item . "\n")
+        ;
+    $attrib_ref->{util}->file_save($path, \@contents);
+}
+
+# Removes empty directories in a tree.
+sub _tidy {
+    my ($attrib_ref, @paths) = @_;
+    # Selects only directories which are not symbolic links
+    my @items = map {[$_, undef, undef]} grep {-d && !-l} @paths;
+    while (my $item = pop(@items)) {
+        my ($path, $n_children_ref, $n_siblings_ref) = @{$item};
+        if (!defined($n_children_ref)) {
+            opendir(my $handle, $path)
+                || return $E->throw($E->DEST_CLEAN, $path, $!);
+            my @children = grep {$_ ne q{.} && $_ ne q{..}} (readdir($handle));
+            closedir($handle);
+            $n_children_ref = \scalar(@children);
+            if (@children) {
+                # Descends into directories
+                my @sub_dirs
+                    = grep {-d && !-l} map {catfile($path, $_)} @children;
+                if (@sub_dirs == @children) {
+                    # If all children are directories, it may be possible to
+                    # remove this directory later if all children are empty
+                    push(@items, [$path, $n_children_ref, $n_siblings_ref]);
+                }
+                push(@items, (map {[$_, undef, $n_children_ref]} @sub_dirs));
+            }
+        }
+        if (!${$n_children_ref}) { # i.e. directory is empty
+            rmdir($path) || return $E->throw($E->DEST_CLEAN, $path, $!);
+            if (defined($n_siblings_ref)) {
+                --${$n_siblings_ref};
+            }
+        }
+    }
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Share::Dest
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Share::Dest;
+    my $helper = FCM::System::Make::Share::Dest->new(\%attrib);
+    my $ctx = $helper->ctx_load($path, $expected_class);
+    my $path = $helper->path($m_ctx, $key);
+    # ...
+
+=head1 DESCRIPTION
+
+A helper class for manipulating the destination of a context in a FCM make
+sub-system, e.g. extract.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. The %attrib should contain the following:
+
+=over 4
+
+=item dest_items
+
+An ARRAY containing the names of the items that can be created at the context
+destination.
+
+=item path_of
+
+A HASH to map the (keys) names of the items and (values) their relative paths
+(as ARRAY) in a context destination.
+
+=back
+
+=item $instance->ctx_load($path,$expected_class)
+
+Loads a storable context from $path and returns the context. The $expected_class
+is the expected class of the loaded context. The method die() if it fails to
+load the context or if the loaded context does not belong to the expected class.
+
+=item $instance->dest_done($ctx)
+
+Finalises the destination of $ctx by freezing the $ctx in the system directory,
+removing the lock file, and tidying up any empty directories created by the
+system.
+
+=item $instance->dest_init($ctx)
+
+Initialises the destination of $ctx by checking for a lock directory in the
+destination, creating a lock if possible, cleaning up items created by the
+previous make of the system if necessary, and setting up the system directory.
+
+=item $instance->path($ctx,$key,@paths)
+
+Returns the path of a named item ($key) relative to $ctx, which can either be a
+HASH reference with {'dest' => $dest, 'name' => $name}, or a scalar path
+pointing to $dest, where $dest is the root of the path and $name is the name of
+the context. If @paths are specified, they are concatenated at the end
+of the path.
+
+=item $instance->paths($ctx,$key,@paths)
+
+Returns an ARRAY reference containing the search paths of a named item ($key)
+relative to the destinations of $ctx and its inherited contexts. If @paths are
+specified, they are concatenated at the end of each returned path.
+
+=item $instance->path_of($key)
+
+Returns the template value of the named item in a make destination.
+
+=item $instance->save($item,$ctx,$key,@paths)
+
+Saves $item in a path given by $instance->path($ctx,$key,@paths). $item can be a
+string or a reference to an ARRAY of strings. A "\n" is added to the end of each
+string.
+
+=item $instance->tidy(@paths)
+
+Recursively removes empty directories in @paths.
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item %FCM::System::Make::PATH_OF
+
+A HASH containing the default values of named paths in a make destination. The
+following keys are used by the system:
+
+=over 4
+
+=item config
+
+The standard path to the configuration file.
+
+=item sys
+
+The path to the system directory.
+
+=item sys-cache
+
+The path to the system cache directory.
+
+=item sys-config-as-parsed
+
+The path to the as-parsed configuration file.
+
+=item sys-config-on-success
+
+The path to the on-success configuration file.
+
+=item sys-ctx
+
+The path to the frozen make context (for retrieval by incremental makes).
+
+=item sys-ctx-uncompressed
+
+The path to the uncompressed form of sys-ctx.
+
+=item sys-lock
+
+The path to the lock directory.
+
+=item sys-lock-info
+
+The path to the lock info file.
+
+=item target
+
+The target destination of a make.
+
+=back
+
+=back
+
+=head1 DIAGNOSTICS
+
+=head2 FCM::System::Exception
+
+The methods of this class throws this exception on errors.
+
+=head1 TODO
+
+Time-stamp the as-parsed and the on-success configuration files.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Subsystem.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Subsystem.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Make/Share/Subsystem.pm	(revision 5095)
@@ -0,0 +1,322 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::System::Make::Share::Subsystem;
+use base qw{Exporter};
+
+our @EXPORT = qw{
+    _config_parse
+    _config_parse_class_prop
+    _config_parse_prop
+    _config_parse_inherit_hook_prop
+    _config_unparse_class_prop
+    _config_unparse_join
+    _config_unparse_prop
+    _prop
+    _prop0
+    _props
+};
+
+use FCM::Context::ConfigEntry;
+use FCM::Context::Make::Share::Property;
+use FCM::System::Exception;
+use Storable qw{dclone};
+use Text::ParseWords qw{shellwords};
+
+use constant {PROP_DEFAULT => 0, PROP_NS_OK => 1};
+
+# Aliases
+my $E = 'FCM::System::Exception';
+
+# Parses a configuration entry into the context.
+sub _config_parse {
+    my ($attrib_ref, $ctx, $entry, $label) = @_;
+    my %config_parser_of = (
+        'prop' => \&_config_parse_prop,
+        %{$attrib_ref->{config_parser_of}},
+    );
+    if (!$label || !exists($config_parser_of{$label})) {
+        return;
+    }
+    $config_parser_of{$label}->($attrib_ref, $ctx, $entry);
+    1;
+}
+
+# Parses a configuration entry into the subsystem property.
+sub _config_parse_class_prop {
+    my ($attrib_ref, $entry, $label) = @_;
+    if ($label ne 'prop') {
+        return;
+    }
+    if (@{$entry->get_ns_list()}) {
+        return $E->throw($E->CONFIG_NS, $entry);
+    }
+    my @keys = grep {$_ ne 'class'} keys(%{$entry->get_modifier_of()});
+    if (grep {!exists($attrib_ref->{prop_of}{$_})} @keys) {
+        return $E->throw($E->CONFIG_MODIFIER, $entry);
+    }
+    for my $key (@keys) {
+        $attrib_ref->{prop_of}{$key}[PROP_DEFAULT] = $entry->get_value();
+    }
+    1;
+}
+
+# Reads the ?.prop declaration from a config entry.
+sub _config_parse_prop {
+    my ($attrib_ref, $ctx, $entry) = @_;
+    for my $key (keys(%{$entry->get_modifier_of()})) {
+        my $prop = $ctx->get_prop_of($key);
+        if (!defined($prop)) {
+            if (!defined(_prop_default($attrib_ref, $key))) {
+                return $E->throw($E->CONFIG_MODIFIER, $entry);
+            }
+            $prop = FCM::Context::Make::Share::Property->new({id => $key});
+            $ctx->get_prop_of()->{$key} = $prop;
+        }
+        my $prop_ctx;
+        if (defined($entry->get_value())) {
+            $prop_ctx = $prop->CTX_VALUE->new({value => $entry->get_value()});
+        }
+        if (!@{$entry->get_ns_list()}) {
+            @{$entry->get_ns_list()} = (q{});
+        }
+        for my $ns (@{$entry->get_ns_list()}) {
+            if ($ns && !_prop_ns_ok($attrib_ref, $key)) {
+                return $E->throw($E->CONFIG_NS, $entry);
+            }
+            if (defined($prop_ctx)) {
+                $prop->get_ctx_of()->{$ns} = $prop_ctx;
+            }
+            elsif (exists($prop->get_ctx_of()->{$ns})) {
+                delete($prop->get_ctx_of()->{$ns});
+            }
+        }
+    }
+}
+
+# A hook command for the "inherit/use" declaration, inherit properties.
+sub _config_parse_inherit_hook_prop {
+    my ($attrib_ref, $ctx, $i_ctx) = @_;
+    while (my ($key, $i_prop) = each(%{$i_ctx->get_prop_of()})) {
+        if (!defined($ctx->get_prop_of($key))) {
+            $ctx->get_prop_of()->{$key} = dclone($i_prop);
+        }
+        my %prop_ctx_of = %{$ctx->get_prop_of($key)->get_ctx_of()};
+        while (my ($ns, $i_prop_ctx) = each(%{$i_prop->get_ctx_of()})) {
+            if (    !exists($prop_ctx_of{$ns})
+                ||  $prop_ctx_of{$ns}->get_inherited()
+            ) {
+                my $prop_ctx = dclone($i_prop_ctx);
+                $prop_ctx->set_inherited(1);
+                $ctx->get_prop_of($key)->get_ctx_of()->{$ns} = $prop_ctx;
+            }
+        }
+    }
+}
+
+# Serializes a list of words.
+sub _config_unparse_join {
+    join(
+        q{ },
+        (map {my $s = $_; $s =~ s{(["'\s])}{\\$1}gxms; $s} grep {defined()} @_),
+    );
+}
+
+# Entries of the class prop settings.
+sub _config_unparse_class_prop {
+    my ($attrib_ref, $id) = @_;
+    map {
+        my $key = $_;
+        FCM::Context::ConfigEntry->new({
+            label       => join(q{.}, $id, 'prop'),
+            modifier_of => {'class' => 1, $key => 1},
+            value       => $attrib_ref->{prop_of}{$key}[PROP_DEFAULT],
+        });
+    } sort keys(%{$attrib_ref->{prop_of}});
+}
+
+# Entries of the prop settings.
+sub _config_unparse_prop {
+    my ($attrib_ref, $ctx) = @_;
+    my $label = join(q{.}, $ctx->get_id(), 'prop');
+    my %prop_of = %{$ctx->get_prop_of()};
+    map {
+        my $key = $_;
+        my $setting = $prop_of{$key};
+        map {
+            my $ns = $_;
+            my $prop_ctx = $setting->get_ctx_of()->{$ns};
+            $prop_ctx->get_inherited()
+            ? ()
+            : FCM::Context::ConfigEntry->new({
+                label       => $label,
+                modifier_of => {$key => 1},
+                ns_list     => ($ns ? [$ns] : []),
+                value       => $prop_ctx->get_value(),
+            });
+        } sort(keys(%{$setting->get_ctx_of()}));
+    } sort(keys(%prop_of));
+}
+
+# Returns the value of a named property (for a given $ns).
+sub _prop {
+    my ($attrib_ref, $id, $ctx, $ns) = @_;
+    my $setting = defined($ctx) ? $ctx->get_prop_of()->{$id} : undef;
+    if (!defined($ctx) || !defined($setting)) {
+        return _prop_default($attrib_ref, $id);
+    }
+    if (!_prop_ns_ok($attrib_ref, $id) || !$ns) {
+        my $prop_ctx = $setting->get_ctx();
+        return (
+              defined($prop_ctx) ? $prop_ctx->get_value()
+            :                      _prop_default($attrib_ref, $id)
+        );
+    }
+    my %prop_ctx_of = %{$setting->get_ctx_of()};
+    my $iter_ref
+        = $attrib_ref->{util}->ns_iter($ns, $attrib_ref->{util}->NS_ITER_UP);
+    while (defined(my $item = $iter_ref->())) {
+        if (exists($prop_ctx_of{$item}) && defined($prop_ctx_of{$item})) {
+            return $prop_ctx_of{$item}->get_value();
+        }
+    }
+    return _prop_default($attrib_ref, $id);
+}
+
+# Returns the first non-space value of a $setting for a given $ns.
+sub _prop0 {
+    (_props(@_))[0];
+}
+
+# Returns all suitable values of a $setting for a given $ns.
+sub _props {
+    my $prop = _prop(@_);
+    shellwords($prop ? $prop : q{});
+}
+
+# Returns the default value of a named property.
+sub _prop_default {
+    my ($attrib_ref, $id) = @_;
+    if (!exists($attrib_ref->{prop_of}{$id})) {
+        return;
+    }
+    $attrib_ref->{prop_of}{$id}[PROP_DEFAULT];
+}
+
+# Returns true if the given property can accept a name-space.
+sub _prop_ns_ok {
+    my ($attrib_ref, $id) = @_;
+        exists($attrib_ref->{prop_of}{$id})
+    &&  exists($attrib_ref->{prop_of}{$id}[PROP_NS_OK])
+    &&  $attrib_ref->{prop_of}{$id}[PROP_NS_OK]
+    ;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Make::Share::Subsystem
+
+=head1 SYNOPSIS
+
+    use FCM::System::Make::Share::Subsystem;
+
+=head1 DESCRIPTION
+
+Provides common "local" functions for a make subsystem.
+
+=head1 FUNCTIONS
+
+The following functions are automatically exported by this module.
+
+=over 4
+
+=item _config_parse(\%attrib,$ctx,$entry,$label)
+
+Reads a configuration $entry into the $ctx context. The $label is the label of
+the $entry, but with the prefix (which should be the same as $ctx->get_id() plus
+a dot) removed.
+
+=item _config_parse_class_prop(\%attrib,$entry,$label)
+
+Reads a configuration $entry into the subsystem default property
+$attrib{prop_of}. The $label is the label of the $entry, but with the prefix
+(the subsystem ID plus a dot) removed.
+
+=item _config_parse_prop(\%attrib,$ctx,$entry)
+
+Reads a property configuration $entry into the $ctx context. This method may
+die() with a FCM::System::Exception on error. If the property modifier is
+invalid for the given subsystem, it returns an exception with the CODE
+FCM::System::Exception->CONFIG_MODIFIER. If the property does not support a
+namespace, it returns an exception with the CODE
+FCM::System::Exception->CONFIG_NS.
+
+=item _config_parse_inherit_hook_prop(\%attrib,$ctx,$i_ctx)
+
+The $ctx context is the current subsystem context and the $i_ctx context is the
+inherited subsystem context. Inherits property settings from $i_ctx into $ctx.
+
+=item _config_unparse_join(@list)
+
+Joins the @list into a string that can be parsed again by shellwords.
+
+=item _config_unparse_class_prop(\%attrib,$id)
+
+Turns the default properties in the current subsystem into a list of
+configuration entries. $id is the ID of the current subsystem.
+
+=item _config_unparse_prop(\%attrib,$ctx)
+
+Turns the properties in $ctx into a list of configuration entries.
+
+=item _prop(\%attrib,$id,$ctx,$ns)
+
+Returns the value of property $id. If the property does not exist, it returns
+undef. If the property is not defined in $ctx, it returns the default value. If
+the property is defined in $ctx, it returns the defined value in $ctx. If $ns is
+set and a name-space is allowed for the property, it walks the name-space to
+attempt to return the nearest value of the property for the given name-space.
+
+=item _prop0(\%attrib,$id,$ctx,$ns)
+
+Shorthand for (_props(\%attrib,$id,$ctx,$ns))[0].
+
+=item _props(\%attrib,$id,$ctx,$ns)
+
+Shorthand for shellwords(_prop(\%attrib,$id,$ctx,$ns)).
+
+=back
+
+=head1 DEPENDENCIES
+
+The %attrib argument to the functions in this module may require the following
+keys to be set correctly: {config_parser_of}, {prop_of}, {util}.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Misc.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Misc.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Misc.pm	(revision 5095)
@@ -0,0 +1,345 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Misc;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd};
+use FCM::Context::Event;
+use FCM::Context::Locator;
+use FCM::System::Exception;
+use FCM::Util::ConfigReader;
+use File::Path qw{mkpath rmtree};
+use File::Spec::Functions qw{catfile};
+use List::Util qw{max};
+use Text::ParseWords qw{shellwords};
+
+# The (keys) named actions of this class and (values) their implementations.
+our %ACTION_OF = (
+    browse       => \&_browse,
+    config_parse => \&_config_parse,
+    export_items => \&_export_items,
+    keyword_find => \&_keyword_find,
+);
+# Alias to exception class
+my $E = 'FCM::System::Exception';
+
+# Creates the class.
+__PACKAGE__->class({util => '&'}, {action_of => \%ACTION_OF});
+
+# Launches a web browser to display some version controlled resources.
+sub _browse {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    my $UTIL = $attrib_ref->{util};
+    my @command = shellwords(
+          exists($option_ref->{browser}) ? $option_ref->{browser}
+        :                                  $UTIL->external_cfg_get('browser')
+    );
+    if (!@args) {
+        @args = (cwd());
+    }
+    for my $value (@args) {
+        my $locator = FCM::Context::Locator->new($value);
+        my $url = $UTIL->loc_browser_url($locator);
+        my %value_of = %{$UTIL->shell_simple([@command, $url])};
+        if ($value_of{rc}) {
+            return $E->throw(
+                $E->SHELL,
+                {command_list => [@command, $url], %value_of},
+                $value_of{e},
+            );
+        }
+        $attrib_ref->{util}->event(FCM::Context::Event->OUT, $value_of{o});
+    }
+    return;
+}
+
+# Parses and displays the content of a FCM configuration file.
+sub _config_parse {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    my $reader_attrib_ref;
+    if (exists($option_ref->{'fcm1'})) {
+        $reader_attrib_ref = \%FCM::Util::ConfigReader::FCM1_ATTRIB;
+    }
+    for my $value (@args) {
+        my $locator = FCM::Context::Locator->new($value);
+        my $iter = $attrib_ref->{util}->config_reader(
+            $locator, $reader_attrib_ref,
+        );
+        while (my $entry = $iter->()) {
+            $attrib_ref->{util}->event(
+                FCM::Context::Event->CONFIG_ENTRY,
+                $entry,
+                exists($option_ref->{'fcm1'}),
+            );
+        }
+    }
+    return;
+}
+
+# Exports directories in a project as sequential versioned items.
+sub _export_items {
+    my ($attrib_ref, $option_ref, $location) = @_;
+    if (!$location) {
+        return $E->throw($E->EXPORT_ITEMS_SRC);
+    }
+    $location ||= q{.};
+    my $UTIL = $attrib_ref->{util};
+    # Options and arguments
+    $option_ref->{directory} ||= cwd();
+    $option_ref->{'config-file'} ||= ['fcm-export-items.cfg'];
+    my $locator = FCM::Context::Locator->new($location);
+    $UTIL->loc_as_invariant($locator);
+    # Timer
+    my $time_start = time();
+    my $timer = $UTIL->timer();
+    my %EVENT = (
+        'create' => sub {
+            $UTIL->event(FCM::Context::Event->EXPORT_ITEM_CREATE, @_);
+        },
+        'delete' => sub {
+            $UTIL->event(FCM::Context::Event->EXPORT_ITEM_DELETE, @_);
+        },
+        'timer' => sub {
+            $UTIL->event(
+                FCM::Context::Event->TIMER, 'export-items', $time_start, @_,
+            );
+        },
+    );
+    $EVENT{'timer'}->();
+    # Reads configuration file
+    my $config_reader = $attrib_ref->{util}->config_reader(
+        FCM::Context::Locator->new($option_ref->{'config-file'}->[0]),
+        {   %FCM::Util::ConfigReader::FCM1_ATTRIB,
+            event_level => $attrib_ref->{util}->util_of_report()->LOW,
+        },
+    );
+    my %conditions_of;
+    while (defined(my $entry = $config_reader->())) {
+        # Value: conditions
+        my @conditions;
+        for my $word (shellwords($entry->get_value())) {
+            my ($operator, $rev) = $word =~ qr{\A ([<>]=?|[!=]=) (.+) \z}imsx;
+            if (!$operator || !$rev) {
+                return $E->throw($E->CONFIG_VALUE, $entry);
+            }
+            push(@conditions, $operator . $rev); # FIXME: keyword?
+        }
+        # Label: targets and namespaces
+        my ($target) = $entry->get_label() =~ qr{\A (.+) / \*\z}msx;
+        if ($target) {
+            my $l_target = $UTIL->loc_cat($locator, $target);
+            $UTIL->loc_find(
+                $l_target,
+                sub {
+                    my ($l_child, $attrib_of_child_ref) = @_;
+                    if (!$attrib_of_child_ref->{is_dir}) {
+                        my $ns_of_child = $attrib_of_child_ref->{ns};
+                        my $iter
+                            = $UTIL->ns_iter($ns_of_child, $UTIL->NS_ITER_UP);
+                        $iter->(); # discard
+                        my $ns = $UTIL->ns_cat($target, $iter->());
+                        if (!exists($conditions_of{$ns})) {
+                            $conditions_of{$ns} = \@conditions;
+                        }
+                    }
+                },
+            );
+        }
+        else {
+            $conditions_of{$entry->get_label()} = \@conditions;
+        }
+    }
+    # Export
+    NS:
+    while (my ($ns, $conditions_ref) = each(%conditions_of)) {
+        # FIXME: this should be encapsulated by the locator util.
+        my @command_list = (
+            qw{svn log -q},
+            $UTIL->loc_cat($locator, $ns)->get_value(),
+        );
+        my %value_of = %{$UTIL->shell_simple(\@command_list)};
+        if ($value_of{rc}) {
+            return $E->throw(
+                $E->SHELL,
+                {command_list => \@command_list, %value_of},
+                $value_of{e},
+            );
+        }
+        my @revs = map {($_ =~ qr{\Ar(\d+)})} split("\n", $value_of{o});
+        my %v_of;
+        my $v = 0;
+        for my $rev (reverse(@revs)) {
+            $v_of{$rev} = 'v' . ++$v;
+        }
+        my %cur_v_of = %v_of;
+        # Exports only revisions matching the conditions
+        for my $condition (@{$conditions_ref}) {
+            for my $rev (keys(%cur_v_of)) {
+                if (!eval($rev . $condition)) {
+                    delete($cur_v_of{$rev});
+                }
+            }
+        }
+        # Destination directory
+        my $path = catfile($option_ref->{directory}, $ns);
+        if (-d $path) {
+            if ($option_ref->{new} || !keys(%cur_v_of)) {
+                rmtree($path);
+            }
+            else {
+                # Delete excluded revisions if they exist in incremental mode
+                if (opendir(my $handle, $path)) {
+                    while (my $item = readdir($handle)) {
+                        if (exists($v_of{$item}) && !exists($cur_v_of{$item})) {
+                            for (($item, $v_of{$item})) {
+                                my $p = catfile($path, $_);
+                                rmtree($p);
+                                $EVENT{'delete'}->($ns, $item, $p);
+                            }
+                        }
+                    }
+                    closedir($handle);
+                }
+            }
+        }
+        if (!keys(%cur_v_of)) {
+            next NS;
+        }
+        if (!-d $path) {
+            mkpath($path);
+        }
+
+        # Exports each revision, and creates symlink for each v
+        while (my ($rev, $v) = each(%cur_v_of)) {
+            my $target = catfile($option_ref->{directory}, $ns, $v);
+            if (-l $target || -f $target) {
+                unlink($target);
+                $EVENT{'delete'}->($ns, $v, $target);
+            }
+            if (!-d $target) {
+                my $url_peg_rev = $UTIL->loc_cat($locator, $ns)->get_value();
+                my ($url) = $url_peg_rev =~ qr{\A(.*?)(?:@[^@/]+)?\z}msx;
+                my @command_list = (qw{svn export -q -r}, $rev, $url, $target);
+                my %value_of = %{$UTIL->shell_simple(\@command_list)};
+                if ($value_of{rc} || !-d $target) {
+                    return $E->throw(
+                        $E->SHELL,
+                        {command_list => \@command_list, %value_of},
+                        $value_of{e},
+                    );
+                }
+                $EVENT{'create'}->($ns, $v, $target);
+            }
+            my $link = catfile($option_ref->{directory}, $ns, $rev);
+            if (-e $link && !-l $link) {
+                rmtree($link);
+                $EVENT{'delete'}->($ns, $rev, $link);
+            }
+            elsif (-l $link && readlink($link) ne $v) {
+                unlink($link);
+                $EVENT{'delete'}->($ns, $rev, $link);
+            }
+            if (!-e $link) {
+                symlink($v, $link);
+                $EVENT{'create'}->($ns, $rev, $link);
+            }
+        }
+
+        # Symbolic link to the "latest" version directory
+        my $link_of_latest = catfile($option_ref->{directory}, $ns, 'latest');
+        my $v_of_latest = $cur_v_of{max(keys(%cur_v_of))};
+        if (-e $link_of_latest && !-l $link_of_latest) {
+            rmtree($link_of_latest);
+            $EVENT{'delete'}->($ns, 'latest', $link_of_latest);
+        }
+        elsif (-l $link_of_latest && readlink($link_of_latest) ne $v_of_latest) {
+            unlink($link_of_latest);
+            $EVENT{'delete'}->($ns, 'latest', $link_of_latest);
+        }
+        if (!-l $link_of_latest) {
+            symlink($v_of_latest, $link_of_latest);
+            $EVENT{'create'}->($ns, 'latest', $link_of_latest);
+        }
+    }
+    $EVENT{'timer'}->($timer->());
+}
+
+# Searches FCM keywords.
+sub _keyword_find {
+    my ($attrib_ref, $option_ref, @args) = @_;
+    my $UTIL = $attrib_ref->{util};
+    my @entries;
+    if (@args) {
+        for my $key (@args) {
+            my $iter = $UTIL->loc_kw_iter(FCM::Context::Locator->new($key));
+            while (my $entry = $iter->()) {
+                if (!$entry->get_implied()) {
+                    $UTIL->loc_kw_load_rev_prop($entry);
+                    push(@entries, $entry);
+                }
+            }
+        }
+    }
+    else {
+        @entries = values(%{$UTIL->loc_kw_ctx()->get_entry_by_key()});
+    }
+    for my $entry (sort {$a->get_key() cmp $b->get_key()} @entries) {
+        $UTIL->event(FCM::Context::Event->KEYWORD_ENTRY, $entry);
+    }
+    return;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Misc
+
+=head1 SYNOPSIS
+
+    use FCM::System::Misc;
+    my $system = FCM::System::Misc->new(\%attrib);
+    $system->keyword_find(@args);
+
+=head1 DESCRIPTION
+
+The rest of the FCM system.
+
+=head1 METHODS
+
+Implements the browse(), config_parse(), export_items() and keyword_find()
+methods for L<FCM::System|FCM::System>. See L<FCM::System|FCM::System> for a
+description of the calling interfaces of these functions.
+
+=head1 DIAGNOSTICS
+
+=head2 FCM::System::Exception
+
+Methods of this class may throw a FCM::System::Exception.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Old.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Old.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/System/Old.pm	(revision 5095)
@@ -0,0 +1,142 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::System::Old;
+use base qw{FCM::Class::CODE};
+
+use Cwd qw{cwd};
+use FCM1::Build;
+use FCM1::Config;
+use FCM1::Extract;
+#use FCM1::ExtractConfigComparator;
+use FCM1::Keyword;
+
+my %CLASS_OF = (build => 'FCM1::Build', extract => 'FCM1::Extract');
+
+my %KEY_OF = (
+    'archive'     => 'ARCHIVE',
+    'clean'       => 'CLEAN',
+    'full'        => 'FULL',
+    'ignore-lock' => 'IGNORE_LOCK',
+    'jobs'        => 'JOBS',
+    'stage'       => 'STAGE',
+    'targets'     => 'TARGETS',
+);
+
+__PACKAGE__->class(
+    {util => '&'},
+    {   init => \&_init,
+        action_of => {
+            build          => sub {_run('build', @_)},
+            config_compare => \&_config_compare,
+            extract        => sub {_run('extract', @_)},
+        },
+    },
+);
+
+sub _init {
+    my ($attrib_ref) = @_;
+    if (!defined(FCM1::Keyword::get_util())) {
+        FCM1::Keyword::set_util($attrib_ref->{util});
+    }
+}
+
+sub _config_compare {
+    my ($attrib_ref, $option_hash_ref, @args) = @_;
+    $attrib_ref->{util}->class_load('FCM1::CmUrl');
+    $attrib_ref->{util}->class_load('FCM1::ExtractConfigComparator');
+    if (exists($option_hash_ref->{verbosity})) {
+        FCM1::Config->instance()->verbose($option_hash_ref->{verbosity});
+    }
+    my %option = %{$option_hash_ref};
+    if (exists($option{'wiki-format'})) {
+        $option{'wiki'} = delete($option{'wiki-format'});
+    }
+    my $system = FCM1::ExtractConfigComparator->new({files => \@args, %option});
+    $system->invoke();
+}
+
+sub _run {
+    my ($key, $attrib_ref, $option_hash_ref, @args) = @_;
+    if (exists($option_hash_ref->{targets})) {
+        @{$option_hash_ref->{targets}}
+            = split(qr{:}msx, join(':', @{$option_hash_ref->{targets}}));
+    }
+    if (exists($option_hash_ref->{verbosity})) {
+        FCM1::Config->instance()->verbose($option_hash_ref->{verbosity});
+    }
+    my $system = $CLASS_OF{$key}->new();
+    my $path_to_cfg = @args ? $args[0] : cwd();
+    $system->cfg()->src($path_to_cfg);
+    my %option_of;
+    while (my ($key, $value) = each(%{$option_hash_ref})) {
+        if (exists($KEY_OF{$key})) {
+            $option_of{$KEY_OF{$key}} = $value;
+        }
+    }
+    $system->invoke(%option_of);
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::System::Old
+
+=head1 SYNOPSIS
+
+    use FCM::System::Old;
+    my $system = FCM::System::Old->new();
+    $system->('extract', \%option, \@args);
+
+=head1 DESCRIPTION
+
+Provides a compatibility layer for obsolete FCM 1 functionalities.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new()
+
+Creates and returns an instance of this class.
+
+=item $instance->build(\%option,@args)
+
+Invokes the FCM 1 build system.
+
+=item $instance->config_compare(\%option,@args)
+
+Invokes the FCM 1 cmp-ext-cfg application.
+
+=item $instance->extract(\%option,@args)
+
+Invokes the FCM 1 extract system.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util.pm	(revision 5095)
@@ -0,0 +1,1050 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+
+package FCM::Util;
+use base qw{FCM::Class::CODE};
+
+use Digest::MD5;
+use Digest::SHA;
+use FCM::Context::Event;
+use FCM::Context::Locator;
+use FCM::Util::ConfigReader;
+use FCM::Util::ConfigUpgrade;
+use FCM::Util::Event;
+use FCM::Util::Exception;
+use FCM::Util::Locator;
+use FCM::Util::Reporter;
+use FCM::Util::Shell;
+use FCM::Util::TaskRunner;
+use File::Basename qw{basename dirname};
+use File::Path qw{mkpath};
+use File::Spec::Functions qw{catfile};
+use FindBin;
+use Scalar::Util qw{blessed reftype};
+use Text::ParseWords qw{shellwords};
+use Time::HiRes qw{gettimeofday tv_interval};
+
+use constant {NS_ITER_UP => 1};
+
+# The (keys) named actions of this class and (values) their implementations.
+our %ACTION_OF = (
+    cfg_init             => \&_cfg_init,
+    class_load           => \&_class_load,
+    config_reader        => _util_of_func('config_reader', 'main'),
+    external_cfg_get     => \&_external_cfg_get,
+    event                => \&_event,
+    file_checksum        => \&_file_checksum,
+    file_ext             => \&_file_ext,
+    file_head            => \&_file_head,
+    file_load            => \&_file_load,
+    file_load_handle     => \&_file_load_handle,
+    file_md5             => \&_file_md5,
+    file_save            => \&_file_save,
+    file_tilde_expand    => \&_file_tilde_expand,
+    hash_cmp             => \&_hash_cmp,
+    loc_as_invariant     => _util_of_loc_func('as_invariant'),
+    loc_as_keyword       => _util_of_loc_func('as_keyword'),
+    loc_as_normalised    => _util_of_loc_func('as_normalised'),
+    loc_as_parsed        => _util_of_loc_func('as_parsed'),
+    loc_browser_url      => _util_of_loc_func('browser_url'),
+    loc_cat              => _util_of_loc_func('cat'),
+    loc_dir              => _util_of_loc_func('dir'),
+    loc_export           => _util_of_loc_func('export'),
+    loc_export_ok        => _util_of_loc_func('export_ok'),
+    loc_exists           => _util_of_loc_func('test_exists'),
+    loc_find             => _util_of_loc_func('find'),
+    loc_kw_ctx           => _util_of_loc_func('kw_ctx'),
+    loc_kw_ctx_load      => _util_of_loc_func('kw_ctx_load'),
+    loc_kw_iter          => _util_of_loc_func('kw_iter'),
+    loc_kw_load_rev_prop => _util_of_loc_func('kw_load_rev_prop'),
+    loc_kw_prefix        => _util_of_func('locator', 'kw_prefix'),
+    loc_origin           => _util_of_loc_func('origin'),
+    loc_reader           => _util_of_loc_func('reader'),
+    loc_rel2abs          => _util_of_loc_func('rel2abs'),
+    loc_trunk_at_head    => _util_of_loc_func('trunk_at_head'),
+    loc_what_type        => _util_of_loc_func('what_type'),
+    loc_up_iter          => _util_of_loc_func('up_iter'),
+    ns_cat               => \&_ns_cat,
+    ns_common            => \&_ns_common,
+    ns_in_set            => \&_ns_in_set,
+    ns_iter              => \&_ns_iter,
+    ns_sep               => sub {$_[0]->{ns_sep}},
+    report               => _util_of_func('reporter', 'report'),
+    shell                => _util_of_func('shell', 'invoke'),
+    shell_simple         => _util_of_func('shell', 'invoke_simple'),
+    shell_which          => _util_of_func('shell', 'which'),
+    task_runner          => _util_of_func('task_runner', 'main'),
+    timer                => \&_timer,
+    uri_match            => \&_uri_match,
+    util_of_event        => _util_impl_func('event'),
+    util_of_report       => _util_impl_func('reporter'),
+    version              => \&_version,
+);
+# The default paths to the configuration files.
+our @FCM1_KEYWORD_FILES = (
+    catfile((getpwuid($<))[7], qw{.fcm}),
+);
+our @CONF_PATHS = (
+    catfile($FindBin::Bin, qw{.. etc fcm}),
+    catfile((getpwuid($<))[7], qw{.met-um fcm}),
+    catfile((getpwuid($<))[7], qw{.metomi fcm}),
+);
+our %CFG_BASENAME_OF = (
+    external => 'external.cfg',
+    keyword  => 'keyword.cfg',
+);
+# Values of external commands
+our %EXTERNAL_VALUE_OF = (
+    'browser'       => 'firefox',
+    'diff3'         => 'diff3',
+    'diff3.flags'   => '-E -m',
+    'graphic-diff'  => 'xxdiff',
+    'graphic-merge' => 'xxdiff',
+    'ssh'           => 'ssh',
+    'ssh.flags'     => '-n -oBatchMode=yes',
+    'rsync'         => 'rsync',
+    'rsync.flags'   => '-a --exclude=.* --delete-excluded --timeout=900'
+                       . ' --rsh="ssh -oBatchMode=yes"',
+);
+# The name-space separator
+our $NS_SEP = '/';
+# The (keys) named utilities and their implementation classes.
+our %UTIL_CLASS_OF = (
+    config_reader => 'FCM::Util::ConfigReader',
+    event         => 'FCM::Util::Event',
+    locator       => 'FCM::Util::Locator',
+    reporter      => 'FCM::Util::Reporter',
+    shell         => 'FCM::Util::Shell',
+    task_runner   => 'FCM::Util::TaskRunner',
+);
+
+# Alias
+my $E = 'FCM::Util::Exception';
+
+# Regular expression: match a URI
+my $RE_URI = qr/
+    \A              (?# start)
+    (               (?# capture 1, scheme, start)
+        [A-Za-z]    (?# alpha)
+        [\w\+\-\.]* (?# optional alpha, numeric, plus, minus and dot)
+    )               (?# capture 1, scheme, end)
+    :               (?# colon)
+    (.*)            (?# capture 2, opaque, rest of string)
+    \z              (?# end)
+/xms;
+
+# Creates the class.
+__PACKAGE__->class(
+    {   cfg_basename_of   => {isa => '%', default => {%CFG_BASENAME_OF}},
+        conf_paths        => {isa => '@', default => [@CONF_PATHS]},
+        event             => '&',
+        external_value_of => {isa => '%', default => {%EXTERNAL_VALUE_OF}},
+        ns_sep            => {isa => '$', default => $NS_SEP},
+        util_class_of     => {isa => '%', default => {%UTIL_CLASS_OF}},
+        util_of           => '%',
+    },
+    {init => \&_init, action_of => \%ACTION_OF},
+);
+
+# Initialises attributes.
+sub _init {
+    my ($attrib_ref, $self) = @_;
+    # Initialise the utilities
+    while (my ($key, $util_class) = each(%{$attrib_ref->{util_class_of}})) {
+        if (!defined($attrib_ref->{util_of}{$key})) {
+            _class_load($attrib_ref, $util_class);
+            $attrib_ref->{util_of}{$key} = $util_class->new({util => $self});
+        }
+    }
+    if (exists($ENV{FCM_CONF_PATH})) {
+        $attrib_ref->{conf_paths} = [shellwords($ENV{FCM_CONF_PATH})];
+    }
+}
+
+# Loads the named configuration from its configuration files.
+sub _cfg_init {
+    my ($attrib_ref, $basename, $action_ref) = @_;
+    if (exists($ENV{FCM_CONF_PATH})) {
+        $attrib_ref->{conf_paths} = [shellwords($ENV{FCM_CONF_PATH})];
+    }
+    for my $path (
+        grep {-f} map {catfile($_, $basename)} @{$attrib_ref->{conf_paths}}
+    ) {
+        my $config_reader = $ACTION_OF{config_reader}->(
+            $attrib_ref, FCM::Context::Locator->new($path),
+        );
+        $action_ref->($config_reader);
+    }
+}
+
+# Loads a class/package.
+sub _class_load {
+    my ($attrib_ref, $name, $test_method) = @_;
+    $test_method ||= 'new';
+    if (!UNIVERSAL::can($name, $test_method)) {
+        eval('require ' . $name);
+        if (my $e = $@) {
+            return $E->throw($E->CLASS_LOADER, $name, $e);
+        }
+    }
+    return $name;
+}
+
+# Invokes an event.
+sub _event {
+    my ($attrib_ref, $event, @args) = @_;
+    if (!blessed($event)) {
+        $event = FCM::Context::Event->new({code => $event, args => \@args}),
+    }
+    $attrib_ref->{'util_of'}{'event'}->main($event);
+}
+
+# Returns the value of an external tool.
+{   my $EXTERNAL_CFG_INIT;
+    sub _external_cfg_get {
+        my ($attrib_ref, $key) = @_;
+        my $value_hash_ref = $attrib_ref->{external_value_of};
+        if (!$EXTERNAL_CFG_INIT) {
+            $EXTERNAL_CFG_INIT = 1;
+            _cfg_init(
+                $attrib_ref,
+                $attrib_ref->{cfg_basename_of}{external},
+                sub {
+                    my $config_reader = shift();
+                    while (defined(my $entry = $config_reader->())) {
+                        my $k = $entry->get_label();
+                        if ($k && exists($value_hash_ref->{$k})) {
+                            $value_hash_ref->{$k} = $entry->get_value();
+                        }
+                    }
+                }
+            );
+        }
+        if (!$key || !exists($value_hash_ref->{$key})) {
+            return;
+        }
+        return $value_hash_ref->{$key};
+    }
+}
+
+# Returns the checksum of the content in a file system path.
+sub _file_checksum {
+    my ($attrib_ref, $path, $algorithm) = @_;
+    my $handle = _file_load_handle($attrib_ref, $path);
+    binmode($handle);
+    $algorithm ||= 'md5';
+    my $digest = $algorithm eq 'md5'
+        ? Digest::MD5->new() : Digest::SHA->new($algorithm);
+    $digest->addfile($handle);
+    my $checksum = $digest->hexdigest();
+    close($handle);
+    return $checksum;
+}
+
+# Returns the file extension of a file system path.
+sub _file_ext {
+    my ($attrib_ref, $path) = @_;
+    my $pos_of_dot = rindex($path, q{.});
+    if ($pos_of_dot == -1) {
+        return (wantarray() ? (undef, $path) : undef);
+    }
+    my $ext = substr($path, $pos_of_dot + 1);
+    wantarray() ? ($ext, substr($path, 0, $pos_of_dot)) : $ext;
+}
+
+# Loads the first $n lines from a file system path.
+sub _file_head {
+    my ($attrib_ref, $path, $n) = @_;
+    $n ||= 1;
+    my $handle = _file_load_handle(@_);
+    my $content = q{};
+    for (1 .. $n) {
+        $content .= readline($handle);
+    }
+    close($handle);
+    (wantarray() ? (map {$_ . "\n"} split("\n", $content)) : $content);
+}
+
+# Loads the contents from a file system path.
+sub _file_load {
+    my ($attrib_ref, $path) = @_;
+    my $handle = _file_load_handle(@_);
+    my $content = do {local($/); readline($handle)};
+    close($handle);
+    (wantarray() ? (map {$_ . "\n"} split("\n", $content)) : $content);
+}
+
+# Opens a file handle to read from a file system path.
+sub _file_load_handle {
+    my ($attrib_ref, $path) = @_;
+    open(my($handle), '<', $path) || return $E->throw($E->IO, $path, $!);
+    $handle;
+}
+
+# Returns the MD5 checksum of the content in a file system path.
+sub _file_md5 {
+    my ($attrib_ref, $path) = @_;
+    _file_checksum($attrib_ref, $path, 'md5');
+}
+
+# Saves content to a file system path.
+sub _file_save {
+    my ($attrib_ref, $path, $content) = @_;
+    if (!-e dirname($path)) {
+        eval {mkpath(dirname($path))};
+        if (my $e = $@) {
+            return $E->throw($E->IO, $path, $e);
+        }
+    }
+    open(my($handle), '>', $path) || return $E->throw($E->IO, $path, $!);
+    if (ref($content) && ref($content) eq 'ARRAY') {
+        print($handle @{$content}) || return $E->throw($E->IO, $path, $!);
+    }
+    else {
+        print($handle $content) || return $E->throw($E->IO, $path, $!);
+    }
+    close($handle) || return $E->throw($E->IO, $path, $!);
+}
+
+# Expand leading ~ and ~USER syntax in $path and return the resulting string.
+sub _file_tilde_expand {
+    my ($attrib_ref, $path) = @_;
+    $path =~ s{\A~([^/]*)}{$1 ? (getpwnam($1))[7] : (getpwuid($<))[7]}exms;
+    return $path;
+}
+
+# Compares contents of 2 HASH references.
+sub _hash_cmp {
+    my ($attrib_ref, $hash_1_ref, $hash_2_ref, $keys_only) = @_;
+    my %hash_2 = %{$hash_2_ref};
+    my %modified;
+    while (my ($key, $v1) = each(%{$hash_1_ref})) {
+        if (exists($hash_2{$key})) {
+            my $v2 = $hash_2{$key};
+            if (    !$keys_only
+                &&  (
+                        defined($v1) && defined($v2) && $v1 ne $v2
+                    ||  defined($v1) && !defined($v2)
+                    ||  !defined($v1) && defined($v2)
+                )
+            ) {
+                $modified{$key} = 0;
+            }
+            delete($hash_2{$key});
+        }
+        else {
+            $modified{$key} = -1;
+        }
+    }
+    while (my $key = each(%hash_2)) {
+        if (!exists($hash_1_ref->{$key})) {
+            $modified{$key} = 1;
+        }
+    }
+    return %modified;
+}
+
+# Concatenates 2 name-spaces.
+sub _ns_cat {
+    my ($attrib_ref, @ns_list) = @_;
+    join(
+        $attrib_ref->{ns_sep},
+        grep {$_ && $_ ne $attrib_ref->{ns_sep}} @ns_list,
+    );
+}
+
+# Returns the common parts of 2 name-spaces.
+sub _ns_common {
+    my ($attrib_ref, $ns1, $ns2) = @_;
+    my $iter1 = _ns_iter($attrib_ref, $ns1);
+    my $iter2 = _ns_iter($attrib_ref, $ns2);
+    my $common_ns = q{};
+    while (defined(my $s1 = $iter1->()) && defined(my $s2 = $iter2->())) {
+        if ($s1 ne $s2) {
+            return $common_ns;
+        }
+        $common_ns = $s1;
+    }
+    return $common_ns;
+}
+
+# Returns true if $ns is in one of the name-spaces given by keys(%set).
+sub _ns_in_set {
+    my ($attrib_ref, $ns, $ns_set_ref) = @_;
+    if (!keys(%{$ns_set_ref})) {
+        return;
+    }
+    my @ns_list;
+    my $ns_iter = _ns_iter($attrib_ref, $ns);
+    while (defined(my $n = $ns_iter->())) {
+        push(@ns_list, $n);
+    }
+    grep {exists($ns_set_ref->{$_})} @ns_list;
+}
+
+# Returns an iterator to walk up/down a name-space.
+sub _ns_iter {
+    my ($attrib_ref, $ns, $up) = @_;
+    if ($ns eq $attrib_ref->{ns_sep}) {
+        $ns = q{};
+    }
+    my @give = split($attrib_ref->{ns_sep}, $ns);
+    my @take = ();
+    my $next = q{};
+    if ($up) {
+        @give = reverse(@give);
+        $next = $ns;
+    }
+    sub {
+        my $ret = $next;
+        $next = undef;
+        if (@give) {
+            push(@take, shift(@give));
+            $next = join($attrib_ref->{ns_sep}, ($up ? reverse(@give) : @take));
+        }
+        return $ret;
+    };
+}
+
+# Returns a timer.
+sub _timer {
+    my ($attrib_ref, $start_ref) = @_;
+    $start_ref ||= [gettimeofday()];
+    sub {tv_interval($start_ref)};
+}
+
+# Matches a URI.
+sub _uri_match {
+    my ($attrib_ref, $string) = @_;
+    $string =~ $RE_URI;
+}
+
+# Returns a function to return/set the object in the "util_of" basket.
+sub _util_impl_func {
+    my ($id) = @_;
+    sub {
+        my ($attrib_ref, $value) = @_;
+        if (defined($value) && ref($value) && reftype($value) eq 'CODE') {
+            $attrib_ref->{'util_of'}{$id} = $value;
+        }
+        $attrib_ref->{'util_of'}{$id};
+    };
+}
+
+# Returns a function to delegate a method to a utility in the "util_of" basket.
+sub _util_of_func {
+    my ($id, $method) = @_;
+    sub {
+        my $attrib_ref = shift();
+        $attrib_ref->{util_of}{$id}->(($method ? ($method) : ()), @_);
+    };
+}
+
+# Returns a function to delegate a method to the locator utility.
+{   my $KEYWORD_CFG_INIT;
+    sub _util_of_loc_func {
+        my ($method) = @_;
+        sub {
+            my $attrib_ref = shift();
+            if (!$KEYWORD_CFG_INIT) {
+                $KEYWORD_CFG_INIT = 1;
+                my $config_upgrade = FCM::Util::ConfigUpgrade->new();
+                for my $path (grep {-f} @FCM1_KEYWORD_FILES) {
+                    my $config_reader = $ACTION_OF{config_reader}->(
+                        $attrib_ref,
+                        FCM::Context::Locator->new($path),
+                        \%FCM::Util::ConfigReader::FCM1_ATTRIB,
+                    );
+                    $ACTION_OF{loc_kw_ctx_load}->(
+                        $attrib_ref,
+                        sub {$config_upgrade->upgrade($config_reader->())},
+                    );
+                }
+                _cfg_init(
+                    $attrib_ref,
+                    $attrib_ref->{cfg_basename_of}{keyword},
+                    sub {$ACTION_OF{loc_kw_ctx_load}->($attrib_ref, @_)},
+                );
+            }
+            $attrib_ref->{util_of}{locator}->($method, @_);
+        };
+    }
+}
+
+# Returns the FCM version string.
+{   my $FCM_VERSION;
+    sub _version {
+        my ($attrib_ref) = @_;
+        if (!defined($FCM_VERSION)) {
+            my $fcm_home = dirname($FindBin::Bin);
+            # Try "git describe"
+            my $value_hash_ref = eval {
+                $ACTION_OF{shell_simple}->(
+                    $attrib_ref,
+                    ['git', "--git-dir=$FindBin::Bin/../.git", 'describe'],
+                );
+            };
+            if (my $e = $@) {
+                if (!$E->caught($e)) {
+                    die($e);
+                }
+                $@ = undef;
+            }
+            my $version;
+            if ($value_hash_ref->{o} && !$value_hash_ref->{rc}) {
+                chomp($value_hash_ref->{o});
+                $version = $value_hash_ref->{o};
+            }
+            else {
+                # Read fcm-version.js file
+                my $path = catfile($fcm_home, qw{doc etc fcm-version.js});
+                open(my($handle), '<', $path) || die("$path: $!");
+                my $content = do {local($/); readline($handle)};
+                close($handle);
+                ($version) = $content =~ qr{\AFCM\.VERSION="(.*)";}msx;
+            }
+            $FCM_VERSION = sprintf("%s (%s)", $version, $fcm_home);
+        }
+        return $FCM_VERSION;
+    }
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util
+
+=head1 SYNOPSIS
+
+    use FCM::Util;
+    $u = FCM::Util->new();
+    $u->class_load('Foo');
+
+=head1 DESCRIPTION
+
+Utilities used by the FCM system.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. The %attrib hash can be used configure the behaviour of
+the instance:
+
+=over 4
+
+=item conf_paths
+
+The search paths to the configuration files. The default is the value in
+@FCM::Util::CONF_PATHS.
+
+=item cfg_basename_of
+
+A HASH to map the named configuration with the base names of their paths.
+(default=%CFG_BASENAME_OF)
+
+=item external_value_of
+
+A HASH to map the named external tools with their default values.
+(default=%EXTERNAL_VALUE_OF)
+
+=item event
+
+A CODE to handle event.
+
+=item ns_sep
+
+The name space separator. (default=/)
+
+=item util_class_of
+
+A HASH to map (keys) utility names to (values) their implementation classes. See
+%FCM::System::UTIL_CLASS_OF.
+
+=item util_of
+
+A HASH to map (keys) utility names to (values) their implementation instances.
+
+=back
+
+=item $u->cfg_init($basename,\&action)
+
+Search site/user configuration given by $basename. Invoke the callback
+&action($config_reader) for each configuration file found.
+
+=item $u->class_load($name,$test_method)
+
+If $name can call $test_method, returns $name. (If $test_method is not defined,
+the default is "new".) Otherwise, calls require($name). Returns $name.
+
+=item $u->config_reader($locator,\%reader_attrib)
+
+Returns an iterator for getting the configuration entries from $locator (which
+should be an instance of L<FCM::Context::Locator|FCM::Context::Locator>.
+
+The iterator returns the next useful entry of the configuration file as an
+object of L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry>. It returns
+under if there is no more useful entry to return.
+
+The %reader_attrib may be used to override the default attributes. The HASH
+should contain a {parser} and a {processor}. The {parser} is a CODE reference to
+parse a declaration in the configuration file into an entry. The {processor} is
+a CODE reference to process the entry. If the {processor} returns true, the
+entry is considered a special entry (e.g. a variable declaration or an
+C<include> declaration) that is processed, and will not be returned by the
+iterator.
+
+The %reader_attrib can be defined using the following pre-defined sets:
+
+=over 4
+
+=item %FCM::Util::ConfigReader::FCM1_ATTRIB
+
+Using this will generate a reader for configuration files written in the FCM 1
+format.
+
+=item %FCM::Util::ConfigReader::FCM2_ATTRIB
+
+Using this will generate a reader for configuration files written in the FCM 2
+format. (default)
+
+=back
+
+In addition, $reader_attrib{event_level} can be used to adjust the event
+verbosity level.
+
+The parser and the processor are called with a %state, which contains the
+current state of the reader, and has the following elements:
+
+=over 4
+
+=item cont
+
+This is set to true if there is a continue marker at the end of the current
+line. The next line should be parsed as part of the current context.
+
+=item ctx
+
+The context of the current entry, which should be an instance of
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry>.
+
+=item line
+
+The content of the current line.
+
+=item stack
+
+An ARRAY reference that represents an include stack. The top of the stack
+(the final element) represents the most current file being read. An include file
+will be put on top of the stack, and removed when EOF is reached. When the stack
+is empty, the iterator is exhausted.
+
+Each element of the stack is an 4-element ARRAY reference. Element 1 is the
+L<FCM::Context::Locator|FCM::Context::Locator> object that represents the
+current file. Element 2 is the line number of the current file. Element 3 is the
+file handle for reading the current file. Element 4 is a CODE reference with an
+interface $f->($path), for turning $path from a relative location under the
+container of the current file into an absolute location.
+
+=item var
+
+A HASH reference containing the variables (from the environment and local to the
+configuration file) that can be used for substitution.
+
+=back
+
+=item $u->external_cfg_get($key)
+
+Returns the value of a named tool.
+
+=item $u->event($event,@args)
+
+Raises an event. The 1st argument $event can either be a blessed reference of
+L<FCM::Context::Event|FCM::Context::Event> or a valid event code. If the former
+is true, @args is not used, otherwise, @args should be the event arguments for
+the specified event code.
+
+=item $u->file_checksum($path, $algorithm)
+
+Returns the checksum of $path. If $algorithm is not specified, the default
+algorithm to use is MD5. Otherwise, any algorithm supported by Perl's
+Digest::SHA module can be used.
+
+=item $u->file_ext($path)
+
+Returns file extension of $path. E.g.:
+
+    my $path = '/foo/bar.baz';
+    my $extension = $u->file_ext($path); # 'baz'
+    my ($extension, $root) = $u->file_ext($path); # ('baz', '/foo/bar')
+
+=item $u->file_head($path, $n)
+
+Loads $n lines (or 1 line if $n not specified) from a $path in the file system.
+In scalar context, returns the content in a scalar. In list context, separate
+the content by the new line character "\n", and returns the resulting list.
+
+=item $u->file_load($path)
+
+Loads contents from a $path in the file system. In scalar context, returns the
+content in a scalar. In list context, separate the content by the new line
+character "\n", and returns the resulting list.
+
+=item $u->file_load_handle($path)
+
+Returns a file handle for loading contents from $path.
+
+=item $u->file_md5($path)
+
+Deprecated. Equivalent to $u->file_checksum($path, 'md5').
+
+=item $u->file_save($path, $content)
+
+Saves $content to a $path in the file system.
+
+=item $u->file_tilde_expand($path)
+
+Expand any leading "~" or "~USER" syntax to the HOME directory of the current
+user or the HOME directory of USER. Return the modified string.
+
+=item $u->hash_cmp(\%hash_1,\%hash_2,$keys_only)
+
+Compares the contents of 2 HASH references. If $keys_only is specified, only
+compares the keys. Returns a HASH where each element represents a difference
+between %hash_1 and %hash_2 - if the value is positive, the key exists in
+%hash_2 but not %hash_1, if the value is negative, the key exists in %hash_1 but
+not %hash_2, and if the value is zero, the key exists in both, but the values
+are different.
+
+=item $u->loc_as_invariant($locator)
+
+If the $locator->get_value_level() is below FCM::Context::Locator->L_INVARIANT,
+determines the invariant value of $locator, and sets its value to the result.
+Returns $locator->get_value(). 
+
+See L<FCM::Context::Locator|FCM::Context::Locator> for information on locator
+value level.
+
+=item $u->loc_as_keyword($locator)
+
+Calls $u->loc_as_normalised($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_NORMALISED. Returns the value of the locator as an FCM
+keyword, where possible.
+
+=item $u->loc_as_normalised($locator)
+
+If the $locator->get_value_level() is below FCM::Context::Locator->L_NORMALISED,
+determines the normalised value of $locator, and sets its value to the result.
+Returns $locator->get_value().
+
+See L<FCM::Context::Locator|FCM::Context::Locator> for information on locator
+value level.
+
+=item $u->loc_as_parsed($locator)
+
+If the $locator->get_value_level() is below FCM::Context::Locator->L_PARSED,
+determines the parsed value of $locator, and sets its value to the result.
+Returns $locator->get_value().
+
+See L<FCM::Context::Locator|FCM::Context::Locator> for information on locator
+value level.
+
+=item $u->loc_browser_url($locator)
+
+Calls $u->loc_as_normalised($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_NORMALISED. Returns the value of the locator as a
+browser URL, where possible.
+
+=item $u->loc_cat($locator,@paths)
+
+Calls $u->loc_as_parsed($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_PARSED. Concatenates the value of the $locator with the
+given @paths according to the $locator type. Returns a new FCM::Context::Locator
+that represents the concatenated value.
+
+=item $u->loc_dir($locator)
+
+Calls $u->loc_as_parsed($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_PARSED. Determines the "directory" name of the value of
+the $locator according to the $locator type. Returns a new FCM::Context::Locator
+that represents the resulting value.
+
+=item $u->loc_exists($locator)
+
+Calls $u->loc_as_normalised($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_NORMALISED. Return a true value if the location
+represented by $locator exists.
+
+=item $u->loc_export($locator,$dest)
+
+Calls $u->loc_as_normalised($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_NORMALISED. Exports the file or directory tree
+represented by $locator to a file system $dest.
+
+=item $u->loc_export_ok($locator)
+
+Calls $u->loc_as_parsed($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_PARSED. Returns true if it is possible and safe to
+call $u->loc_export($locator).
+
+=item $u->loc_find($locator,\&callback)
+
+Searches the directory tree of $locator. Invokes &callback for each node with
+the following interface:
+
+    $callback_ref->($locator_of_child_node, \%target_attrib);
+
+where %target_attrib contains the keys:
+
+=over 4
+
+=item {is_dir}
+
+This is set to true if the child node is a directory.
+
+=item {last_modified_rev}
+
+This is set to the last modified revision of the child node, if relevant.
+
+=item {last_modified_time}
+
+This is set to the last modified time of the child node.
+
+=item {ns}
+
+This is set to the relative name-space (i.e. the relative path) of the child
+node.
+
+=back
+
+=item $u->loc_kw_ctx()
+
+Returns the keyword context (an instance of FCM::Context::Keyword).
+
+=item $u->loc_kw_ctx_load(@config_entry_iterators)
+
+Loads configuration entries into the keyword context. The
+@config_entry_iterators should be a list of CODE references, with the following
+calling interfaces:
+
+    while (my $config_entry = $config_entry_iterator->()) {
+        # ... $config_entry should be an instance of FCM::Context::ConfigEntry
+    }
+
+=item $u->loc_kw_iter($locator)
+
+Returns an iterator. When called, the iterator returns location keyword entry
+context (as an instance of
+L<FCM::Context::Keyword::Entry::Location|FCM::Context::Keyword>) for $locator
+until exhausted.
+
+    my $iterator = $u->loc_kw_iter($locator)
+    while (my $kw_ctx_entry = $iterator->()) {
+        # ... do something with $kw_ctx_entry
+    }
+
+=item $u->loc_kw_load_rev_prop($entry)
+
+Loads the revision keywords to $entry
+(L<FCM::Context::Keyword::Entry::Location|FCM::Context::Keyword>), assuming that
+$entry is not an implied location keyword, and that the keyword locator points
+to a VCS location that supports setting up revision keywords in properties.
+
+=item $u->loc_kw_prefix()
+
+Returns the prefix of a FCM keyword. This should be "fcm".
+
+=item $u->loc_origin($locator)
+
+Calls $u->loc_as_parsed($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_PARSED. Determines the origin of $locator, and returns
+a new FCM::Context::Locator that represents the result. E.g. if $locator points
+to a Subversion working copy, it returns a new locator that represents the URL
+of the working copy.
+
+=item $u->loc_reader($locator)
+
+Calls $u->loc_as_normalised($locator) if $locator->get_value_level() is below
+FCM::Context::Locator->L_NORMALISED. Returns a file handle for reading the
+content from $locator.
+
+=item $u->loc_rel2abs($locator,$locator_base)
+
+If the value of $locator is a relative path, sets it to an absolute path base on
+the $locator_base, provided that $locator and $locator_base is the same type.
+
+=item $u->loc_trunk_at_head($locator)
+
+Returns a string to represent the relative path to the latest main tree, if it
+is relevant for $locator.
+
+=item $u->loc_what_type($locator)
+
+Sets $locator->get_type() and returns its value. Currently, this can either be
+"svn" for a locator pointing to a Subversion resource or "fs" for a locator
+pointing to a file system resource.
+
+=item $u->loc_up_iter($locator)
+
+Returns an iterator that walks up the hierarchy of the $locator, according to
+its type.
+
+=item $u->ns_cat(@name_spaces)
+
+Concatenates name-spaces and returns the result.
+
+=item $u->ns_common($ns1,$ns2)
+
+Returns the common parts of 2 name-spaces. For example, if $ns1 is
+"egg/ham/bacon" and $ns2 is "egg/ham/sausage", it should return "egg/ham".
+
+=item $u->ns_in_set($ns,\%set)
+
+Returns true if $ns is in a name-space given by the keys of %set.
+
+=item $u->ns_iter($ns,$up)
+
+Returns an iterator that walks up or down a name-space. E.g.:
+
+    $iter_ref = $u->ns_iter('a/bee/cee', $u->NS_ITER_UP);
+    while (defined(my $item = $iter_ref->())) {
+        print("[$item]");
+    }
+    # should print: [a/bee/cee][a/bee][a][]
+
+    $iter_ref = $u->ns_iter('a/bee/cee');
+    while (defined(my $item = $iter_ref->())) {
+        print("[$item]");
+    }
+    # should print: [][a][a/bee][a/bee/cee]
+
+=item $u->ns_sep()
+
+Returns the name-space separator, (i.e. normally "/").
+
+=item $u->report(\%option,$message)
+
+Reports messages using $u->util_of_report(). The default is an instance of
+L<FCM::Util::Reporter|FCM::Util::Reporter>. See
+L<FCM::Util::Reporter|FCM::Util::Reporter> for detail.
+
+=item $u->shell($command,\%action_of)
+
+Invokes the $command, which can be scalar or a reference to an ARRAY. If a
+scalar is specified, it will be separated into an array using the shellwords()
+function in L<Text::ParseWords|Text::ParseWords>. If it is a reference to an
+ARRAY, the ARRAY will be passed to open3() as is.
+
+The %action_of should contain the actions for i: standard input, e: standard
+error output and o: standard output. The default for each of these is an
+anonymous subroutinue that does nothing.
+
+Each time the pipe to the child standard input is available for writing, it will
+call $action_of{i}->(). If it returns a defined value, the value will be written
+to the pipe. If it returns undef, the pipe will be closed.
+
+Each time the pipe from the child standard (error) output is available for
+reading, it will read some values to a buffer, and invoke the callback
+$action_of{o}->($buffer) (or $action_of{e}->($buffer)). The return value of the
+callback will be ignored.
+
+On normal completion, it returns the status code of the command and raises an
+FCM::Context::Event->SHELL event:
+
+Any abnormal failure will cause an instance of FCM::Util::Exception to be
+thrown. (The return of a non-zero status code by the child is considered a
+normal completion.)
+
+=item $u->shell_simple($command)
+
+Wraps $u->shell(), and returns a HASH reference containing {e} (the
+standard error), {o} (the standard output) and {rc} (the return code).
+
+=item $u->shell_which($name)
+
+Returns the full path of an executable command $name if it can be found in the
+system PATH.
+
+=item $u->task_runner($action_code_ref,$n_workers)
+
+Returns a runner of tasks. It can be configured to work in serial (default) or
+parallel. The runner has the following methods:
+
+    $n_done = $runner->main($get_code_ref,$put_code_ref);
+    $runner->destroy();
+
+For each $task (L<FCM::Context::Task|FCM::Context::Task>) returned by the
+$get_code_ref->() iterator, invokes $action_ref->($task->get_ctx()). When
+$action_ref returns, send the $task back to the caller by calling
+$put_code_ref->($task). When it is done, the runner returns the number of tasks
+it has done.
+
+The $runner->destroy() method should be called to destroy the $runner when it is
+not longer used.
+
+=item $u->timer(\@start)
+
+Returns a CODE reference, which can be called to return the elapsed time. The
+@start argument is optional. If specified, it should be in a format as returned
+by Time::HiRes::gettimeofday(). If not specified, the current gettimeofday() is
+used.
+
+=item $u->uri_match($string)
+
+Returns true if $string is a URI. In array context, returns the scheme and the
+opague part of the URI if $string is a URI, or an empty list otherwise.
+
+=item $u->util_of_event($value)
+
+Returns and/or sets the L<FCM::Util::Event|FCM::Util::Event> object that is used
+to handle the $u->report() method.
+
+=item $u->util_of_report($value)
+
+Returns and/or sets the L<FCM::Util::Reporter|FCM::Util::Reporter> object that
+is used to handle the $u->report() method.
+
+=item $u->version()
+
+Returns the FCM version string in the form C<VERSION (BIN)> where VERSION is the
+version string returned by "git describe" or the version file and BIN is
+absolute path of the "fcm" command.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=head2 FCM::Util::Exception
+
+This exception is a sub-class of L<FCM::Exception|FCM::Exception> and is thrown
+by methods of this class on error.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/ConfigReader.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/ConfigReader.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/ConfigReader.pm	(revision 5095)
@@ -0,0 +1,609 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::ConfigReader;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::ConfigEntry;
+use FCM::Context::Event;
+use FCM::Context::Locator;
+use FCM::Util::Exception;
+use File::Spec::Functions qw{file_name_is_absolute};
+use Text::Balanced   qw{extract_bracketed};
+use Text::ParseWords qw{parse_line shellwords};
+
+# Alias
+our $UTIL;
+# Alias to exception class
+my $E = 'FCM::Util::Exception';
+# The variable name, which means the container of the current configuration file
+my $HERE = 'HERE';
+# Element indices in a stack item
+my ($I_LOCATOR, $I_LINE_NUM, $I_HANDLE, $I_HERE_LOCATOR) = (0 .. 3);
+# Patterns for extracting/matching strings
+my %PATTERN_OF = (
+    # Config: comment delimiter, e.g. "... #comment"
+    comment => qr/\s+ \#/xms,
+    # Config: continue, start of next line
+    cont_next => qr/
+        \A (.*?)        (?# start and capture 1, shortest of anything)
+        (\\*)           (?# capture 2, a number of backslashes)
+        \s* \z          (?# optional space until the end)
+    /xms,
+    # Config: continue, end of previous line
+    cont_prev => qr/\A \s* \\? (.*) \z/xms,
+    # Config: removal of the assignment operator at start of string
+    fcm2_equal => qr/
+        \A \s*          (?# start and optional spaces)
+        (=)             (?# capture 1, equal sign)
+        (.*) \z         (?# capture 2, rest of string)
+    /xms,
+    # Config: label of an inc statement
+    fcm1_include => qr/\Ainc\z/ixms,
+    # Config: label of an include statement
+    fcm2_include => qr/\Ainclude\z/ixms,
+    # Config: label of an include-path statement
+    fcm2_include_path => qr/\Ainclude-path\z/ixms,
+    # Config: label
+    fcm2_label => qr/
+        \A \s*          (?# start and optional spaces)
+        (\$?[\w\-\.]+)  (?# capture 1, optional dollar, then valid label)
+        (.*) \z         (?# capture 2, rest of string)
+    /xms,
+    # Config: a variable identifier in a value, e.g. "... ${var}", "$var"
+    fcm1_var => qr/
+        \A              (?# start)
+        (.*?)           (?# capture 1, shortest of anything)
+        ([\$\%])        (?# capture 2, variable sigil, dollar or percent)
+        (\{)?           (?# capture 3, curly brace start, optional)
+        ([A-z]\w+(?:::[A-z]\w+)*) (?# capture 4, variable name)
+        ((?(3)\}))      (?# capture 5, curly brace end, if started in capture 4)
+        (.*)            (?# capture 6, rest of string)
+        \z              (?# end)
+    /xms,
+    # Config: a variable identifier in a value, e.g. "... ${var}", "$var"
+    fcm2_var => qr/
+        \A              (?# start)
+        (.*?)           (?# capture 1, shortest of anything)
+        (\\*)           (?# capture 2, escapes)
+        (\$)            (?# capture 3, variable sigil, dollar)
+        (\{)?           (?# capture 4, curly brace start, optional)
+        ([A-z]\w+)      (?# capture 5, variable name)
+        ((?(4)\}))      (?# capture 6, curly brace end, if started in capture 4)
+        (.*)            (?# capture 7, rest of string)
+        \z              (?# end)
+    /xms,
+    # Config: a $HERE, ${HERE} in the beginning of a string
+    here => qr/
+        \A                  (?# start)
+        (\$HERE|\$\{HERE\}) (?# capture 1, \$HERE)
+        (\/.*)?             (?# capture 2, rest of string)
+        \z                  (?# end)
+    /xms,
+    # Config: an empty or comment line
+    ignore => qr/\A \s* (?:\#|\z)/xms,
+    # Config: comma separator
+    delim_csv => qr/\s*,\s*/xms,
+    # Config: modifier key:value separator
+    delim_mod => qr/\s*:\s*/xms,
+    # A variable name
+    var_name => qr/\A [A-Za-z_]\w* \z/xms,
+    # Config: trim value
+    trim => qr/\A \s* (.*?) \s* \z/xms,
+    # Config: trim value within braces
+    trim_brace => qr/\A [\[\{] \s* (.*?) \s* [\]\}] \z/xms,
+);
+# Default (post-)processors for a configuration entry
+our %FCM1_ATTRIB = (
+    parser    => _parse_func(\&_parse_fcm1_label, \&_parse_fcm1_var),
+    processor => sub {
+        _process_assign_func('%')->(@_)
+        ||
+        _process_include_func('fcm1_include')->(@_)
+        ||
+        _process_fcm1_label(@_)
+        ;
+    },
+);
+# Default (post-)processors for a configuration entry
+our %FCM2_ATTRIB = (
+    parser    => _parse_func(\&_parse_fcm2_label, \&_parse_fcm2_var),
+    processor => sub {
+        _process_assign_func('$', '?')->(@_)
+        ||
+        _process_include_path_func('fcm2_include_path')->(@_)
+        ||
+        _process_include_func('fcm2_include')->(@_)
+        ;
+    },
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   event_level => '$',
+        parser      => {isa => '&', default => sub {$FCM2_ATTRIB{parser}}   },
+        processor   => {isa => '&', default => sub {$FCM2_ATTRIB{processor}}},
+        util        => '&',
+    },
+    {action_of => {main => \&_main}},
+);
+
+# Returns a configuration reader.
+sub _main {
+    my ($attrib_ref, $locator, $reader_attrib_ref) = @_;
+    if (!defined($locator)) {
+        return;
+    }
+    my %reader_attrib
+        = defined($reader_attrib_ref) ? %{$reader_attrib_ref} : ();
+    my @include_paths = exists($reader_attrib{include_paths})
+        ?  @{$reader_attrib{include_paths}} : ();
+    my %state = (
+        cont  => undef,
+        ctx   => undef,
+        line  => undef,
+        include_paths => \@include_paths,
+        stack => [[$locator, 0]],
+        var   => {},
+    );
+    my %attrib = (%{$attrib_ref}, %reader_attrib);
+    sub {_read(\%attrib, \%state)};
+}
+
+# Returns a parser for a configuration line (FCM 1 or FCM 2 format).
+sub _parse_func {
+    my ($parse_label_func, $parse_var_func) = @_;
+    sub {
+        my ($state_ref) = @_;
+        my $line
+            = $state_ref->{cont} ? $state_ref->{line}
+            :                      $parse_label_func->($state_ref)
+            ;
+        my $value
+            = $parse_var_func->($state_ref, _parse_value($state_ref, $line));
+        if ($state_ref->{ctx}->get_value()) {
+            $value = $state_ref->{ctx}->get_value() . $value;
+        }
+        $state_ref->{ctx}->set_value($value);
+        if (!$state_ref->{cont}) {
+            _parse_var_here($state_ref);
+        }
+    };
+}
+
+# Parses a configuration line label (FCM 1 format).
+sub _parse_fcm1_label {
+    my ($state_ref) = @_;
+    my ($label, $line) = split(qr{\s+}xms, $state_ref->{line}, 2);
+    $state_ref->{ctx}->set_label($label);
+    return $line;
+}
+
+# Parses a configuration line label (FCM 2 format).
+sub _parse_fcm2_label {
+    my ($state_ref) = @_;
+    my %EXTRACTOR_OF = (
+        equal    => sub {($_[0] =~ $PATTERN_OF{fcm2_equal})},
+        label    => sub {($_[0] =~ $PATTERN_OF{fcm2_label})},
+        modifier => sub {extract_bracketed($_[0], '{}')} ,
+        ns       => sub {extract_bracketed($_[0], '["]')},
+    );
+    my %ACTION_OF = (
+        equal    => sub {$_[1] || $E->throw($E->CONFIG_SYNTAX, $_[0])},
+        label    => sub {$_[0]->set_label($_[1])},
+        modifier => \&_parse_fcm2_label_modifier,
+        ns       => \&_parse_fcm2_label_ns,
+    );
+    my %EXPAND_VAR_IN = (modifier => 1, ns => 1);
+    my $line = $state_ref->{line};
+    for my $key (qw{label modifier ns equal}) {
+        $line ||= q{};
+        (my $content, $line) = $EXTRACTOR_OF{$key}->($line);
+        if ($EXPAND_VAR_IN{$key}) {
+            $content = _parse_fcm2_var($state_ref, $content);
+        }
+        $ACTION_OF{$key}->($state_ref->{ctx}, $content);
+    }
+    return $line;
+}
+
+# Parses the modifier part in a configuration line label (FCM 2 format).
+sub _parse_fcm2_label_modifier {
+    my ($ctx, $content) = @_;
+    if ($content) {
+        my ($str) = $content =~ $PATTERN_OF{trim_brace};
+        my %hash;
+        for my $item (parse_line($PATTERN_OF{delim_csv}, 0, $str)) {
+            my ($key, $value) = split($PATTERN_OF{delim_mod}, $item, 2);
+            # Note: "key1, key2: value2, ..." == "key1: 1, key2: value2, ..."
+            $hash{$key} = ($value ? $value : 1);
+        }
+        $ctx->set_modifier_of(\%hash);
+    }
+}
+
+# Parses the ns part in a configuration line label (FCM 2 format).
+sub _parse_fcm2_label_ns {
+    my ($ctx, $content) = @_;
+    if ($content) {
+        my ($str) = $content =~ $PATTERN_OF{trim_brace};
+        my @ns = map {$_ eq q{/} ? q{} : $_} parse_line(q{ }, 0, $str);
+        $ctx->set_ns_list(\@ns);
+    }
+}
+
+# Expands variables in a string in a FCM 1 configuration file.
+sub _parse_fcm1_var {
+    my ($state_ref, $value) = @_;
+    my %V = %{$state_ref->{var}};
+    my $lead = q{};
+    my $tail = $value;
+    MATCH:
+    while (defined($tail) && length($tail) > 0) {
+        my ($pre, $sigil, $br_open, $name, $br_close, $post)
+            = map {defined($_) ? $_ : q{}} ($tail =~ $PATTERN_OF{fcm1_var});
+        if (!$name) {
+            return $lead . $tail;
+        }
+        $tail = $post;
+        my $symbol = $sigil . $br_open . $name . $br_close;
+        my $substitute
+            = $name eq $HERE                       ? $symbol
+            : $sigil eq '$' && exists($ENV{$name}) ? $ENV{$name}
+            : $sigil eq '%' && exists($V{$name})   ? $V{$name}
+            :                                        undef
+            ;
+        if (!defined($substitute)) {
+            $UTIL->event(
+                FCM::Context::Event->CONFIG_VAR_UNDEF,
+                $state_ref->{ctx},
+                $symbol,
+            );
+        }
+        $substitute ||= $symbol;
+        $lead .= $pre . $substitute;
+    }
+    return $lead;
+}
+
+# Expands variables in a string in a FCM 2 configuration file.
+sub _parse_fcm2_var {
+    my ($state_ref, $value) = @_;
+    my %V = (%ENV, %{$state_ref->{var}});
+    my $lead = q{};
+    my $tail = $value;
+    while (defined($tail) && length($tail) > 0) {
+        my ($pre, $esc, $sigil, $br_open, $name, $br_close, $post)
+            = map {defined($_) ? $_ : q{}} ($tail =~ $PATTERN_OF{fcm2_var});
+        if (!$name) {
+            return $lead . $tail;
+        }
+        $tail = $post;
+        my $symbol = $sigil . $br_open . $name . $br_close;
+        my $substitute
+            = $name eq $HERE           ? $symbol
+            : $esc && length($esc) % 2 ? $symbol
+            : exists($V{$name})        ? $V{$name}
+            :                            undef
+            ;
+        if (!defined($substitute)) {
+            return $E->throw(
+                $E->CONFIG_VAR_UNDEF, $state_ref->{ctx}, "undef($symbol)",
+            );
+        }
+        $lead .= $pre . substr($esc, 0, length($esc) / 2) . $substitute;
+    }
+    return $lead;
+}
+
+# Parses the value part of a configuration line.
+sub _parse_value {
+    my ($state_ref, $line) = @_;
+    $line ||= q{};
+    my ($value) = parse_line($PATTERN_OF{comment}, 1, $line);
+    $value ||= q{};
+    chomp($value);
+    ($value) = $value =~ $PATTERN_OF{$state_ref->{cont} ? 'cont_prev' : 'trim'};
+    $state_ref->{cont} = q{};
+    if ($value) {
+        my ($lead, $tail) = $value =~ $PATTERN_OF{cont_next};
+        if ($tail && length($tail) % 2) {
+            $value = $lead;
+            $state_ref->{cont} = $tail;
+        }
+    }
+    return $value;
+}
+
+# Expands the leading $HERE variable in the value of a configuration entry.
+sub _parse_var_here {
+    my ($state_ref) = @_;
+    my @values = shellwords($state_ref->{ctx}->get_value());
+    if (!grep {$_ =~ $PATTERN_OF{here}} @values) {
+        return;
+    }
+    VALUE:
+    for my $value (@values) {
+        my ($head, $tail)
+            = map {defined($_) ? $_ : q{}} $value =~ $PATTERN_OF{here};
+        if (!$head) {
+            next VALUE;
+        }
+        $tail = index($tail, '/') == 0 ? substr($tail, 1) : q{}; # FIXME
+        my $here = $state_ref->{stack}->[-1]->[$I_HERE_LOCATOR];
+        $value = $UTIL->loc_cat($here, $tail)->get_value();
+    }
+    $state_ref->{ctx}->set_value(join(
+        q{ },
+        map {my $s = $_; $s =~ s{(['"\s])}{\\$1}gmsx; $s} @values,
+    ));
+}
+
+# Returns a function to process a variable assignment. If
+# $assign_if_undef_modifier is specified and is present in the declaration, only
+# assign a variable if it is not yet defined.
+sub _process_assign_func {
+    my ($sigil, $assign_if_undef_modifier) = @_;
+    sub {
+        my ($state_ref) = @_;
+        my $ctx = $state_ref->{ctx};
+        if (index($ctx->get_label(), $sigil) != 0) { # not a variable assignment
+            return;
+        }
+        my $name = substr($ctx->get_label(), length($sigil));
+        if ($name !~ $PATTERN_OF{var_name}) {
+            return $E->throw($E->CONFIG_SYNTAX, $state_ref->{ctx});
+        }
+        if ($name eq $HERE) {
+            return $E->throw($E->CONFIG_USAGE, $state_ref->{ctx});
+        }
+        if (    !$assign_if_undef_modifier
+            ||  !exists($ctx->get_modifier_of()->{$assign_if_undef_modifier})
+            ||  !exists($ENV{$name}) && !exists($state_ref->{var}{$name})
+        ) {
+            $state_ref->{var}{$name} = $ctx->get_value();
+        }
+        return 1;
+    }
+}
+
+# Processes a FCM 1 label.
+sub _process_fcm1_label {
+    my ($state_ref) = @_;
+    $state_ref->{var}{$state_ref->{ctx}->get_label()}
+        = $state_ref->{ctx}->get_value();
+    return;
+}
+
+# Processes an include-path declaration.
+sub _process_include_path_func {
+    my ($key) = @_;
+    my $PATTERN = $PATTERN_OF{$key};
+    sub {
+        my ($state_ref) = @_;
+        if ($state_ref->{ctx}->get_label() !~ $PATTERN) {
+            return;
+        }
+        my $M = $state_ref->{ctx}->get_modifier_of();
+        my $type = exists($M->{type}) ? $M->{type} : undef;
+        if (exists($M->{'+'})) {
+            push(@{$state_ref->{include_paths}}, (
+                map {
+                    FCM::Context::Locator->new($_, {type => $type});
+                } shellwords($state_ref->{ctx}->get_value()),
+            )),
+        }
+        else {
+            $state_ref->{include_paths} = [
+                map {
+                    FCM::Context::Locator->new($_, {type => $type});
+                } shellwords($state_ref->{ctx}->get_value())
+            ],
+        }
+        return 1;
+    };
+}
+
+# Processes an include declaration.
+sub _process_include_func {
+    my ($key) = @_;
+    my $PATTERN = $PATTERN_OF{$key};
+    sub {
+        my ($state_ref) = @_;
+        if ($state_ref->{ctx}->get_label() !~ $PATTERN) {
+            return;
+        }
+        my $M = $state_ref->{ctx}->get_modifier_of();
+        my $type = exists($M->{type}) ? $M->{type} : undef;
+        push(@{$state_ref->{stack}}, (map {
+            my $name = $_;
+            my $locator;
+            if (    $UTIL->uri_match($name)
+                ||  file_name_is_absolute($name)
+            ) {
+                $locator = FCM::Context::Locator->new($name, {type => $type});
+            }
+            if (!defined($locator)) {
+                HEAD:
+                for my $head (
+                    $state_ref->{stack}->[-1]->[$I_HERE_LOCATOR],
+                    @{$state_ref->{include_paths}},
+                ) {
+                    my $locator_at_head = $UTIL->loc_cat($head, $name);
+                    if ($UTIL->loc_exists($locator_at_head)) {
+                        $locator = $locator_at_head;
+                        last HEAD;
+                    }
+                }
+            }
+            if (!defined($locator)) {
+                return $E->throw(
+                    $E->CONFIG_LOAD, $state_ref->{stack}, "include=$name",
+                );
+            }
+            [$locator, 0, undef, undef];
+        } shellwords($state_ref->{ctx}->get_value())));
+        return 1;
+    };
+}
+
+# Reads the next entry of a configuration file.
+sub _read {
+    my ($attrib_ref, $state_ref) = @_;
+    local($UTIL) = $attrib_ref->{util};
+    STACK:
+    while (@{$state_ref->{stack}}) {
+        my $S = $state_ref->{stack}->[-1];
+        # Open a file handle for the top of the stack, if necessary
+        if (!defined($S->[$I_HANDLE])) {
+            eval {
+                # Check for cyclic dependency
+                for my $i (-scalar(@{$state_ref->{stack}}) .. -2) {
+                    my $value = $UTIL->loc_as_invariant(
+                        $state_ref->{stack}->[$i]->[$I_LOCATOR],
+                    );
+                    if ($value eq $UTIL->loc_as_invariant($S->[$I_LOCATOR])) {
+                        return $E->throw($E->CONFIG_CYCLIC, $state_ref->{stack});
+                    }
+                }
+                $S->[$I_HANDLE] = $UTIL->loc_reader($S->[$I_LOCATOR]);
+                $S->[$I_HERE_LOCATOR] = $UTIL->loc_dir($S->[$I_LOCATOR]);
+            };
+            if (my $e = $@) {
+                if ($E->caught($e) && $e->get_code() eq $E->CONFIG_CYCLIC) {
+                    die($e);
+                }
+                return $E->throw($E->CONFIG_LOAD, $state_ref->{stack}, $e);
+            }
+            $UTIL->event(
+                FCM::Context::Event->CONFIG_OPEN,
+                _stack_cp($state_ref->{stack}),
+                $attrib_ref->{event_level},
+            );
+        }
+        # Read a line and parse it
+        LINE:
+        while ($state_ref->{line} = readline($S->[$I_HANDLE])) {
+            if ($state_ref->{line} =~ $PATTERN_OF{ignore}) {
+                next LINE;
+            }
+            $S->[$I_LINE_NUM] = $.;
+            if (!$state_ref->{cont}) {
+                $state_ref->{ctx} = FCM::Context::ConfigEntry->new({
+                    stack => _stack_cp($state_ref->{stack}),
+                });
+            }
+            $attrib_ref->{parser}->($state_ref);
+            if (!$state_ref->{cont}) {
+                if ($attrib_ref->{processor}->($state_ref)) {
+                    next STACK;
+                }
+                return $state_ref->{ctx};
+            }
+        }
+        # At end of file
+        if ($state_ref->{cont}) {
+            return $E->throw($E->CONFIG_CONT_EOF, $state_ref->{ctx});
+        }
+        close($state_ref->{stack}->[-1]->[$I_HANDLE]);
+        $state_ref->{stack}->[-1]->[$I_HANDLE] = undef; # free the memory
+        pop(@{$state_ref->{stack}});
+    }
+    return;
+}
+
+# Copies a stack, selecting only the and the line number.
+sub _stack_cp {
+    [map {[@{$_}[$I_LOCATOR, $I_LINE_NUM]]} @{$_[0]}];
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Config
+
+=head1 SYNOPSIS
+
+    use FCM::Util;
+    my $util = FCM::Util->new(\%attrib);
+    # ... time passes, and now we want to read a FCM 1 config
+    my ($locator, $reader);
+    $locator = FCM::Context::Locator->new($path_to_an_fcm1_config);
+    $reader
+        = $util->config_reader($locator, \%FCM::Util::ConfigReader::FCM1_ATTRIB);
+    while (my $entry = $reader->()) {
+        # ...
+    }
+    # ... time passes, and now we want to read a FCM 2 config
+    $locator = FCM::Context::Locator->new($path_to_an_fcm2_config);
+    $reader = $util->config_reader($locator);
+    while (my $entry = $reader->()) {
+        # ...
+    }
+
+=head1 DESCRIPTION
+
+This module is part of L<FCM::Util|FCM::Util>. Provides a function to generate
+configuration file readers.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new new instance. The %attrib must contain the following:
+
+=over 4
+
+=item {parser}
+
+A CODE reference to parse the lines in a configuration file into entry contexts.
+It should have a calling interface $f->(\%state). (See L</STATE> for a
+description of %state.) The return value is ignored.
+
+=item {processor}
+
+A CODE reference to post-process each entry context. It should have a calling
+interface $f->(\%state). (See L</STATE> for a description of %state.) The
+processor should return true if the current entry has been processed and is no
+longer considered useful for the user.
+
+=item {util}
+
+The L<FCM::Util|FCM::Util> object, which initialises this class.
+
+=back
+
+=back
+
+See the description of the config_reader() method in L<FCM::Util|FCM::Util> for
+detail.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/ConfigUpgrade.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/ConfigUpgrade.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/ConfigUpgrade.pm	(revision 5095)
@@ -0,0 +1,136 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::Util::ConfigUpgrade;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::ConfigEntry;
+
+my %DECL_PATTERN_OF = (
+    browser_mapping => qr{\A set::browser_mapping(?:_default|:: ([^:]+)):: (.+) \z}ixms,
+    keyword_loc     => qr{\A set::(?:repos|url):: (.+) \z}ixms,
+    keyword_rev     => qr{\A set::revision:: ([^:]+) :: (.+) \z}ixms,
+);
+my @UPGRADE_FUNCS = (
+    \&_upgrade_browser_mapping_decl,
+    \&_upgrade_keyword_loc_decl,
+    \&_upgrade_keyword_rev_decl,
+);
+
+# Creates the class.
+__PACKAGE__->class({}, {action_of => {upgrade => \&_upgrade}});
+
+sub _upgrade {
+    my ($attrib_ref, $config_entry) = @_;
+    if (!defined($config_entry)) {
+        return;
+    }
+    for my $func (@UPGRADE_FUNCS) {
+        $func->($config_entry);
+        if ($func->($config_entry)) {
+            return $config_entry;
+        }
+    }
+    return $config_entry;
+}
+
+# Upgrades a browser mapping declaration.
+sub _upgrade_browser_mapping_decl {
+    my ($config_entry) = @_;
+    my ($ns, $key)
+        = $config_entry->get_label() =~ $DECL_PATTERN_OF{browser_mapping};
+    if (!$key) {
+        return;
+    }
+    $config_entry->set_label(
+          $key eq 'browser_url_template' ? 'browser.loc-tmpl'
+        : $key eq 'browser_rev_template' ? 'browser.rev-tmpl'
+        :                                  'browser.comp-pat'
+    );
+    if ($ns) {
+        $config_entry->set_ns_list([$ns]);
+    }
+}
+
+# Upgrades a location keyword declaration.
+sub _upgrade_keyword_loc_decl {
+    my ($config_entry) = @_;
+    my ($ns) = $config_entry->get_label() =~ $DECL_PATTERN_OF{keyword_loc};
+    if (!$ns) {
+        return;
+    }
+    $config_entry->set_label('location');
+    $config_entry->get_modifier_of()->{primary} = 1;
+    $config_entry->set_ns_list([$ns]);
+}
+
+# Upgrades a revision keyword declaration.
+sub _upgrade_keyword_rev_decl {
+    my ($config_entry) = @_;
+    my ($ns, $key) = $config_entry->get_label() =~ $DECL_PATTERN_OF{keyword_rev};
+    if (!$ns || !$key) {
+        return;
+    }
+    $config_entry->set_label('revision');
+    $config_entry->set_ns_list([$ns, $key]);
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::ConfigUpgrade
+
+=head1 SYNOPSIS
+
+    use FCM::Util::ConfigUpgrade;
+    $upgrade = FCM::Util::ConfigUpgrade->new();
+    if (!$upgrade->($entry)) {
+        die($entry->get_label(), ": cannot upgrade.\n");
+    }
+    # ... do something with $entry
+
+=head1 DESCRIPTION
+
+Provides a utility to upgrade FCM 1 configuration to FCM 2 configuration.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new()
+
+Creates and returns a new instance of this utility.
+
+=item $util->($entry)
+
+Upgrades the content of $entry, where possible. Only keyword related
+declarations in the FCM 1 common configuration files are currently supported.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Event.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Event.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Event.pm	(revision 5095)
@@ -0,0 +1,1251 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Event;
+use base qw{FCM::Class::CODE};
+
+use Data::Dumper qw{Dumper};
+use FCM::Context::Event;
+use File::Basename qw{basename};
+use List::Util qw{first};
+use POSIX qw{strftime};
+use Scalar::Util qw{blessed};
+
+my $CTX = 'FCM::Context::Event';
+my $IS_MULTI_LINE = 1;
+
+# Event keys and their actions.
+my %ACTION_OF = (
+    $CTX->CM_ABORT                      => \&_event_cm_abort,
+    $CTX->CM_BRANCH_CREATE_SOURCE       => _func('cm_branch_create_source'),
+    $CTX->CM_BRANCH_LIST                => \&_event_cm_branch_list,
+    $CTX->CM_COMMIT_MESSAGE             => \&_event_cm_commit_message,
+    $CTX->CM_CONFLICT_TEXT              => _func('cm_conflict_text'),
+    $CTX->CM_CONFLICT_TEXT_SKIP         => \&_event_cm_conflict_text_skip,
+    $CTX->CM_CONFLICT_TREE              => _func('cm_conflict_tree'),
+    $CTX->CM_CONFLICT_TREE_SKIP         => \&_event_cm_conflict_tree_skip,
+    $CTX->CM_CONFLICT_TREE_TIME_WARN    => \&_event_cm_conflict_tree_time_warn,
+    $CTX->CM_CREATE_TARGET              => _func('cm_create_target'),
+    $CTX->CM_LOG_EDIT                   => _func('cm_log_edit'),
+    $CTX->CONFIG_OPEN                   => \&_event_config_open,
+    $CTX->CONFIG_ENTRY                  => \&_event_config_entry,
+    $CTX->CONFIG_VAR_UNDEF              => \&_event_config_var_undef,
+    $CTX->E                             => \&_event_e,
+    $CTX->EXPORT_ITEM_CREATE            => _func('export_item_create'),
+    $CTX->EXPORT_ITEM_DELETE            => _func('export_item_delete'),
+    $CTX->FCM_VERSION                   => _func('fcm_version'),
+    $CTX->KEYWORD_ENTRY                 => \&_event_keyword_entry,
+    $CTX->MAKE_BUILD_SHELL_OUT          => \&_event_make_build_shell_out,
+    $CTX->MAKE_BUILD_SOURCE_ANALYSE     => \&_event_make_build_source_analyse,
+    $CTX->MAKE_BUILD_SOURCE_SUMMARY     => _func('make_build_source_summary'),
+    $CTX->MAKE_BUILD_TARGET_DONE        => \&_event_make_build_target_done,
+    $CTX->MAKE_BUILD_TARGET_FAIL        => \&_event_make_build_target_fail,
+    $CTX->MAKE_BUILD_TARGET_FROM_NS     => \&_event_make_build_target_from_ns,
+    $CTX->MAKE_BUILD_TARGET_SELECT      => \&_event_make_build_target_select,
+    $CTX->MAKE_BUILD_TARGET_SELECT_TIMER=> _func('make_build_target_select_t'),
+    $CTX->MAKE_BUILD_TARGET_MISSING_DEP => \&_event_make_build_target_missing_dep,
+    $CTX->MAKE_BUILD_TARGET_STACK       => \&_event_make_build_target_stack,
+    $CTX->MAKE_BUILD_TARGET_SUMMARY     => _func('make_build_target_sum'),
+    $CTX->MAKE_BUILD_TARGET_TASK_SUMMARY=> _func('make_build_target_task_sum'),
+    $CTX->MAKE_BUILD_TARGETS_FAIL       => \&_event_make_build_targets_fail,
+    $CTX->MAKE_DEST                     => \&_event_make_dest,
+    $CTX->MAKE_EXTRACT_PROJECT_TREE     => \&_event_make_extract_project_tree,
+    $CTX->MAKE_EXTRACT_RUNNER_SUMMARY   => \&_event_make_extract_runner_summary,
+    $CTX->MAKE_EXTRACT_SYMLINK          => \&_event_make_extract_symlink,
+    $CTX->MAKE_EXTRACT_TARGET           => \&_event_make_extract_target,
+    $CTX->MAKE_EXTRACT_TARGET_SUMMARY   => \&_event_make_extract_target_summary,
+    $CTX->MAKE_MIRROR                   => \&_event_make_mirror,
+    $CTX->OUT                           => \&_event_out,
+    $CTX->SHELL                         => \&_event_shell,
+    $CTX->TASK_WORKERS                  => \&_event_task_workers,
+    $CTX->TIMER                         => \&_event_timer,
+);
+# Helper for "_event_e", list of exception classes and their formatters.
+our @E_FORMATTERS = (
+    ['FCM1::Cm::Exception'    , \&_format_e_cm            ],
+    ['FCM1::CLI::Exception'   , sub {$_[0]->get_message()}],
+    ['FCM::Class::Exception' , \&_format_e_class         ],
+    ['FCM::CLI::Exception'   , \&_format_e_cli           ],
+    ['FCM::System::Exception', \&_format_e_sys           ],
+    ['FCM::Util::Exception'  , \&_format_e_util          ],
+);
+# Error format strings for FCM1::Cm::Exception.
+our %E_CM_FORMAT_FOR = (
+    DIFF_PROJECTS     => "%s (target) and %s (source) are not related.\n",
+    INVALID_BRANCH    => "%s: not a valid URL of a standard FCM branch.\n",
+    INVALID_PROJECT   => "%s: not a valid URL of a standard FCM project.\n",
+    INVALID_TARGET    => "%s: not a valid working copy or URL.\n",
+    INVALID_URL       => "%s: not a valid URL.\n",
+    INVALID_WC        => "%s: not a valid working copy.\n",
+    MERGE_REV_INVALID => "%s: not a revision in the available merge list.\n",
+    MERGE_SELF        => "%s: cannot be merged to its own working copy: %s.\n",
+    MERGE_UNRELATED   => "%s: target and %s: source not directly related.\n",
+    MERGE_UNSAFE      => "%s: source contains changes outside the target"
+                         . " sub-directory. Please merge with a full tree.\n",
+    MKPATH            => "%s: cannot create directory.\n",
+    NOT_EXIST         => "%s: does not exist.\n",
+    PARENT_NOT_EXIST  => "%s: parent %s no longer exists.\n",
+    RMTREE            => "%s: cannot remove.\n",
+    ST_CONFLICT       => "File(s) in conflicts:\n%s",
+    ST_MISSING        => "File(s) missing:\n%s",
+    ST_OUT_OF_DATE    => "File(s) out of date:\n%s",
+    SWITCH_UNSAFE     => "%s: merge template exists."
+                         . " Please remove before retrying.\n",
+    WC_INVALID_BRANCH => "%s: not a working copy of a standard FCM branch.\n",
+    WC_URL_NOT_EXIST  => "%s: working copy URL does not exists at HEAD.\n",
+);
+# Helper for "_format_e_sys", formatters based on exception code.
+our %E_SYS_FORMATTER_FOR = (
+    BUILD_SOURCE     => _format_e_func('e_sys_build_source'),
+    BUILD_SOURCE_SYN => _format_e_func('e_sys_build_source_syn'),
+    BUILD_TARGET     => \&_format_e_sys_build_target,
+    BUILD_TARGET_BAD => _format_e_func('e_sys_build_target_bad', $IS_MULTI_LINE),
+    BUILD_TARGET_CYC => \&_format_e_sys_build_target_cyc,
+    BUILD_TARGET_DEP => \&_format_e_sys_build_target_dep,
+    BUILD_TARGET_DUP => \&_format_e_sys_build_target_dup,
+    CACHE_LOAD       => _format_e_func('e_sys_cache_load'),
+    CACHE_TYPE       => _format_e_func('e_sys_cache_type'),
+    CM_ALREADY_EXIST => _format_e_func('e_sys_cm_already_exist'),
+    CM_ARG           => _format_e_func('e_sys_cm_arg'),
+    CM_BRANCH_NAME   => _format_e_func('e_sys_cm_branch_name'),
+    CM_BRANCH_SOURCE => _format_e_func('e_sys_cm_branch_source'),
+    CM_CHECKOUT      => _format_e_func('e_sys_cm_checkout'),
+    CM_LOG_EDIT_NULL => _format_e_func('e_sys_cm_log_edit_null'),
+    CM_LOG_EDIT_DELIMITER => _format_e_func('e_sys_cm_log_edit_delimiter'),
+    CM_OPT_ARG       => _format_e_func('e_sys_cm_opt_arg'),
+    CM_PROJECT_NAME  => _format_e_func('e_sys_cm_project_name'),
+    CM_REPOSITORY    => _format_e_func('e_sys_cm_repository'),
+    CONFIG_CONFLICT  => _format_e_sys_config_func('conflict'),
+    CONFIG_INHERIT   => _format_e_sys_config_func('inherit'),
+    CONFIG_MODIFIER  => _format_e_sys_config_func('modifier'),
+    CONFIG_NS        => _format_e_sys_config_func('ns'),
+    CONFIG_NS_VALUE  => _format_e_sys_config_func('ns_value'),
+    CONFIG_UNKNOWN   => _format_e_sys_config_func('unknown'),
+    CONFIG_VALUE     => _format_e_sys_config_func('value'),
+    CONFIG_VERSION   => _format_e_sys_config_func('version'),
+    COPY             => _format_e_func('e_sys_copy'),
+    DEST_CLEAN       => _format_e_func('e_sys_dest_clean'),
+    DEST_CREATE      => _format_e_func('e_sys_dest_create'),
+    DEST_LOCK        => _format_e_func('e_sys_dest_lock'),
+    DEST_LOCKED      => _format_e_func('e_sys_dest_locked'),
+    EXPORT_ITEMS_SRC => _format_e_func('e_sys_export_items_src'),
+    EXTRACT_LOC_BASE => _format_e_func('e_sys_extract_loc_base'),
+    EXTRACT_MERGE    => \&_format_e_sys_extract_merge,
+    EXTRACT_NS       => _format_e_func('e_sys_extract_ns', $IS_MULTI_LINE),
+    MIRROR           => \&_format_e_sys_mirror,
+    MIRROR_NULL      => _format_e_func('e_sys_mirror_null'),
+    MIRROR_SOURCE    => _format_e_func('e_sys_mirror_source', $IS_MULTI_LINE),
+    MIRROR_TARGET    => _format_e_func('e_sys_mirror_target'),
+    MAKE             => _format_e_func('e_sys_make'),
+    MAKE_ARG         => \&_format_e_sys_make_arg,
+    MAKE_CFG         => _format_e_func('e_sys_make_cfg'),
+    MAKE_CFG_FILE    => _format_e_func('e_sys_make_cfg_file'),
+    MAKE_PROP_NS     => \&_format_e_sys_make_prop_ns,
+    MAKE_PROP_VALUE  => \&_format_e_sys_make_prop_value,
+    SHELL            => \&_format_e_sys_shell,
+);
+# Helper for "_format_e_util", formatters based on exception code.
+our %E_UTIL_FORMATTER_FOR = (
+    CLASS_LOADER         => _format_e_func('e_util_class_loader'),
+    CONFIG_CONT_EOF      => _format_e_util_config_func('eof'),
+    CONFIG_CYCLIC        => _format_e_util_config_stack_func('cyclic'),
+    CONFIG_LOAD          => _format_e_util_config_stack_func('load'),
+    CONFIG_SYNTAX        => _format_e_util_config_func('syntax'),
+    CONFIG_USAGE         => _format_e_util_config_func('usage'),
+    CONFIG_VAR_UNDEF     => _format_e_util_config_func('var_undef'),
+    IO                   => _format_e_func('e_util_io'),
+    LOCATOR_AS_INVARIANT => _format_e_util_locator_func(''),
+    LOCATOR_BROWSER_URL  => _format_e_util_locator_func('_browser_url'),
+    LOCATOR_FIND         => _format_e_util_locator_func(''),
+    LOCATOR_KEYWORD_LOC  => _format_e_util_locator_func('_keyword_loc'),
+    LOCATOR_KEYWORD_REV  => _format_e_util_locator_func('_keyword_rev'),
+    LOCATOR_READER       => _format_e_util_locator_func('_reader'),
+    LOCATOR_TYPE         => _format_e_util_locator_func('_type'),
+    SHELL_OPEN3          => _format_e_util_shell_func('_open3'),
+    SHELL_OS             => _format_e_util_shell_func('_os'),
+    SHELL_SIGNAL         => _format_e_util_shell_func('_signal'),
+    SHELL_WHICH          => _format_e_util_shell_func('_which'),
+);
+# Alias
+our $R;
+# Named diagnostic strings
+our %S = (
+    # ERROR DIAGNOSTICS
+    e_class                      => '%s: %s => %s: internal error at %s:%d',
+    e_cli_app                    => '%s: unknown command,'
+                                    . ' type \'%s help\' for help',
+    e_cli_opt                    => '%s: incorrect usage,'
+                                    . ' type \'%s help %1$s\' for help',
+    e_sys_build_source           => '%s: source does not exist',
+    e_sys_build_source_syn       => '%s(%d): syntax error',
+    e_sys_build_target           => '%s: target not found after an update:',
+    e_sys_build_target_1         => '%s: expect target file',
+    e_sys_build_target_bad       => '%s: don\'t know how to build specified'
+                                    . ' target',
+    e_sys_build_target_cyclic    => '%s: target depends on itself',
+    e_sys_build_target_dep       => '%s: bad or missing dependency (type=%s)',
+    e_sys_build_target_dup       => '%s: same target from [%s]',
+    e_sys_build_target_stack     => '    required by: %s',
+    e_sys_cache_load             => '%s: cannot retrieve cache',
+    e_sys_cache_type             => '%s: unexpected cache type',
+    e_sys_cm_already_exist       => '%s: already exists',
+    e_sys_cm_arg                 => '%s: bad argument',
+    e_sys_cm_branch_name         => '%s: invalid branch name',
+    e_sys_cm_branch_source       => '%s: invalid branch source',
+    e_sys_cm_checkout            => '%s: is already a working copy of %s',
+    e_sys_cm_log_edit_delimiter  => '%sthe above log delimiter is altered',
+    e_sys_cm_log_edit_null       => 'log message is empty',
+    e_sys_cm_opt_arg             => '%s=%s: bad option argument',
+    e_sys_cm_project_name        => '%s: invalid project name',
+    e_sys_cm_repository          => '%s: invalid repository',
+    e_sys_config_conflict        => '%s: cannot modify, value is inherited',
+    e_sys_config_inherit         => '%s: cannot inherit from an incomplete make',
+    e_sys_config_modifier        => '%s: incorrect modifier in declaration',
+    e_sys_config_ns              => '%s: incorrect name-space declaration',
+    e_sys_config_ns_value        => '%s: mismatch between name-space and value',
+    e_sys_config_unknown         => '%s: unknown declaration',
+    e_sys_config_value           => '%s: incorrect value in declaration',
+    e_sys_config_version         => '%s: requested version mismatch',
+    e_sys_copy                   => '%s -> %s: copy failed',
+    e_sys_dest_clean             => '%s: cannot remove',
+    e_sys_dest_create            => '%s: cannot create',
+    e_sys_dest_locked            => '%s: lock exists at the destination',
+    e_sys_export_items_src       => 'source location not specified',
+    e_sys_extract_loc_base       => '%s: cannot determine base location',
+    e_sys_extract_merge          => '%s: merge results in conflict',
+    e_sys_extract_merge_output   => '    merge output: %s',
+    e_sys_extract_merge_source   => '    source from location %2d: %s',
+    e_sys_extract_merge_source_0 => '(none)',
+    e_sys_extract_merge_source_x => '!!! source from location %2d: %s',
+    e_sys_extract_ns             => '%s: name-spaces declared but not used',
+    e_sys_mirror                 => '%s <- %s: mirror failed',
+    e_sys_mirror_null            => 'mirror target not specified',
+    e_sys_mirror_source          => '%s: cannot mirror this step',
+    e_sys_mirror_target          => '%s: cannot create mirror target',
+    e_sys_make                   => '%s: step is not implemented',
+    e_sys_make_arg               => 'arg %d (%s): invalid config declaration',
+    e_sys_make_arg_more          => 'did you mean "%s"?',
+    e_sys_make_cfg               => 'no configuration specified or found',
+    e_sys_make_cfg_file          => '%s: no such configuration file',
+    e_sys_make_prop_ns           => '%s.prop{%s}[%s] = %s: bad name-space',
+    e_sys_make_prop_value        => '%s.prop{%s}[%s] = %s: bad value',
+    e_sys_shell                  => '%s # rc=%d',
+    e_unknown                    => 'command failed',
+    e_util_class_loader          => '%s: required package cannot be loaded',
+    e_util_config                => '%s:%d: %s',
+    e_util_config_eof            => 'continuation at eof',
+    e_util_config_syntax         => 'syntax error',
+    e_util_config_usage          => 'incorrect usage',
+    e_util_config_var_undef      => 'reference to undefined variable',
+    e_util_config_stack_cyclic   => '%s: cannot load config file,'
+                                    . ' cyclic dependency',
+    e_util_config_stack_load     => '%s: cannot load config file',
+    e_util_io                    => '%s: I/O error',
+    e_util_locator               => '%s: not found',
+    e_util_locator_browser_url   => '%s: cannot determine browser URL',
+    e_util_locator_keyword_loc   => '%s: location keyword not defined',
+    e_util_locator_keyword_rev   => '%s: revision keyword not defined',
+    e_util_locator_reader        => '%s: cannot be read',
+    e_util_locator_type          => '%s: unsupported type of location',
+    e_util_shell_open3           => '%s: command failed to invoke',
+    e_util_shell_os              => '%s: command failed due to OS error',
+    e_util_shell_signal          => '%s: command received a signal',
+    e_util_shell_which           => '%s: command not found',
+
+    # NORMAL DIAGNOSTICS
+    cm_abort_null                => 'command will result in no change',
+    cm_abort_user                => 'by user',
+    cm_branch_create_source      => 'Source: %s (%d)',
+    cm_branch_list               => '%s: %d match(es)',
+    cm_commit_message            => 'Change summary:' . "\n"
+                                    . '-' x 80 . "\n" . '%s'
+                                    . '-' x 80 . "\n"
+                                    . 'Commit message is as follows:' . "\n"
+                                    . '-' x 80 . "\n" . '%s%s'
+                                    . '-' x 80,
+    cm_conflict_text             => '%s: in text conflict.',
+    cm_conflict_text_skip        => '%s: skipped binary file in text conflict.',
+    cm_conflict_tree             => '%s: in tree conflict.',
+    cm_conflict_tree_skip        => '%s: skipped unhandled tree conflict.',
+    cm_conflict_tree_time_warn   => '%s: looking for a rename operation,'
+                                    . ' please wait...',
+    cm_create_target             => 'Created: %s',
+    cm_log_edit                  => '%s: starting commit message editor...',
+    config_open                  => 'config-file=%s%s',
+    config_var_undef             => '%s:%d: %s: variable not defined',
+    event                        => '%s: event raised',
+    export_item_create           => 'A %s@%s -> %s',
+    export_item_delete           => 'D %s@%s -> %s',
+    fcm_version                  => '%s',
+    keyword_loc                  => 'location[%s] = %s',
+    keyword_loc_primary          => 'location{primary}[%s] = %s',
+    keyword_rev                  => 'revision[%s:%s] = %s',
+    make_build_shell_out_1       => '[>>&1] ',
+    make_build_shell_out_2       => '[>>&2] ',
+    make_build_source_analyse    => 'analyse %4.1f %s',
+    make_build_source_analyse_1  => '             -> (%9s) %s',
+    make_build_source_summary    => 'sources: total=%d, analysed=%d,'
+                                    . ' elapsed-time=%.1fs, total-time=%.1fs',
+    make_build_target_done_0     => '%-9s ---- %s %-20s <- %s',
+    make_build_target_done_1     => '%-9s %4.1f %s %-20s <- %s',
+    make_build_target_from_ns    => 'source->target %s -> (%s) %s/ %s',
+    make_build_target_select     => 'required-target: %-9s %-7s %s',
+    make_build_target_select_t   => 'target-tree-analysis: elapsed-time=%.1fs',
+    make_build_target_stack      => 'target %s%s%s',
+    make_build_target_stack_more => ' (n-deps=%d)',
+    make_build_target_missing_dep=> '%-30s: ignore-missing-dep: (%3$9s) %2$s',
+    make_build_target_sum        => 'TOTAL     targets:'
+                                    . ' modified=%d, unchanged=%d, failed=%d,'
+                                    . ' elapsed-time=%.1fs',
+    make_build_target_task_sum   => '%-9s targets:'
+                                    . ' modified=%d, unchanged=%d, failed=%d,'
+                                    . ' total-time=%.1fs',
+    make_build_targets_fail_0    => '! %-20s: depends on failed target: %s',
+    make_build_targets_fail_1    => '! %-20s: update task failed',
+    make_description             => 'description=%s',
+    make_dest                    => 'dest=%s',
+    make_dest_use                => 'use=%s',
+    make_extract_project_tree    => 'location %5s:%2d: %s%s',
+    make_extract_project_tree_1  => ' (%s)',
+    make_extract_runner_summary  => '%s: n-tasks=%d,'
+                                    . ' elapsed-time=%.1fs, total-time=%.1fs',
+    make_extract_target          => '%s%s %5s:%-6s %s',
+    make_extract_target_base_yes => '0',
+    make_extract_target_base_no  => '-',
+    make_extract_symlink         => 'symlink ignored: %s',
+    make_extract_target_summary_d=> '  dest: %4d [%1s %s]',
+    make_extract_target_summary_s=> 'source: %4d [%1s %s]',
+    make_mirror                  => '%s <- %s',
+    make_mode                    => 'mode=%s',
+    make_mode_new                => 'new',
+    make_mode_incr               => 'incremental',
+    shell                        => 'shell(%d %4.1f) %s',
+    task_workers_destroy         => '%s worker processes destroyed',
+    task_workers_init            => '%s worker processes started',
+    timer_done                   => '%-20s# %.1fs',
+    timer_init                   => '%-20s# %s',
+);
+# Symbols/Descriptions for a make extract target status.
+my %MAKE_EXTRACT_TARGET_SYM_OF = (
+    ST_ADDED     => ['A', 'added'                        ],
+    ST_DELETED   => ['D', 'deleted'                      ],
+    ST_MODIFIED  => ['M', 'modified'                     ],
+    ST_O_ADDED   => ['a', 'added, overriding inherited'  ],
+    ST_O_DELETED => ['d', 'deleted, overriding inherited'],
+    ST_UNCHANGED => ['U', 'unchanged'                    ],
+    ST_UNKNOWN   => ['?', 'unknown'                      ],
+);
+# Symbols/Descriptions for a make source status.
+my %MAKE_EXTRACT_SOURCE_SYM_OF = (
+    ST_ADDED     => ['A', 'added by a diff source tree'     ],
+    ST_DELETED   => ['D', 'deleted by a diff source tree'   ],
+    ST_MERGED    => ['G', 'merged from 2+ diff source trees'],
+    ST_MODIFIED  => ['M', 'modified by a diff source tree'  ],
+    ST_UNCHANGED => ['U', 'from base'                       ],
+    ST_UNKNOWN   => ['?', 'unknown'                         ],
+);
+
+# Creates the class.
+__PACKAGE__->class({util => '&'}, {action_of => {main => \&_main}});
+
+sub _main {
+    my ($attrib_ref, $event) = @_;
+    local($R) = $attrib_ref->{util}->util_of_report();
+    if (!exists($ACTION_OF{$event->get_code()})) {
+        return $R->report(
+            {level => $R->HIGH}, sprintf($S{event}, $event->get_code()),
+        );
+    }
+    $ACTION_OF{$event->get_code()}->(@{$event->get_args()});
+}
+
+# Formats a stack of configuration files.
+sub _format_config_stack {
+    my ($config_stack_ref) = @_;
+    my @config_stack = @{$config_stack_ref};
+    my $indent_char = q{};
+    my $return = q{};
+    my $i = 0;
+    for my $item (@config_stack) {
+        my ($locator, $line) = @{$item};
+        my $indent = q{ - } x $i++;
+        $return .= sprintf(
+            $S{'config_open'} . "\n",
+            $indent, ($locator->get_value() . ($line ? ':' . $line : q{})),
+        );
+    }
+    return $return;
+}
+
+# Formats a CM exception.
+sub _format_e_cm {
+    my ($e) = @_;
+    sprintf($E_CM_FORMAT_FOR{$e->get_code()}, $e->get_targets());
+}
+
+# Formats a class exception.
+sub _format_e_class {
+    my ($e) = @_;
+    sprintf(
+        $S{e_class},
+        $e->get_package(),
+        $e->get_key(),
+        (defined($e->get_value()) ? $e->get_value() : 'undef'),
+        @{$e->get_caller()}[1, 2],
+    );
+}
+
+# Formats a CLI exception.
+sub _format_e_cli {
+    my ($e) = @_;
+    my $format
+        = $e->get_code() eq $e->APP ? $S{e_cli_app}
+        :                             $S{e_cli_opt}
+        ;
+    sprintf($format, $e->get_ctx()->[0], basename($0));
+}
+
+# Formats a system exception.
+sub _format_e_sys {
+    my ($e) = @_;
+    if (exists($E_SYS_FORMATTER_FOR{$e->get_code()})) {
+        return $E_SYS_FORMATTER_FOR{$e->get_code()}->($e);
+    }
+    $e;
+}
+
+# Formats a system exception - CONFIG_*.
+sub _format_e_sys_config_func {
+    my ($suffix) = @_;
+    my $key = 'e_sys_config_' . $suffix;
+    sub {
+        my ($e) = @_;
+        my @ctx_list
+            = ref($e->get_ctx()) eq 'ARRAY' ? @{$e->get_ctx()}
+            :                                 ($e->get_ctx())
+            ;
+        map {(
+            sprintf($S{$key}, $_->as_string()),
+            _format_config_stack($_->get_stack()),
+        )} @ctx_list;
+    }
+}
+
+# Formats a system exception - BUILD_TARGET.
+sub _format_e_sys_build_target {
+    my ($e) = @_;
+    my $ctx = $e->get_ctx();
+    (   sprintf($S{e_sys_build_target}, $ctx->get_key()),
+        sprintf($S{e_sys_build_target_1}, $ctx->get_path()),
+    );
+}
+
+# Formats a system exception - BUILD_TARGET_CYC.
+sub _format_e_sys_build_target_cyc {
+    my ($e) = @_;
+    my @messages;
+    while (my ($key, $hash_ref) = each(%{$e->get_ctx()})) {
+        my ($head, @stack) = reverse(@{$hash_ref->{'keys'}});
+        push(@messages, sprintf($S{e_sys_build_target_cyclic}, $head));
+        push(@messages, map {sprintf($S{e_sys_build_target_stack}, $_)} @stack);
+    }
+    @messages;
+}
+
+# Formats a system exception - BUILD_TARGET_DEP.
+sub _format_e_sys_build_target_dep {
+    my ($e) = @_;
+    my @messages;
+    while (my ($key, $hash_ref) = each(%{$e->get_ctx()})) {
+        my ($head, @stack) = reverse(@{$hash_ref->{'keys'}});
+        for (@{$hash_ref->{'values'}}) { # [$dep_key, $dep_type]
+            my ($dep_name, $dep_type, $dep_remark) = @{$_};
+            if ($dep_remark) {
+                $dep_type = $dep_remark . '.' . $dep_type;
+            }
+            push(
+                @messages,
+                sprintf($S{e_sys_build_target_dep}, $dep_name, $dep_type),
+            );
+        }
+        push(@messages, map {sprintf($S{e_sys_build_target_stack}, $_)} @stack);
+    }
+    @messages;
+}
+
+# Formats a system exception - BUILD_TARGET_DUP.
+sub _format_e_sys_build_target_dup {
+    my ($e) = @_;
+    my @messages;
+    while (my ($key, $hash_ref) = each(%{$e->get_ctx()})) {
+        my ($head, @stack) = reverse(@{$hash_ref->{'keys'}});
+        my @ns_list = @{$hash_ref->{'values'}};
+        my $ns = _format_shell_words({'delimiter' => q{, }}, sort(@ns_list));
+        push(@messages, sprintf($S{e_sys_build_target_dup}, $key, $ns));
+        push(@messages, map {sprintf($S{e_sys_build_target_stack}, $_)} @stack);
+    }
+    @messages;
+}
+
+# Formats a system exception - EXTRACT_MERGE.
+sub _format_e_sys_extract_merge {
+    my ($e) = @_;
+    my $target = $e->get_ctx()->{'target'};
+    my $source0 = $target->get_source_of()->{0};
+    my $location_of_0 = $S{e_sys_extract_merge_source_0};
+    if ($source0->get_locator()) {
+        $location_of_0 = $source0->get_locator()->get_value();
+    }
+    my $key = $e->get_ctx()->{'key'};
+    my $location_of_key
+        = $target->get_source_of()->{$key}->get_locator()->get_value();
+    (   sprintf($S{e_sys_extract_merge}, $target->get_ns()),
+        sprintf($S{e_sys_extract_merge_output}, $e->get_ctx()->{'output'}),
+        sprintf($S{e_sys_extract_merge_source}, 0, $location_of_0),
+        (   map {sprintf(
+                $S{e_sys_extract_merge_source},
+                $_,
+                $target->get_source_of()->{$_}->get_locator()->get_value(),
+            )} @{$e->get_ctx()->{'keys_done'}}
+        ),
+        sprintf($S{e_sys_extract_merge_source_x}, $key, $location_of_key),
+        (   map {sprintf(
+                $S{e_sys_extract_merge_source},
+                $_,
+                $target->get_source_of()->{$_}->get_locator()->get_value(),
+            )} @{$e->get_ctx()->{'keys_left'}}
+        ),
+    );
+}
+
+# Formats a system exception - MIRROR.
+sub _format_e_sys_mirror {
+    my ($e) = @_;
+    my ($target, @sources) = @{$e->get_ctx()};
+    sprintf($S{e_sys_mirror}, $target, _format_shell_words(@sources));
+}
+
+# Formats a system exception - MAKE_ARG
+sub _format_e_sys_make_arg {
+    my ($e) = @_;
+    my @return;
+    for (@{$e->get_ctx()}) {
+        my ($arg_index, $arg_value) = @{$_};
+        push(@return, sprintf($S{e_sys_make_arg}, $arg_index, $arg_value));
+        my $advice
+            = $arg_value =~ qr{\.cfg\z}msx ? '-f ' . $arg_value
+            : $arg_value eq '0'            ? '-q'
+            : $arg_value eq '2'            ? '-v'
+            : $arg_value eq '3'            ? '-v -v'
+            :                                undef;
+        if (defined($advice)) {
+            push(@return, sprintf($S{e_sys_make_arg_more}, $advice));
+        }
+    }
+    return @return;
+}
+
+# Formats a system exception - MAKE_PROP_NS
+sub _format_e_sys_make_prop_ns {
+    my ($e) = @_;
+    map {sprintf($S{e_sys_make_prop_ns}, @{$_})} @{$e->get_ctx()};
+}
+
+# Formats a system exception - MAKE_PROP_VALUE
+sub _format_e_sys_make_prop_value {
+    my ($e) = @_;
+    map {sprintf($S{e_sys_make_prop_value}, @{$_})} @{$e->get_ctx()};
+}
+
+# Formats a system exception - SHELL.
+sub _format_e_sys_shell {
+    my ($e) = @_;
+    my $command = _format_shell_words(@{$e->get_ctx()->{command_list}});
+    my %value_of = (out => q{}, rc => '?', %{$e->get_ctx()});
+    return (
+        #(map {sprintf($S{e_sys_shell_err}, $_)} split("\n", $value_of{err})),
+        #(map {sprintf($S{e_sys_shell_out}, $_)} split("\n", $value_of{out})),
+        sprintf($S{e_sys_shell}, $command, $value_of{rc}),
+    );
+}
+
+# Formats a util exception.
+sub _format_e_util {
+    my ($e) = @_;
+    if (exists($E_UTIL_FORMATTER_FOR{$e->get_code()})) {
+        return $E_UTIL_FORMATTER_FOR{$e->get_code()}->($e);
+    }
+    $e;
+}
+
+# Returns a CODE to format a util config-reader exception.
+sub _format_e_util_config_func {
+    my ($id) = @_;
+    sub {
+        my ($e) = @_;
+        (   sprintf(
+                $S{'e_util_config'},
+                $e->get_ctx()->get_stack()->[-1][0]->get_value(),
+                $e->get_ctx()->get_stack()->[-1][1],
+                $S{'e_util_config_' . $id},
+            ),
+            $e->get_ctx()->as_string(),
+        );
+    };
+}
+
+# Returns a CODE to format a util config-reader exception where the ctx is the
+# locator stack.
+sub _format_e_util_config_stack_func {
+    my ($id) = @_;
+    sub {
+        my ($e) = @_;
+        my @return = (
+            _format_config_stack($e->get_ctx()),
+            sprintf(
+                $S{'e_util_config_stack_' . $id},
+                $e->get_ctx()->[-1][0]->get_value(),
+            ),
+        );
+        @return;
+    };
+}
+
+# Formats a locator exception.
+sub _format_e_util_locator_func {
+    my ($id) = @_;
+    sub {sprintf($S{'e_util_locator' . $id}, $_[0]->get_ctx()->get_value())};
+}
+
+# Formats a shell exception.
+sub _format_e_util_shell_func {
+    my ($id) = @_;
+    sub {
+        sprintf(
+            $S{'e_util_shell' . $id},
+            _format_shell_words(@{$_[0]->get_ctx()})
+        );
+    };
+}
+
+# Returns a CODE to format a exception context in a single/multi line.
+sub _format_e_func {
+    my ($id, $is_multi_line) = @_;
+    sub {
+        my ($e) = @_;
+        my @args;
+        if (defined($e->get_ctx())) {
+            @args = (ref($e->get_ctx()) || ref($e->get_ctx()) eq 'ARRAY')
+                ? @{$e->get_ctx()} : $e->get_ctx();
+        }
+        $is_multi_line
+            ? (map {sprintf($S{$id}, $_)} @args) : (sprintf($S{$id}, @args));
+    };
+}
+
+# Formats a simple reference.
+sub _format_ref {
+    my ($hash_ref) = @_;
+    local($Data::Dumper::Terse) = 1;
+    local($Data::Dumper::Indent) = 0;
+    Dumper($hash_ref);
+}
+
+# Formats words into a string suitable for used in a shell command.
+sub _format_shell_words {
+    my %option = ('delimiter' => q{ });
+    if (@_ && ref($_[0]) && ref($_[0]) eq 'HASH') {
+        %option = (%option, %{$_[0]});
+        shift();
+    }
+    my (@words) = @_;
+    join(
+        $option{'delimiter'},
+        map {my $s = $_; $s =~ s{(['"\s])}{\\$1}gmsx; $s} @words,
+    );
+}
+
+# Notification on abort of a CM command.
+sub _event_cm_abort {
+    my ($id) = @_;
+    $R->report(
+        {level => $R->QUIET, prefix => $R->PREFIX_QUIT, type => $R->TYPE_ERR},
+        $S{'cm_abort_' . $id},
+    );
+}
+
+# Notification on a project branch listing.
+sub _event_cm_branch_list {
+    my ($project, @branches) = @_;
+    $R->report(sprintf($S{'cm_branch_list'}, $project, scalar(@branches)));
+    for my $branch (@branches) {
+        $R->report({level => $R->QUIET, prefix => $R->PREFIX_NULL}, $branch);
+    }
+}
+
+# Notification on a log message to be used by a commit.
+sub _event_cm_commit_message {
+    my ($ctx) = @_;
+    $R->report(
+        {prefix => $R->PREFIX_NULL},
+        sprintf(
+            $S{'cm_commit_message'},
+            $ctx->get_info_part(), $ctx->get_user_part(), $ctx->get_auto_part(),
+        ),
+    );
+}
+
+# Notification on a skipped file in text conflict.
+sub _event_cm_conflict_text_skip {
+    my ($ctx) = @_;
+    $R->report({type => $R->TYPE_ERR}, sprintf($S{'cm_conflict_text_skip'}, $ctx));
+}
+
+# Notification for an unhandled type of tree conflict.
+sub _event_cm_conflict_tree_skip {
+    my ($ctx) = @_;
+    $R->report({type => $R->TYPE_ERR}, sprintf($S{'cm_conflict_tree_skip'}, $ctx));
+}
+
+# Warning that the tree conflict operation search may take some time.
+sub _event_cm_conflict_tree_time_warn {
+    my ($ctx) = @_;
+    $R->report({type => $R->TYPE_ERR}, sprintf($S{'cm_conflict_tree_time_warn'}, $ctx));
+}
+
+# Notification when a config entry is found.
+sub _event_config_entry {
+    my ($entry, $in_fcm1) = @_;
+    $R->report(
+        {level => $R->QUIET, prefix => $R->PREFIX_NULL},
+        $entry->as_string($in_fcm1),
+    );
+}
+
+# Notification for a configuration file open.
+sub _event_config_open {
+    my ($config_stack_ref, $level) = @_;
+    $R->report(
+        {level => (defined($level) ? $level : $R->DEBUG)},
+        sub {
+            my $value = $config_stack_ref->[-1][0]->get_value();
+            my $indent = q{ - } x (scalar(@{$config_stack_ref}) - 1);
+            sprintf($S{config_open}, $indent, $value);
+        },
+    );
+}
+
+# Notification when a config variable is undefined.
+sub _event_config_var_undef {
+    my ($entry, $symbol) = @_;
+    $R->report(
+        {type => $R->TYPE_ERR},
+        sprintf(
+            $S{'config_var_undef'},
+            $entry->get_stack()->[-1][0]->get_value(),
+            $entry->get_stack()->[-1][1],
+            $symbol,
+        ),
+    );
+}
+
+# Notification for an exception.
+sub _event_e {
+    my ($exception) = @_;
+    my @e_stack = ($exception);
+    while ( blessed($e_stack[-1])
+        &&  $e_stack[-1]->can('get_exception')
+        &&  (my $e = $e_stack[-1]->get_exception())
+    ) {
+        push(@e_stack, $e);
+    }
+    while (my $e = shift(@e_stack)) {
+        my $formatter;
+        if (blessed($e)) {
+            my $item = first {$e->isa($_->[0])} @E_FORMATTERS;
+            if ($item) {
+                $formatter = $item->[1];
+            }
+            if (!$formatter && $e->can('as_string')) {
+                $formatter = sub {$e->as_string()};
+            }
+        }
+        elsif (ref($e)) {
+            $formatter = \&_format_ref;
+        }
+        elsif ($e eq "\n") {
+            chomp($e);
+        }
+        $R->report(
+            {level => $R->FAIL, type => $R->TYPE_ERR},
+            (defined($formatter) ? $formatter->($e) : $e),
+        );
+    }
+    1;
+}
+
+# Notification when a keyword entry is found.
+sub _event_keyword_entry {
+    my ($entry) = @_;
+    if ($entry->is_implied()) {
+        return;
+    }
+    my @implied_entry_list
+        = values(%{$entry->get_ctx_of_implied()->get_entry_by_key()});
+    if (@implied_entry_list) {
+        $R->report(
+            {level => $R->QUIET, prefix => $R->PREFIX_NULL},
+            sprintf(
+                $S{keyword_loc_primary},
+                $entry->get_key(),
+                $entry->get_value(),
+            ),
+        );
+        for my $implied_entry (
+            sort {$a->get_key() cmp $b->get_key()} @implied_entry_list
+        ) {
+            $R->report(
+                {level => $R->MEDIUM, prefix => $R->PREFIX_NULL},
+                sprintf(
+                    $S{keyword_loc},
+                    $implied_entry->get_key(),
+                    $implied_entry->get_value(),
+                ),
+            );
+        }
+    }
+    else {
+        $R->report(
+            {level => $R->QUIET, prefix => $R->PREFIX_NULL},
+            sprintf($S{keyword_loc}, $entry->get_key(), $entry->get_value()),
+        );
+    }
+    my @revision_entry_list
+        = values(%{$entry->get_ctx_of_rev()->get_entry_by_key()});
+    for my $revision_entry (
+        sort {$a->get_key() cmp $b->get_key()} @revision_entry_list
+    ) {
+        $R->report(
+            {level => $R->QUIET, prefix => $R->PREFIX_NULL},
+            sprintf(
+                $S{keyword_rev},
+                $entry->get_key(),
+                $revision_entry->get_key(),
+                $revision_entry->get_value(),
+            ),
+        );
+    }
+    1;
+}
+
+# Notification of the output from a command.
+sub _event_out {
+    my ($out, $err) = @_;
+    my %option = (delimiter => q{}, prefix => $R->PREFIX_NULL);
+    if ($err) {
+        $R->report({level => $R->WARN, type => $R->TYPE_ERR, %option}, $err);
+    }
+    if ($out) {
+        $R->report({level => $R->QUIET, %option}, $out);
+    }
+}
+
+# Notification of the output from a command invoked by make/build.
+sub _event_make_build_shell_out {
+    my ($out, $err) = @_;
+    if ($err) {
+        $R->report(
+            {   level => $R->HIGH,
+                prefix => $S{'make_build_shell_out_2'},
+                type => $R->TYPE_ERR,
+            },
+            $err,
+        );
+    }
+    if ($out) {
+        $R->report(
+            {level => $R->HIGH, prefix => $S{'make_build_shell_out_1'}},
+            $out,
+        );
+    }
+}
+
+# Notification when a make destination is being set up.
+sub _event_make_dest {
+    my ($m_ctx, $authority) = @_;
+    if ($m_ctx->get_description()) {
+        $R->report(sprintf($S{make_description}, $m_ctx->get_description()));
+    }
+    $R->report(sprintf($S{make_dest}, $authority . ':' . $m_ctx->get_dest()));
+    $R->report(sprintf(
+        $S{make_mode},
+        $S{'make_mode_' . ($m_ctx->get_prev_ctx() ? 'incr' : 'new')},
+    ));
+    for my $i_ctx (@{$m_ctx->get_inherit_ctx_list()}) {
+        $R->report(sprintf($S{make_dest_use}, $i_ctx->get_dest()));
+    }
+}
+
+# Notification when performing a mirroring.
+sub _event_make_mirror {
+    my ($target, @sources) = @_;
+    $R->report(sprintf($S{make_mirror}, $target, _format_shell_words(@sources)));
+}
+
+# Notification when the multi-thread task runner initiates its workers.
+sub _event_task_workers {
+    my ($id, $n_workers) = @_;
+    my $key = 'task_workers_' . $id;
+    if (exists($S{$key})) {
+        $R->report({level => $R->HIGH}, sprintf($S{$key}, $n_workers));
+    }
+}
+
+# Notification when invoking a shell command.
+sub _event_shell {
+    my ($names_ref, $rc, $elapsed) = @_;
+    my $name = _format_shell_words(@{$names_ref});
+    my $message = sprintf($S{shell}, $rc, $elapsed, $name);
+    $R->report({level => $R->HIGH}, $message);
+}
+
+# Notification when a timer starts/ends.
+sub _event_timer {
+    my ($name, $start, $elapsed, $failed) = @_;
+    my $message;
+    if (defined($elapsed)) {
+        $message = sprintf($S{timer_done}, $name, $elapsed);
+    }
+    else {
+        my $format = '%Y-%m-%dT%H:%M:%SZ';
+        $message = sprintf(
+            $S{timer_init}, $name, strftime($format, gmtime($start)));
+    }
+    my $prefix
+        = $failed           ? $R->PREFIX_FAIL
+        : defined($elapsed) ? $R->PREFIX_DONE
+        :                     $R->PREFIX_INIT
+        ;
+    $R->report({prefix => $prefix}, $message);
+}
+
+# Notification when make-build analyse a source.
+sub _event_make_build_source_analyse {
+    my ($source, $elapse) = @_;
+    $R->report(
+        {level => $R->MEDIUM},
+        sprintf($S{make_build_source_analyse}, $elapse, $source->get_ns()),
+    );
+    for my $dep (@{$source->get_deps()}) {
+        $R->report(
+            {level => $R->HIGH},
+            sprintf($S{make_build_source_analyse_1}, reverse(@{$dep})),
+        );
+    }
+}
+
+# Notification when make-build has updated or does not need to update a target.
+sub _event_make_build_target_done {
+    my ($target, $elapsed_time) = @_;
+    my $tmpl = defined($elapsed_time)
+        ? $S{make_build_target_done_1} : $S{make_build_target_done_0};
+    $R->report(
+        {level => $R->MEDIUM},
+        sprintf(
+            $tmpl,
+            $target->get_task(),
+            (defined($elapsed_time) ? ($elapsed_time) : ()),
+            $MAKE_EXTRACT_TARGET_SYM_OF{$target->get_status()}[0],
+            $target->get_key(),
+            $target->get_ns(),
+        ),
+    );
+}
+
+# Notification when make-build a target fails to update or is failed by
+# dependencies.
+sub _event_make_build_target_fail {
+    my ($target, $elapsed_time) = @_;
+    my $tmpl = defined($elapsed_time)
+        ? $S{make_build_target_done_1} : $S{make_build_target_done_0};
+    $R->report(
+        {level => $R->FAIL, type => $R->TYPE_ERR},
+        sprintf(
+            $tmpl,
+            $target->get_task(),
+            (defined($elapsed_time) ? ($elapsed_time) : ()),
+            '!',
+            $target->get_key(),
+            $target->get_ns(),
+        ),
+    );
+}
+
+# Notification when make-build ignores a missing dependency from a target.
+sub _event_make_build_target_missing_dep {
+    $R->report(
+        {level => $R->WARN, type => $R->TYPE_ERR},
+        sprintf($S{make_build_target_missing_dep}, @_),
+    );
+}
+
+# Notification when make-build generates a target from source.
+sub _event_make_build_target_from_ns {
+    $R->report(
+        {level => $R->HIGH},
+        sprintf($S{make_build_target_from_ns}, @_),
+    );
+}
+
+# Notification when make-build chooses a list of targets to build.
+sub _event_make_build_target_select {
+    my ($target_set_ref) = @_;
+    $R->report(
+        {level => $R->HIGH},
+        sub {
+            map {
+                my $key = $_;
+                my $target = $target_set_ref->{$key};
+                sprintf(
+                    $S{make_build_target_select},
+                    $target->get_task(), $target->get_category(), $key,
+                );
+            }
+            sort keys(%{$target_set_ref});
+        },
+    );
+}
+
+# Notification when make-build checks a target for cyclic dependency.
+sub _event_make_build_target_stack {
+    my ($key, $rank, $n_deps) = @_;
+    $R->report(
+        {level => $R->HIGH},
+        sub {
+            my $indent = q{ - } x $rank;
+            my $more
+                = $n_deps ? sprintf($S{make_build_target_stack_more}, $n_deps)
+                :           q{}
+                ;
+            sprintf($S{make_build_target_stack}, $indent, $key, $more),
+        },
+    );
+}
+
+# Notification when make-build fails to update some targets.
+sub _event_make_build_targets_fail {
+    my ($targets_ref) = @_;
+    $R->report(
+        {type => $R->TYPE_ERR, level => $R->FAIL},
+        (map {
+            my $target = $_;
+            my @failed_by = @{$target->get_failed_by()};
+            my @lines;
+            if (grep {$_ eq $target->get_key()} @failed_by) {
+                push(
+                    @lines,
+                    sprintf($S{make_build_targets_fail_1}, $target->get_key()),
+                );
+            }
+            for my $failed_by_key (grep {$_ ne $target->get_key()} @failed_by) {
+                push(
+                    @lines,
+                    sprintf(
+                        $S{make_build_targets_fail_0},
+                        $target->get_key(),
+                        $failed_by_key,
+                    ),
+                );
+            }
+            @lines;
+        } sort {$a->get_key() cmp $b->get_key()} @{$targets_ref}),
+    );
+}
+
+# Notification when make-extract finished gathering information for its project
+# source trees.
+sub _event_make_extract_project_tree {
+    my %locators_of = %{$_[0]};
+    for my $ns (sort(keys(%locators_of))) {
+        my $i = 0;
+        for my $locator (@{$locators_of{$ns}}) {
+            my $format_last_mod_rev = q{};
+            if ($locator->get_last_mod_rev()) {
+                $format_last_mod_rev = sprintf(
+                    $S{'make_extract_project_tree_1'},
+                    $locator->get_last_mod_rev(),
+                );
+            }
+            $R->report(
+                sprintf(
+                    $S{'make_extract_project_tree'},
+                    $ns, $i++, $locator->get_value(), $format_last_mod_rev
+                ),
+            );
+        }
+    }
+}
+
+# Notification when make-extract used the task runner to perform tasks.
+sub _event_make_extract_runner_summary {
+    $R->report(
+        {level => $R->HIGH},
+        sprintf($S{'make_extract_runner_summary'}, @_),
+    );
+}
+
+# Notification when make-extract completes updating its targets.
+sub _event_make_extract_target_summary {
+    my ($basket) = @_;
+    for (
+        [   'status',
+            'make_extract_target_summary_d',
+            \%MAKE_EXTRACT_TARGET_SYM_OF,
+        ],
+        [   'status_of_source',
+            'make_extract_target_summary_s',
+            \%MAKE_EXTRACT_SOURCE_SYM_OF,
+        ],
+    ) {
+        my ($name, $format_name, $sym_hash_ref) = @{$_};
+        for my $key (sort keys(%{$basket->{$name}})) {
+            $R->report(sprintf(
+                $S{$format_name},
+                $basket->{$name}{$key},
+                $sym_hash_ref->{$key}[0],
+                $sym_hash_ref->{$key}[1],
+            ));
+        }
+    }
+}
+
+# Notification when make-extract ignores a symlink.
+sub _event_make_extract_symlink {
+    my ($source) = @_;
+    $R->report(
+        {type => $R->TYPE_ERR},
+        sprintf($S{make_extract_symlink}, $source->get_locator()->get_value()),
+    );
+}
+
+# Notification when make-extract updates a target.
+sub _event_make_extract_target {
+    my ($target) = @_;
+    if (!exists($MAKE_EXTRACT_TARGET_SYM_OF{$target->get_status()})) {
+        return;
+    }
+    $R->report(
+        {level => $R->MEDIUM},
+        sub {
+            my ($verbosity) = @_;
+            if ($verbosity < $R->DEBUG && $target->is_unchanged()) {
+                return;
+            }
+            my ($ns, $path) = split(qr{/}msx, $target->get_ns(), 2);
+            my %source_of = %{$target->get_source_of()};
+            my $base = delete($source_of{0});
+            my @diff_keys
+                = grep {!$source_of{$_}->is_unchanged()} keys(%source_of);
+            my @st_missing_diff_keys
+                = grep {$source_of{$_}->is_missing()} @diff_keys;
+            if (@st_missing_diff_keys) {
+                @diff_keys = @st_missing_diff_keys;
+            }
+            sprintf(
+                $S{make_extract_target},
+                $MAKE_EXTRACT_TARGET_SYM_OF{$target->get_status()}[0],
+                $MAKE_EXTRACT_SOURCE_SYM_OF{$target->get_status_of_source()}[0],
+                $ns,
+                join(
+                q{,},
+                    (   defined($base) && defined($base->get_locator())
+                            ? ($S{make_extract_target_base_yes})
+                            : ($S{make_extract_target_base_no})
+                    ),
+                    sort({$a <=> $b} @diff_keys),
+                ),
+                $path,
+            );
+        },
+    );
+}
+
+# Returns a CODE to perform a simple notification with sprintf format.
+sub _func {
+    my ($id) = @_;
+    sub {$R->report(sprintf($S{$id}, @_))};
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Event
+
+=head1 SYNOPSIS
+
+    use FCM::Util::Event;
+    $event_handler = FCM::Util::Event->new(\%attrib);
+    $event_handler->($event);
+
+=head1 DESCRIPTION
+
+Handles events wrapped as L<FCM::Context::Event|FCM::Context::Event> objects by
+stringifying and reporting them.
+
+This module is part of L<FCM::Util|FCM::Util>. See also the description of the
+$u->report() method in L<FCM::Util|FCM::Util>.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. The %attrib HASH can have the following elements:
+
+=over 4
+
+=item util
+
+The parent L<FCM::Util|FCM::Util> object.
+
+=back
+
+=item $util->event($event_ctx)
+
+Notification of an $event_ctx, which should be a blessed reference of
+L<FCM::Context::Event|FCM::Context::Event>.
+
+=back
+
+=head1 TODO
+
+Modularise?
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Exception.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Exception.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Exception.pm	(revision 5095)
@@ -0,0 +1,219 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+
+package FCM::Util::Exception;
+use base qw{FCM::Exception};
+
+use constant {
+    CLASS_LOADER         => 'CLASS_LOADER',
+    CONFIG_CONT_EOF      => 'CONFIG_CONT_EOF',
+    CONFIG_CYCLIC        => 'CONFIG_CYCLIC',
+    CONFIG_LOAD          => 'CONFIG_LOAD',
+    CONFIG_SYNTAX        => 'CONFIG_SYNTAX',
+    CONFIG_USAGE         => 'CONFIG_USAGE',
+    CONFIG_VAR_UNDEF     => 'CONFIG_VAR_UNDEF',
+    IO                   => 'IO',
+    LOCATOR_AS_INVARIANT => 'LOCATOR_AS_INVARIANT',
+    LOCATOR_BROWSER_URL  => 'LOCATOR_BROWSER_URL',
+    LOCATOR_FIND         => 'LOCATOR_FIND',
+    LOCATOR_KEYWORD_LOC  => 'LOCATOR_KEYWORD_LOC',
+    LOCATOR_KEYWORD_REV  => 'LOCATOR_KEYWORD_REV',
+    LOCATOR_READER       => 'LOCATOR_READER',
+    LOCATOR_TYPE         => 'LOCATOR_TYPE',
+    SHELL_OPEN3          => 'SHELL_OPEN3',
+    SHELL_OS             => 'SHELL_OS',
+    SHELL_SIGNAL         => 'SHELL_SIGNAL',
+    SHELL_WHICH          => 'SHELL_WHICH',
+};
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Exception
+
+=head1 SYNOPSIS
+
+    use FCM::Util::Exception;
+    eval {
+        # something does not work ...
+        FCM::Util::Exception->throw($code, $ctx, $exception);
+    };
+    if (my $e = $@) {
+        if (FCM::Util::Exception->caught($e)) {
+            # do something ...
+        }
+        else {
+            # do something else ...
+        }
+    }
+
+=head1 DESCRIPTION
+
+This exception represents an error condition in an FCM utility. It is a
+sub-class of L<FCM::Exception|FCM::Exception>.
+
+=head1 CONSTANTS
+
+The following are known error code:
+
+=over 4
+
+=item CLASS_LOADER
+
+L<FCM::Util::ClassLoader|FCM::Util::ClassLoader>: The utility fails to load the
+specified class. The $e->get_ctx() method returns the name of the class it fails
+to load.
+
+=item CONFIG_CONT_EOF
+
+L<FCM::Util::ConfigReader|FCM::Util::ConfigReader>: The last line of the
+configuration file has a continuation marker. Expects the $e->get_ctx() method
+to return the L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object that
+represents the problem entry.
+
+=item CONFIG_CYCLIC
+
+L<FCM::Util::ConfigReader|FCM::Util::ConfigReader>: There is a cyclic dependency
+in the include hierarchy. Expects the $e->get_ctx() method to return an ARRAY
+reference of the locator stack. (The last element of the ARRAY is the top of the
+stack, and each element is a 2-element ARRAY reference, where the first element
+is a L<FCM::Context::Locator|FCM::Context::Locator> object and the second
+element is the line number.)
+
+=item CONFIG_LOAD
+
+L<FCM::Util::ConfigReader|FCM::Util::ConfigReader>: An error occurs when loading
+a configuration file. Expects the $e->get_ctx() method to return an ARRAY
+reference of the locator stack. (The last element of the ARRAY is the top of the
+stack, and each element is a 2-element ARRAY reference, where the first element
+is a L<FCM::Context::Locator|FCM::Context::Locator> object and the second
+element is the line number.)
+
+=item CONFIG_SYNTAX
+
+L<FCM::Util::ConfigReader|FCM::Util::ConfigReader>: A syntax error in the
+declaration. Expects the $e->get_ctx() method to return the
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object that represents
+the problem entry.
+
+=item CONFIG_USAGE
+
+L<FCM::Util::ConfigReader|FCM::Util::ConfigReader>: An attempt to assign a value
+to a reserved variable. Expects the $e->get_ctx() method to return the
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object that represents
+the problem entry.
+
+=item CONFIG_VAR_UNDEF
+
+L<FCM::Util::ConfigReader|FCM::Util::ConfigReader>: References to an undefined
+variable. Expects the $e->get_ctx() method to return the
+L<FCM::Context::ConfigEntry|FCM::Context::ConfigEntry> object that represents
+the problem entry, and $e->get_exception() to return a string that looks like
+"undef($symbol)" where $symbol is the symbol that references the variable.
+
+=item IO
+
+L<FCM::Util::IO|FCM::Util::IO>: I/O exception. Expects $e->get_ctx() to return
+the path that triggers the exception, and the $e->get_exception() to return the
+$! string.
+
+=item LOCATOR_AS_INVARIANT
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The invariant value of the locator
+cannot be determined. Expects $e->get_ctx() method to return the associated
+L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item LOCATOR_BROWSER_URL
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The locator cannot be mapped to a
+browser URL. Expects $e->get_ctx() method to return the associated
+L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item LOCATOR_FIND
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The locator does not exist. Expects
+$e->get_ctx() method to return the associated
+L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item LOCATOR_KEYWORD_LOC
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The location keyword as specified in
+the locator is not defined. Expects $e->get_ctx() method to return the
+associated L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item LOCATOR_KEYWORD_REV
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The revision keyword as specified in
+the locator is not defined. Expects $e->get_ctx() method to return the
+associated L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item LOCATOR_READER
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The locator cannot be read. Expects
+$e->get_ctx() method to return the associated
+L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item LOCATOR_TYPE
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The locator type cannot be determined
+or cannot be supported. Expects $e->get_ctx() method to return the associated
+L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item LOCATOR_WHEN
+
+L<FCM::Util::Locator|FCM::Util::Locator>: The last modified date and revision of
+the locator cannot be determined. Expects $e->get_ctx() method to return the
+associated L<FCM::Context::Locator|FCM::Context::Locator> object.
+
+=item SHELL_OPEN3
+
+L<FCM::Util::Shell|FCM::Util::Shell>: The utility fails to invoke
+IPC::Open3::open3(). Expects $e->get_ctx() to return an ARRAY reference of the
+command line, and $e->get_exception() to return the error from open3().
+
+=item SHELL_OS
+
+L<FCM::Util::Shell|FCM::Util::Shell>: An OS error occurs when invoking a shell
+command. Expects $e->get_ctx() to return an ARRAY reference of the
+command line, and $e->get_exception() to return $!.
+
+=item SHELL_SIGNAL
+
+L<FCM::Util::Shell|FCM::Util::Shell>: The system receives a signal when invoking
+a shell command. Expects $e->get_ctx() to return an ARRAY reference of the
+command line, and $e->get_exception() to return the signal number.
+
+=item SHELL_WHICH
+
+L<FCM::Util::Shell|FCM::Util::Shell>: The shell command does not exist in the
+PATH. Expects $e->get_ctx() to return an ARRAY reference of the command line.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator.pm	(revision 5095)
@@ -0,0 +1,767 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Locator;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Keyword;
+use FCM::Context::Locator;
+use FCM::Util::Exception;
+use FCM::Util::Locator::FS;
+use FCM::Util::Locator::SSH;
+use FCM::Util::Locator::SVN;
+
+# URI prefix for FCM scheme
+use constant PREFIX => 'fcm';
+
+# Methods of an instance of this class
+my %ACTION_OF = (
+    as_invariant     => \&_as_invariant,
+    as_keyword       => \&_as_keyword,
+    as_normalised    => \&_as_normalised,
+    as_parsed        => \&_as_parsed,
+    browser_url      => \&_browser_url,
+    cat              => _locator_func(sub {$_[0]->cat(@_[1 .. $#_])}),
+    dir              => _locator_func(sub {$_[0]->dir($_[1])}),
+    export           => \&_export,
+    export_ok        => \&_export_ok,
+    find             => \&_find,
+    kw_ctx           => sub {$_[0]->{kw_ctx}},
+    kw_ctx_load      => \&_kw_ctx_load,
+    kw_iter          => \&_kw_iter,
+    kw_load_rev_prop => \&_kw_load_rev_prop,
+    kw_prefix        => sub {PREFIX},
+    origin           => _locator_func(sub {$_[0]->origin($_[1])}),
+    reader           => \&_reader,
+    rel2abs          => \&_rel2abs,
+    test_exists      => \&_test_exists,
+    trunk_at_head    => \&_trunk_at_head,
+    what_type        => \&_what_type,
+    up_iter          => \&_up_iter,
+);
+# Default browser config
+our %BROWSER_CONFIG = (
+    comp_pat => qr{\A // ([^/]+) /+ ([^/]+)_svn /*(.*) \z}xms,
+    rev_tmpl => '@{1}',
+    loc_tmpl => 'http://{1}/projects/{2}/intertrac/source:/{3}{4}',
+);
+# Alias to the exception class
+my $E = 'FCM::Util::Exception';
+# Loaders for keyword context from configuration entries
+my %KEYWORD_CFG_LOADER_FOR = (
+    'location'
+    => \&_kw_ctx_load_loc,
+    'revision'
+    => \&_kw_ctx_load_rev,
+    'browser.comp-pat'
+    => _kw_ctx_load_browser_func(sub {$_[0]->set_comp_pat($_[1])}),
+    'browser.loc-tmpl'
+    => _kw_ctx_load_browser_func(sub {$_[0]->set_loc_tmpl($_[1])}),
+    'browser.rev-tmpl'
+    => _kw_ctx_load_browser_func(sub {$_[0]->set_rev_tmpl($_[1])}),
+);
+my @KEYWORD_IMPLIED_SUFFICES = (
+    [branches => [qw{-br _br}]],
+    [tags     => [qw{-tg _tg}]],
+    [trunk    => [qw{-tr _tr}]],
+);
+# Patterns for parsing keyword configurations, etc
+my %PATTERN_OF = (
+    # Assignment delimiter, e.g. "label = value"
+    delim_of_assign  => qr/\s* = \s*/xms,
+    # Key of a FCM location keyword, e.g. "um" in "fcm:um"
+    parse => qr/
+        \A              (?# start)
+        ([\w\+\-\.]+)   (?# capture 1, 1 or more word, plus, minus or dot)
+        (.*) \z         (?# capture 2, rest of string)
+    /xms,
+);
+# The name of the property where revision keywords are set in primary locations
+our $REV_PROP_NAME = 'fcm:revision';
+# The known types
+our @TYPES = qw{svn ssh fs};
+# The classes for the known types
+our %TYPE_UTIL_CLASS_OF = (
+    fs  => 'FCM::Util::Locator::FS',
+    ssh => 'FCM::Util::Locator::SSH',
+    svn => 'FCM::Util::Locator::SVN',
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   types              => {isa => '@', default => [@TYPES]},
+        type_util_class_of => {isa => '%', default => {%TYPE_UTIL_CLASS_OF}},
+        type_util_of       => '%',
+        util               => '&',
+    },
+    {   init => sub {
+            my ($attrib_ref) = @_;
+            my $K = 'FCM::Context::Keyword';
+            $attrib_ref->{browser_config}
+                = $K->BROWSER_CONFIG->new(\%BROWSER_CONFIG);
+            $attrib_ref->{kw_ctx} = $K->new();
+            for my $type (@{$attrib_ref->{types}}) {
+                if (!exists($attrib_ref->{type_util_of}{$type})) {
+                    my $class = $attrib_ref->{type_util_class_of}{$type};
+                    $attrib_ref->{type_util_of}{$type} = $class->new({
+                        type_util_of => $attrib_ref->{type_util_of},
+                        util         => $attrib_ref->{util},
+                    });
+                }
+            }
+        },
+        action_of => \%ACTION_OF,
+    },
+);
+
+# Determines the invariant value of the $locator.
+sub _as_invariant {
+    my ($attrib_ref, $locator) = @_;
+    if ($locator->get_value_level() < $locator->L_INVARIANT) {
+        _as_normalised($attrib_ref, $locator);
+        my $util_of_type = _util_of_type($attrib_ref, $locator);
+        if ($util_of_type->can('as_invariant')) {
+            my $value = eval {
+                $util_of_type->as_invariant($locator->get_value());
+            };
+            if (my $e = $@) {
+                return $E->throw($E->LOCATOR_AS_INVARIANT, $locator, $e);
+            }
+            if ($value) {
+                $locator->set_value($value);
+                $locator->set_value_level($locator->L_INVARIANT);
+            }
+        }
+    }
+    $locator->get_value();
+}
+
+# Determines the keyword value of the $locator.
+sub _as_keyword {
+    my ($attrib_ref, $locator) = @_;
+    _as_normalised($attrib_ref, $locator);
+    my $util_of_type = _util_of_type($attrib_ref, $locator);
+    my ($target, $rev) = $util_of_type->parse($locator->get_value());
+    my $kw_iter = _kw_iter($attrib_ref, $locator);
+    my $entry;
+    while (!defined($entry) && defined($entry = $kw_iter->())) {
+        if ($entry->is_implied()) {
+            $entry = undef;
+        }
+    }
+    if (defined($entry)) {
+        $target
+            = PREFIX . ':' . $entry->get_key()
+            . substr($target, length($entry->get_value()));
+    }
+    if (defined($rev) && $util_of_type->can_work_with_rev($rev)) {
+        my $transformed_rev = _transform_rev_keyword(
+            $attrib_ref, $locator, $rev,
+            sub {$_[0]->get_entry_by_value($_[1])},
+            sub {$_[0]->get_key()},
+        );
+        if ($transformed_rev) {
+            $rev = $transformed_rev;
+        }
+    }
+    scalar($util_of_type->parse($target, $rev));
+}
+
+# Determines the normalised value of the $locator.
+sub _as_normalised {
+    my ($attrib_ref, $locator) = @_;
+    if ($locator->get_value_level() < $locator->L_NORMALISED) {
+        _as_parsed($attrib_ref, $locator);
+        my $util_of_type = _util_of_type($attrib_ref, $locator);
+        my ($target, $rev) = $util_of_type->parse($locator->get_value());
+        if (defined($rev) && !$util_of_type->can_work_with_rev($rev)) {
+            my $origin = $ACTION_OF{origin}->(
+                $attrib_ref, FCM::Context::Locator->new($target),
+            );
+            $rev = _transform_rev_keyword(
+                $attrib_ref, $origin, lc($rev),
+                sub {$_[0]->get_entry_by_key($_[1])},
+                sub {$_[0]->get_value()},
+            );
+            if (!$rev) {
+                return $E->throw($E->LOCATOR_KEYWORD_REV, $locator);
+            }
+        }
+        $locator->set_value(scalar($util_of_type->parse($target, $rev)));
+        $locator->set_value_level($locator->L_NORMALISED);
+    }
+    $locator->get_value();
+}
+
+# Determines the parsed value of the $locator.
+sub _as_parsed {
+    my ($attrib_ref, $locator) = @_;
+    if ($locator->get_value_level() < $locator->L_PARSED) {
+        my $value = $locator->get_value();
+        my ($scheme, $sps) = $attrib_ref->{util}->uri_match($value);
+        if ($scheme && $scheme eq PREFIX) {
+            my ($key, $trail) = $sps =~ $PATTERN_OF{parse};
+            my $entry = $attrib_ref->{kw_ctx}->get_entry_by_key(lc($key));
+            if (!defined($entry)) {
+                return $E->throw($E->LOCATOR_KEYWORD_LOC, $locator);
+            }
+            $value = $entry->get_value() . $trail;
+        }
+        $locator->set_value($value);
+        $locator->set_value_level($locator->L_PARSED);
+    }
+    $locator->get_value();
+}
+
+# Determines the browser URL of the $locator.
+sub _browser_url {
+    my ($attrib_ref, $locator) = @_;
+    _as_normalised($attrib_ref, $locator);
+    my %GET = (
+        comp_pat => sub {$_[0]->get_comp_pat()},
+        loc_tmpl => sub {$_[0]->get_loc_tmpl()},
+        rev_tmpl => sub {$_[0]->get_rev_tmpl()},
+    );
+    my %value_of = map {($_, undef)} keys(%GET);
+    my $iter = _kw_iter($attrib_ref, $locator);
+    while (my $entry = $iter->()) {
+        if (defined($entry->get_browser_config())) {
+            for my $key (keys(%value_of)) {
+                if (!defined($value_of{$key})) {
+                    my $value = $GET{$key}->($entry->get_browser_config());
+                    if (defined($value)) {
+                        $value_of{$key} = $value;
+                    }
+                }
+            }
+        }
+    }
+    for my $key (keys(%value_of)) {
+        if (!$value_of{$key}) {
+            $value_of{$key} = $GET{$key}->($attrib_ref->{browser_config});
+        }
+    }
+    # Extracts components from the locator
+    my $origin = $ACTION_OF{origin}->($attrib_ref, $locator);
+    my ($target, $rev)
+        = _util_of_type($attrib_ref, $origin)->parse($origin->get_value());
+    my ($scheme, $sps) = $attrib_ref->{util}->uri_match($target);
+    if (!$sps) {
+        return $E->throw($E->LOCATOR_BROWSER_URL, $locator);
+    }
+    my @matches = $sps =~ $value_of{comp_pat};
+    if (!@matches) {
+        return $E->throw($E->LOCATOR_BROWSER_URL, $locator);
+    }
+    # Places the components into the template
+    my $result = $value_of{loc_tmpl};
+    for my $field_number (1 .. @matches) {
+        my $match = $matches[$field_number - 1];
+        $result =~ s/\{ $field_number \}/$match/xms;
+    }
+    my $rev_field_number = scalar(@matches) + 1;
+    my $rev_string = q{};
+    if ($rev) {
+        $rev_string = $value_of{rev_tmpl};
+        $rev_string =~ s/\{1\}/$rev/xms;
+    }
+    $result =~ s/\{ $rev_field_number \}/$rev_string/xms;
+    return $result;
+}
+
+# Exports $locator to a $dest.
+sub _export {
+    my ($attrib_ref, $locator, $dest) = @_;
+    if (_util_of_type($attrib_ref, $locator)->can('export')) {
+        _as_normalised($attrib_ref, $locator);
+        my $util_of_type = _util_of_type($attrib_ref, $locator);
+        $util_of_type->export($locator->get_value(), $dest);
+    }
+}
+
+# Returns true if it is possible to safely export $locator.
+sub _export_ok {
+    my ($attrib_ref, $locator) = @_;
+    my $util_of_type = _util_of_type($attrib_ref, $locator);
+    _as_parsed($attrib_ref, $locator);
+    $util_of_type->can('export_ok')
+        && $util_of_type->export_ok($locator->get_value());
+}
+
+# Searches the directory tree of $locator. Calls a function for each node.
+sub _find {
+    my ($attrib_ref, $locator, $callback) = @_;
+    _as_invariant($attrib_ref, $locator);
+    my $type = $locator->get_type();
+    my $util_of_type = _util_of_type($attrib_ref, $locator);
+    my $found = $util_of_type->find(
+        $locator->get_value(),
+        sub {
+            my ($value, $target_attrib_ref) = @_;
+            my $new_locator;
+            if ($value eq $locator->get_value()) {
+                $locator->set_last_mod_rev($target_attrib_ref->{last_mod_rev});
+                $locator->set_last_mod_time($target_attrib_ref->{last_mod_time});
+                $new_locator = $locator;
+            }
+            else {
+                $new_locator = FCM::Context::Locator->new($value, {
+                    last_mod_rev  => $target_attrib_ref->{last_mod_rev},
+                    last_mod_time => $target_attrib_ref->{last_mod_time},
+                    type          => $type,
+                    value_level   => FCM::Context::Locator->L_INVARIANT,
+                });
+            }
+            $callback->($new_locator, $target_attrib_ref);
+        },
+    );
+    return ($found ? $found : $E->throw($E->LOCATOR_FIND, $locator));
+}
+
+# Loads the keyword context from configuration entries.
+sub _kw_ctx_load {
+    my ($attrib_ref, @config_entry_iterators) = @_;
+    for my $config_entry_iterator (@config_entry_iterators) {
+        while (my $config_entry = $config_entry_iterator->()) {
+            my $handler = $KEYWORD_CFG_LOADER_FOR{$config_entry->get_label()};
+            if (defined($handler)) {
+                $handler->($attrib_ref, $config_entry);
+            }
+        }
+    }
+}
+
+# Loads a location keyword browser config from a configuration entry.
+sub _kw_ctx_load_browser_func {
+    my ($setter_ref) = @_;
+    sub {
+        my ($attrib_ref, $c_entry) = @_;
+        my %entry_by_key = %{$attrib_ref->{kw_ctx}->get_entry_by_key()};
+        if (@{$c_entry->get_ns_list()}) {
+            for my $key (@{$c_entry->get_ns_list()}) {
+                if (exists($entry_by_key{$key})) {
+                    $setter_ref->(
+                        $entry_by_key{$key}->get_browser_config(),
+                        $c_entry->get_value(),
+                    );
+                }
+            }
+        }
+        else {
+            $setter_ref->($attrib_ref->{browser_config}, $c_entry->get_value());
+        }
+    }
+}
+
+# Loads the location keyword context from a configuration entry.
+sub _kw_ctx_load_loc {
+    my ($attrib_ref, $c_entry) = @_;
+    my $key   = lc($c_entry->get_ns_list()->[0]);
+    my $value = $c_entry->get_value();
+    my $M     = $c_entry->get_modifier_of();
+    my $type  = (exists($M->{type}) ? $M->{type} : undef);
+    if (exists($M->{primary}) && $M->{primary}) {
+        my $locator = FCM::Context::Locator->new($value, {type => $type});
+        _as_normalised($attrib_ref, $locator);
+        my $entry = $attrib_ref->{kw_ctx}->add_entry(
+            $key, $locator->get_value(), {type => $locator->get_type()},
+        );
+        for (@KEYWORD_IMPLIED_SUFFICES) {
+            my ($value_suffix, $key_suffix_ref) = @{$_};
+            my $locator = $ACTION_OF{cat}->($attrib_ref, $locator, $value_suffix);
+            my $value = $locator->get_value();
+            for my $key_suffix (@{$key_suffix_ref}) {
+                my $implied_entry = $entry->get_ctx_of_implied()->add_entry(
+                    $key . $key_suffix, $value, {implied => 1, type => $type},
+                );
+                $attrib_ref->{kw_ctx}->add_entry($implied_entry);
+            }
+        }
+    }
+    else {
+        $attrib_ref->{kw_ctx}->add_entry($key, $value, {type => $type});
+    }
+}
+
+# Loads the revision keyword context from a configuration entry.
+sub _kw_ctx_load_rev {
+    my ($attrib_ref, $c_entry) = @_;
+    for my $ns (map {lc($_)} @{$c_entry->get_ns_list()}) {
+        my ($key, $r_key) = split(qr{:}msx, $ns);
+        my $entry = $attrib_ref->{kw_ctx}->get_entry_by_key($key);
+        if (defined($entry)) {
+            $entry->get_ctx_of_rev()->add_entry($r_key, $c_entry->get_value());
+        }
+    }
+}
+
+# Returns an iterator that returns location keyword entry context for $locator.
+sub _kw_iter {
+    my ($attrib_ref, $locator, $callback_ref) = @_;
+    my $origin = $ACTION_OF{origin}->($attrib_ref, $locator);
+    my $iter = _up_iter($attrib_ref, $origin);
+    sub {
+        while (my ($leader) = $iter->()) {
+            my $entry = $attrib_ref->{kw_ctx}->get_entry_by_value($leader);
+            if (defined($entry)) {
+                if (defined($callback_ref)) {
+                    $callback_ref->($entry);
+                }
+                return $entry;
+            }
+        }
+        return;
+    }
+}
+
+# Loads revision keywords from the "fcm:revision" property of the locator value
+# of a location keyword entry.
+sub _kw_load_rev_prop {
+    my ($attrib_ref, $entry) = @_;
+    if ($entry->get_loaded_rev_prop() || $entry->get_implied()) {
+        return;
+    }
+    $entry->set_loaded_rev_prop(1);
+    my $locator = FCM::Context::Locator->new(
+        $entry->get_value(), {type => $entry->get_type()},
+    );
+    my $property = _read_property($attrib_ref, $locator, $REV_PROP_NAME);
+    if (!$property) {
+        return;
+    }
+    for my $line (split(qr{\s*\n\s*}xms, $property)) {
+        my ($key, $value) = split($PATTERN_OF{delim_of_assign}, $line, 2);
+        $entry->get_ctx_of_rev()->add_entry($key, $value);
+    }
+}
+
+# Returns a function to "transform" a $locator to another locator.
+sub _locator_func {
+    my ($impl_ref) = @_;
+    sub {
+        my ($attrib_ref, $locator, @args) = @_;
+        _as_parsed($attrib_ref, $locator);
+        my $util_of_type = _util_of_type($attrib_ref, $locator);
+        FCM::Context::Locator->new(
+            scalar($impl_ref->($util_of_type, $locator->get_value(), @args)),
+            {type => $locator->get_type()},
+        );
+    }
+}
+
+# Returns a file handle to read the content of $locator.
+sub _reader {
+    my ($attrib_ref, $locator) = @_;
+    _as_normalised($attrib_ref, $locator);
+    my $reader = eval {
+        _util_of_type($attrib_ref, $locator)->reader($locator->get_value());
+    };
+    if (my $e = $@) {
+        return $E->throw($E->LOCATOR_READER, $locator, $e);
+    }
+    if (!defined($reader)) {
+        return $E->throw($E->LOCATOR_READER, $locator);
+    }
+    return $reader;
+}
+
+# Returns the contents in a named property of $locator
+sub _read_property {
+    my ($attrib_ref, $locator, $prop_name) = @_;
+    _as_normalised($attrib_ref, $locator);
+    my $util_of_type = _util_of_type($attrib_ref, $locator);
+    eval {$util_of_type->read_property($locator->get_value(), $prop_name)};
+}
+
+# If $locator->get_value() is a relative path, set it to a absolute path
+# base on $locator_base->get_value(), if $locator->get_type() does not differ
+# from $locator_base->get_type().
+sub _rel2abs {
+    my ($attrib_ref, $locator, $locator_base) = @_;
+    _as_normalised($attrib_ref, $locator_base);
+    if (    $locator->get_type()
+        &&  $locator->get_type() ne $locator_base->get_type()
+    ) {
+        return $locator;
+    }
+    my $value = $locator->get_value();
+    if (    $attrib_ref->{util}->uri_match($value)
+        ||  index($value, '/') == 0
+        ||  index($value, '~') == 0
+    ) {
+        return $locator;
+    }
+    my $new_locator = $ACTION_OF{cat}->($attrib_ref, $locator_base, $value);
+    $locator->set_value($new_locator->get_value());
+    $locator->set_value_level($new_locator->get_value_level());
+    $locator;
+}
+
+# Test if $locator location exists.
+sub _test_exists {
+    my ($attrib_ref, $locator) = @_;
+    _as_normalised($attrib_ref, $locator);
+    _util_of_type($attrib_ref, $locator)->test_exists($locator->get_value());
+}
+
+# Transforms a revision from/to a keyword, and returns the result.
+sub _transform_rev_keyword {
+    my ($attrib_ref, $locator, $rev, $rev_entry_func, $result_func) = @_;
+    my $iter = _kw_iter($attrib_ref, $locator);
+    while (my $entry = $iter->()) {
+        # $entry->get_ctx_of_rev()->get_entry_by_key($rev)
+        # $entry->get_ctx_of_rev()->get_entry_by_value($rev)
+        if (defined($entry->get_ctx_of_rev())) {
+            if (!$rev_entry_func->($entry->get_ctx_of_rev(), $rev)) {
+                _kw_load_rev_prop($attrib_ref, $entry);
+            }
+        }
+        if (defined($entry->get_ctx_of_rev())) {
+            my $rev_entry = $rev_entry_func->($entry->get_ctx_of_rev(), $rev);
+            if (defined($rev_entry)) {
+                # $rev_entry->get_value()
+                # $rev_entry->get_key()
+                return $result_func->($rev_entry);
+            }
+        }
+    }
+    return;
+}
+
+# Returns a string to represent the relative path to the latest main tree.
+sub _trunk_at_head {
+    my ($attrib_ref, $locator) = @_;
+    my $orig_value = $locator->get_value();
+    my $util_of_type = _util_of_type($attrib_ref, $locator);
+    my $head_value = $util_of_type->trunk_at_head($orig_value);
+    if (!$head_value || $head_value eq $orig_value) {
+        return;
+    }
+    return FCM::Context::Locator->new($head_value, {
+        type => $locator->get_type(),
+    });
+}
+
+# Determines the type of the $locator.
+sub _what_type {
+    my ($attrib_ref, $locator) = @_;
+    if (!defined($locator->get_type())) {
+        _as_parsed($attrib_ref, $locator);
+        TYPE:
+        for my $key (@{$attrib_ref->{types}}) {
+            if (!exists($attrib_ref->{type_util_of}{$key})) {
+                next TYPE;
+            }
+            my $util_of_type = $attrib_ref->{type_util_of}{$key};
+            if ($util_of_type->can_work_with($locator->get_value())) {
+                $locator->set_type($key);
+                last TYPE;
+            }
+        }
+    }
+    return $locator->get_type();
+}
+
+# Returns an iterator that walks up the hierarchy of the $locator.
+sub _up_iter {
+    my ($attrib_ref, $locator) = @_;
+    my $util_of_type = _util_of_type($attrib_ref, $locator);
+    my ($target, $revision) = $util_of_type->parse($locator->get_value());
+    my $leader = $target;
+    sub {
+        if (!defined($leader)) {
+            $leader = $target;
+            return;
+        }
+        my $return = $leader;
+        $leader = $util_of_type->dir($return);
+        if ($return eq $leader) {
+            $leader = undef;
+        }
+        return $util_of_type->parse($return, $revision);
+    };
+}
+
+# Returns the utility that implements the functionality for the $locator's type.
+sub _util_of_type {
+    my ($attrib_ref, $locator) = @_;
+    my $type = _what_type($attrib_ref, $locator);
+    if (exists($attrib_ref->{type_util_of}{$type})) {
+        return $attrib_ref->{type_util_of}{$type};
+    }
+    return $E->throw($E->LOCATOR_TYPE, $locator);
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Locator
+
+=head1 SYNOPSIS
+
+    use FCM::Util;
+    my $util = FCM::Util->new(\%attrib);
+
+    # Usage
+    $ctx = $util->loc_kw_ctx();
+    @location_keyword_ctx_list = $util->loc_kw_ctx($locator);
+
+    $type = $util->loc_what_type($locator);
+    ($time, $rev) = $util->loc_when_modified($locator);
+
+    $locator_value = $util->loc_as_normalised($locator);
+    $locator_value = $util->loc_as_invariant( $locator);
+    $locator_value = $util->loc_as_keyword(   $locator);
+
+    $url = $util->loc_browser_url($locator);
+
+    $locator_of_parent = $util->loc_dir($locator);
+    $locator_of_child  = $util->loc_cat($locator, @paths);
+    $locator_of_origin = $util->loc_origin($locator);
+
+    $iter = $util->loc_up_iter($locator);
+    while (my $value = $iter->()) {
+        # ...
+    }
+
+    $reader = $util->loc_reader($locator);
+
+=head1 DESCRIPTION
+
+This module is part of L<FCM::Util|FCM::Util>. It implements the loc_* methods.
+
+=head1 IMPLEMENTATION
+
+The manipulations of locator values rely on objects with the following
+interface:
+
+=over 4
+
+=item $util_of_type->as_invariant($locator_value)
+
+Should return the invariant form of $locator_value.
+
+=item $util_of_type->can_work_with($locator_value)
+
+Should return true if it can work with $locator_value, i.e. $locator_value is a
+valid type of locator for the utility.
+
+=item $util_of_type->can_work_with_rev($revision_value)
+
+Should return true if it can work with $revision_value, i.e. $revision_value is
+a valid revision for the utility.
+
+=item $util_of_type->cat($locator_value, @paths)
+
+Should concatenate $locator_value and @paths with appropriate separators and
+returns the result.
+
+=item $util_of_type->dir($locator_value)
+
+Should return the parent (directory) of $locator_value.
+
+=item $util_of_type->export($locator_value,$dest)
+
+Optional. Exports a clean directory tree from $locator_value to $dest.
+
+=item $util_of_type->export_ok($locator_value)
+
+Optional. Returns true if it is safe to export $locator_value. E.g. it is not
+safe to export a SVN working copy, because it may contain unversioned items.
+
+=item $util_of_type->find($locator_value,$callback)
+
+Should search the directory tree in $locator_value and for each node (directory
+or file, inclusive of $locator_value), invoke
+$callback->($locator_value_of_child,\%attrib_of_child). %attrib_of_child should
+contain the elements as described by $util->find($locator,$callback).
+
+=item $util_of_type->origin($locator_value)
+
+Should return the origin of $locator_value. E.g. the URL of a Subversion working
+copy.
+
+=item $util_of_type->parse($locator_value)
+
+Should return an absolute and tidied version of $locator_value. In list context,
+should return a 2-element list, separate the scalar context return value into
+the components (PATH,REV).
+
+=item $util_of_type->reader($locator_value)
+
+Should return a file handle for reading the content of $locator_value.
+
+=item $util_of_type->read_property($locator_value,$property_name)
+
+Should return the value of the named property in $locator_value, or undef if
+not relevant for the $locator_value.
+
+=item $util_of_type->test_exists($locator_value)
+
+Should return a true value if the location $locator_value exists.
+
+=item $util_of_type->trunk_at_head($locator_value)
+
+If relevant, should append a string to $locator_value that represents the
+recommended relative path to the latest version of the main tree of a project of
+this type. E.g. for "svn", this should be "$locator_value/trunk@HEAD".
+
+=back
+
+=head1 CONSTANTS
+
+These global variables are for reference only. Their values should not be
+modified. Instead, use the appropriate attributes of the $class->new(\%attrib)
+method to modify the behaviour.
+
+=over 4
+
+=item %FCM::Util::Locator::BROWSER_CONFIG
+
+The default browser configuration.
+
+=item FCM::Util::Locator::PREFIX
+
+The URI prefix for a FCM location keyword.
+
+=item $FCM::Util::Locator::REV_PROP_NAME
+
+The name of the property where revision keywords are set in primary locations.
+
+=item @FCM::Util::Locator::TYPES
+
+The known locator types.
+
+=item %FCM::Util::Locator::TYPE_UTIL_CLASS_OF
+
+Maps the known locator types with their utility classes.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/FS.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/FS.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/FS.pm	(revision 5095)
@@ -0,0 +1,203 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Locator::FS;
+use base qw{FCM::Class::CODE};
+
+use File::Basename qw{dirname};
+use File::Find qw{};
+use File::Spec;
+
+our %ACTION_OF = (
+    can_work_with     => sub {1},
+    can_work_with_rev => sub {},
+    cat               => \&_cat,
+    dir               => \&_dir,
+    find              => \&_find,
+    origin            => \&_parse,
+    parse             => \&_parse,
+    reader            => \&_reader,
+    read_property     => sub {},
+    test_exists       => \&_test_exists,
+    trunk_at_head     => sub {},
+);
+
+# Creates the class.
+__PACKAGE__->class({util => '&'}, {action_of => \%ACTION_OF});
+
+# Joins @paths to the end of $value.
+sub _cat {
+    my ($attrib_ref, $value, @paths) = @_;
+    _parse(
+        $attrib_ref,
+        File::Spec->catfile(scalar(_parse($attrib_ref, $value)), @paths),
+    );
+}
+
+# Returns the directory name of $value.
+sub _dir {
+    my ($attrib_ref, $value) = @_;
+    dirname(scalar(_parse($attrib_ref, $value)));
+}
+
+# Searches directory tree.
+sub _find {
+    my ($attrib_ref, $value, $callback) = @_;
+    my $found;
+    File::Find::find(
+        sub {
+            $found ||= 1;
+            my $path = $File::Find::name;
+            my $ns = File::Spec->abs2rel($path, $value);
+            if ($ns eq q{.}) {
+                $ns = q{};
+            }
+            else {
+                for my $name (split(q{/}, $ns)) {
+                    if (index($name, q{.}) == 0) {
+                        return; # ignore Unix hidden/system files
+                    }
+                }
+            }
+            my $last_mod_time = (-l $path ? lstat($path) : stat($path))[9];
+            $callback->(
+                $path,
+                {   is_dir        => scalar(-d $path),
+                    last_mod_rev  => undef,
+                    last_mod_time => $last_mod_time,
+                    ns            => $ns,
+                },
+            );
+        },
+        $value,
+    );
+    return $found;
+}
+
+# Returns $value in scalar context, or ($value,undef) in list context.
+sub _parse {
+    my ($attrib_ref, $value) = @_;
+    $value = $attrib_ref->{util}->file_tilde_expand($value);
+    $value = File::Spec->rel2abs($value);
+    my ($vol, $dir_name, $base) = File::Spec->splitpath($value);
+    my @dir_names;
+    my %HANDLER_OF = (
+        q{}   => sub {push(@dir_names, $_[0])},
+        q{.}  => sub {},
+        q{..} => sub {if (@dir_names > 1) {pop(@dir_names)}},
+    );
+    for my $name (File::Spec->splitdir($dir_name)) {
+        my $handler
+            = exists($HANDLER_OF{$name}) ? $HANDLER_OF{$name} : $HANDLER_OF{q{}};
+        $handler->($name);
+    }
+    $value = File::Spec->catpath($vol, File::Spec->catdir(@dir_names), $base);
+    return (wantarray() ? ($value, undef) : $value);
+}
+
+# Returns a reader (file handle) for a given file system value.
+sub _reader {
+    my ($attrib_ref, $value) = @_;
+    $value = _parse($attrib_ref, $value);
+    open(my $handle, '<', $value) || die("$!\n");
+    return $handle;
+}
+
+# Return a true value if the location $value exists.
+sub _test_exists {
+    my ($attrib_ref, $value) = @_;
+    -e $value;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Locator::FS
+
+=head1 SYNOPSIS
+
+    use FCM::Util::Locator::FS;
+    $util = FCM::Util::Locator::FS->new(\%option);
+    $handle = $util->reader($value);
+
+=head1 DESCRIPTION
+
+Provides utilities to manipulate the values of file system locators.
+
+=head1 METHODS
+
+=over 4
+
+=item $util->can_work_with($value)
+
+Dummy. Always returns true.
+
+=item $util->can_work_with_rev($revision)
+
+Dummy. Always returns false.
+
+=item $util->cat($value,@paths)
+
+Joins @paths to the end of $value.
+
+=item $util->dir($value)
+
+Returns the parent directory of $value.
+
+=item $util->find($value,$callback)
+
+Searches directory tree of $value.
+
+=item $util->origin($value)
+
+Alias of $util->parse($value).
+
+=item $util->parse($value)
+
+In scalar context, returns $value. In list context, returns ($value,undef).
+
+=item $util->reader($value)
+
+Returns a file handle for $value, if it is a readable regular file.
+
+=item $util->read_property($value,$property_name)
+
+Dummy. Always returns undef.
+
+=item $util->test_exists($value)
+
+Return a true value if the location $value exists.
+
+=item $util->trunk_at_head($value)
+
+Dummy. Always returns undef.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/SSH.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/SSH.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/SSH.pm	(revision 5095)
@@ -0,0 +1,251 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Locator::SSH;
+use base qw{FCM::Class::CODE};
+
+use FCM::Util::Exception;
+use File::Temp;
+use Text::ParseWords qw{shellwords};
+
+our %ACTION_OF = (
+    can_work_with     => \&_can_work_with,
+    can_work_with_rev => sub {},
+    cat               => \&_cat,
+    dir               => \&_dir,
+    export            => \&_export,
+    export_ok         => sub {1},
+    find              => \&_find,
+    origin            => \&_parse,
+    parse             => \&_parse,
+    reader            => \&_reader,
+    read_property     => sub {},
+    test_exists       => \&_test_exists,
+    trunk_at_head     => sub {},
+);
+# Alias to the exception class
+my $E = 'FCM::Util::Exception';
+
+# Creates the class.
+__PACKAGE__->class(
+    {type_util_of => '%', util => '&'},
+    {action_of => \%ACTION_OF},
+);
+
+# Returns true if $value looks like a legitimate HOST:PATH.
+sub _can_work_with {
+    my ($attrib_ref, $value) = @_;
+    if (!$value) {
+        return;
+    }
+    if (index($value, ':') < 0) {
+        return;
+    }
+    my ($auth) = split(':', $value, 2);
+    my $host = index($auth, '@') >= 0 ? (split('@', $auth, 2))[1] : $auth;
+    $host ? gethostbyname($host) : undef;
+}
+
+# Joins @paths to the end of $value.
+sub _cat {
+    my ($attrib_ref, $value, @paths) = @_;
+    my ($auth, $path) = split(':', $value, 2);
+    $auth . ':' . $attrib_ref->{type_util_of}{fs}->cat($path, @paths);
+}
+
+# Returns the directory name of $value.
+sub _dir {
+    my ($attrib_ref, $value) = @_;
+    my ($auth, $path) = split(':', $value, 2);
+    $auth . ':' . $attrib_ref->{type_util_of}{fs}->dir($path);
+}
+
+# Rsync $value to $dest.
+sub _export {
+    my ($attrib_ref, $value, $dest) = @_;
+    my ($auth, $path) = _dir($attrib_ref, $value);
+    my $value_hash_ref = $attrib_ref->{util}->shell_simple([
+        _shell_cmd_list($attrib_ref, 'rsync'),
+        $value . '/',
+        $dest,
+    ]);
+    if ($value_hash_ref->{rc}) {
+        die($value_hash_ref);
+    }
+}
+
+# Searches directory tree.
+sub _find {
+    my ($attrib_ref, $value, $callback) = @_;
+    my ($auth, $path) = split(':', $value, 2);
+    my $value_hash_ref = $attrib_ref->{util}->shell_simple([
+        _shell_cmd_list($attrib_ref, 'ssh'),
+        $auth,
+        "find $path -type f -not -path \"*/.*\" -printf \"%T@ %p\\\\n\"",
+    ]);
+    if ($value_hash_ref->{rc}) {
+        die($value_hash_ref);
+    }
+    my $found;
+    LINE:
+    for my $line (grep {$_} split("\n", $value_hash_ref->{o})) {
+        $found ||= 1;
+        my ($mtime, $name) = split(q{ }, $line, 2);
+        my $ns = substr($name, length($path) + 1);
+        $callback->(
+            $auth . ':' . $name,
+            {   is_dir        => undef,
+                last_mod_rev  => undef,
+                last_mod_time => $mtime,
+                ns            => $ns,
+            },
+        );
+    }
+    $found;
+}
+
+# Returns a reader (file handle) for a given file system value.
+sub _reader {
+    my ($attrib_ref, $value) = @_;
+    my ($auth, $path) = split(':', $value, 2);
+    my $handle = File::Temp->new();
+    my $e;
+    my $rc = $attrib_ref->{util}->shell(
+        [_shell_cmd_list($attrib_ref, 'ssh'), $auth, 'cat', $path],
+        {'e' => \$e, 'o' => sub {print($handle $_[0])}},
+    );
+    if ($rc) {
+        die($e);
+    }
+    seek($handle, 0, 0);
+    return $handle;
+}
+
+# Returns $value in scalar context, or ($value,undef) in list context.
+sub _parse {
+    my ($attrib_ref, $value) = @_;
+    my ($auth, $path) = split(':', $value, 2);
+    $value = $auth . ':' . $attrib_ref->{type_util_of}{fs}->parse($path);
+    return (wantarray() ? ($value, undef) : $value);
+}
+
+# Return a true value if the location $value exists.
+sub _test_exists {
+    my ($attrib_ref, $value) = @_;
+    my ($auth, $path) = split(':', $value, 2);
+    my $value_hash_ref = $attrib_ref->{util}->shell_simple([
+        _shell_cmd_list($attrib_ref, 'ssh'), $auth, "test -e '$path'",
+    ]);
+    return !$value_hash_ref->{rc};
+}
+
+# Get a named command and its flags, return a list.
+sub _shell_cmd_list {
+    my ($attrib_ref, $key) = @_;
+    map {shellwords($_)} (
+        $attrib_ref->{util}->external_cfg_get($key),
+        $attrib_ref->{util}->external_cfg_get($key . '.flags'),
+    );
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Locator::SSH
+
+=head1 SYNOPSIS
+
+    use FCM::Util::Locator::SSH;
+    $util = FCM::Util::Locator::SSH->new(\%option);
+    $handle = $util->reader($value);
+
+=head1 DESCRIPTION
+
+Provides utilities to manipulate the values of locators on file systems on
+remote hosts accessible via SSH and RSYNC.
+
+=head1 METHODS
+
+=over 4
+
+=item $util->can_work_with($value)
+
+Returns true if $value is in the form AUTH:PATH and AUTH is a valid user@host.
+
+=item $util->can_work_with_rev($revision)
+
+Dummy. Always returns false.
+
+=item $util->cat($value,@paths)
+
+Joins @paths to the end of $value.
+
+=item $util->dir($value)
+
+Returns the auth:parent-directory of $value.
+
+=item $util->export($value,$dest)
+
+Rsync a clean directory tree of $value to $dest.
+
+=item $util->export_ok($value)
+
+Returns true if $util->can_work_with($value).
+
+=item $util->find($value,$callback)
+
+Searches directory tree of $value.
+
+=item $util->origin($value)
+
+Alias of $util->parse($value).
+
+=item $util->parse($value)
+
+In scalar context, returns $value. In list context, returns ($value,undef).
+
+=item $util->reader($value)
+
+Returns a file handle for $value, if it is a readable regular file.
+
+=item $util->read_property($value,$property_name)
+
+Dummy. Always returns undef.
+
+=item $util->test_exists($value)
+
+Return a true value if the location $value exists.
+
+=item $util->trunk_at_head($value)
+
+Dummy. Always returns undef.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/SVN.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/SVN.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Locator/SVN.pm	(revision 5095)
@@ -0,0 +1,458 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Locator::SVN;
+use base qw{FCM::Class::CODE};
+
+use File::Temp;
+use POSIX qw{setlocale LC_ALL};
+use Time::Piece;
+
+our %ACTION_OF = (
+    as_invariant      => \&_as_invariant,
+    can_work_with     => \&_can_work_with,
+    can_work_with_rev => \&_can_work_with_rev,
+    cat               => \&_cat,
+    dir               => \&_dir,
+    export            => \&_export,
+    export_ok         => \&_export_ok,
+    find              => \&_find,
+    origin            => \&_origin,
+    parse             => \&_parse,
+    reader            => \&_reader,
+    read_property     => \&_read_property,
+    test_exists       => \&_test_exists,
+    trunk_at_head     => \&_trunk_at_head,
+);
+
+my %PATTERN_OF = (
+    REVISION       => qr{\A(?:\d+|HEAD|BASE|COMMITTED|PREV|\{[^\}]+\})\z}ixms,
+    TARGET_PEG     => qr{\A(.+?)(?:@([^/@]+))?\z}xms,
+    URL_COMPONENTS => qr{\A([A-Za-z][\w\.\+\-]*://)([^/]*)(/?.*)\z}xms,
+);
+
+my %INFO_OF = (
+    'Path'                => 'path',
+    'URL'                 => 'URL',
+    'Repository Root'     => 'repos_root_URL',
+    'Repository UUID'     => 'repos_UUID',
+    'Revision'            => 'rev',
+    'Node Kind'           => 'kind',
+    'Last Changed Author' => 'last_changed_author',
+    'Last Changed Rev'    => 'last_changed_rev',
+    'Last Changed Date'   => 'last_changed_date',
+    # FIXME: currently omitting lock info and other WC info.
+);
+
+my %INFO_KIND_OF = (directory => 'dir', file => 'file');
+
+my %INFO_MOD_OF = (
+    last_changed_date => \&_svn_info_last_changed_date,
+    kind => sub {exists($INFO_KIND_OF{$_[0]}) ? $INFO_KIND_OF{$_[0]} : $_[0]},
+);
+
+# Creates the class.
+__PACKAGE__->class(
+    {type_util_of => '%', util => '&'},
+    {action_of => \%ACTION_OF},
+);
+
+# Returns the invariant version of $value.
+sub _as_invariant {
+    my ($attrib_ref, $value) = @_;
+    my ($target, $revision) = _parse($attrib_ref, $value);
+    if (!$attrib_ref->{util}->uri_match($target) && !$revision) {
+        return;
+    }
+    $revision ||= $attrib_ref->{util}->uri_match($target) ? 'HEAD' : 'BASE';
+    my %info_of;
+    _svn_info(
+        $attrib_ref,
+        sub {%info_of = %{$_[0]}},
+        [$value],
+    );
+    return _parse_simple($attrib_ref, $info_of{URL}, $info_of{rev});
+}
+
+# Returns true if $value looks like a legitimate SVN target.
+sub _can_work_with {
+    my ($attrib_ref, $value) = @_;
+    my ($scheme) = $attrib_ref->{util}->uri_match($value);
+    if ($scheme && grep {$_ eq $scheme} qw{svn file svn+ssh http https}) {
+        return $value;
+    }
+    my ($target, $revision) = _parse($attrib_ref, $value);
+    my $url;
+    local($@);
+    eval {_svn_info($attrib_ref, sub {$url = $_[0]->{URL}}, [$target])};
+    return $url;
+}
+
+# Returns true if $revision looks like a legitimate SVN revision specifier.
+sub _can_work_with_rev {
+    my ($attrib_ref, $revision) = @_;
+    if (!defined($revision)) {
+        return;
+    }
+    return $revision =~ $PATTERN_OF{REVISION};
+}
+
+# Joins @paths to the end of $value.
+sub _cat {
+    my ($attrib_ref, $value, @paths) = @_;
+    my ($target, $rev) = _parse($attrib_ref, $value);
+    my $is_uri = $attrib_ref->{util}->uri_match($target);
+    $target
+        = $is_uri ? join('/', $target, @paths)
+        :           $attrib_ref->{type_util_of}{fs}->cat($target, @paths)
+        ;
+    _parse_simple($attrib_ref, _tidy($target), $rev);
+}
+
+# Returns the directory containing $value.
+sub _dir {
+    my ($attrib_ref, $value) = @_;
+    my ($target, $revision) = _parse($attrib_ref, $value);
+    if ($attrib_ref->{util}->uri_match($target)) {
+        my ($leader, $auth, $trailer) = $target =~ $PATTERN_OF{URL_COMPONENTS};
+        if (!$trailer) {
+            return _parse($attrib_ref, $target, $revision);
+        }
+        $trailer =~ s{/+ [^/]* \z}{}xms;
+        $target = $leader . ($auth ? $auth : q{}) . $trailer;
+    }
+    else {
+        $target = $attrib_ref->{type_util_of}{fs}->dir($target);
+    }
+    _parse_simple($attrib_ref, $target, $revision);
+}
+
+# Export $value to $dest.
+sub _export {
+    my ($attrib_ref, $value, $dest) = @_;
+    _run_svn_simple($attrib_ref, 'export', [$value, $dest], {quiet => undef});
+}
+
+# Returns true if $value is a URL.
+sub _export_ok {
+    my ($attrib_ref, $value) = @_;
+    $attrib_ref->{util}->uri_match($value);
+}
+
+# Searches directory tree of $value.
+sub _find {
+    my ($attrib_ref, $value, $callback) = @_;
+    if (!$attrib_ref->{util}->uri_match($value)) {
+        return $attrib_ref->{type_util_of}{fs}->find($value, $callback);
+    }
+    _svn_info(
+        $attrib_ref,
+        sub {
+            my ($info_ref) = @_;
+            $callback->(
+                $info_ref->{URL} . '@' . $info_ref->{rev},
+                {   is_dir        => $info_ref->{kind} eq 'dir',
+                    last_mod_rev  => $info_ref->{last_changed_rev},
+                    last_mod_time => $info_ref->{last_changed_date},
+                    ns            => $info_ref->{path},
+                },
+            );
+        },
+        [$value],
+        {recursive => undef},
+    );
+    return 1;
+}
+
+# Returns the URL version of $value.
+sub _origin {
+    my ($attrib_ref, $value) = @_;
+    my ($target, $revision) = _parse($attrib_ref, $value);
+    if ($attrib_ref->{util}->uri_match($target)) {
+        return _parse_simple($attrib_ref, $value);
+    }
+    $revision ||= 'BASE';
+    _as_invariant(
+        $attrib_ref,
+        scalar(_parse_simple($attrib_ref, $target, $revision)),
+    );
+}
+
+# In list context, returns ($target, $revision). In scalar context, returns
+# "$target@$revision".
+sub _parse {
+    my ($attrib_ref, $value, $revision) = @_;
+    my ($target, $peg_revision) = $value =~ $PATTERN_OF{TARGET_PEG};
+    if ($peg_revision) {
+        $revision = $peg_revision;
+    }
+    $target
+        = $attrib_ref->{util}->uri_match($value)
+        ? _tidy($target)
+        : $attrib_ref->{type_util_of}{fs}->parse($target)
+        ;
+    _parse_simple($attrib_ref, $target, $revision);
+}
+
+# Same as _parse, but without _tidy.
+sub _parse_simple {
+    my ($attrib_ref, $value, $revision) = @_;
+    (
+        wantarray() ? ($value, $revision)
+        :             $value . ($revision ? q{@} . $revision : q{})
+    );
+}
+
+# Returns a named property of a Subversion target.
+sub _read_property {
+    my ($attrib_ref, $value, $name) = @_;
+    _run_svn_simple($attrib_ref, 'pg', [$name, $value]);
+}
+
+# Returns a reader (file handle) for a given Subversion target.
+sub _reader {
+    my ($attrib_ref, $value) = @_;
+    my ($target, $revision) = _parse($attrib_ref, $value);
+    if ($attrib_ref->{util}->uri_match($target) || $revision) {
+        return _run_svn_handle($attrib_ref, 'cat', [$value]);
+    }
+    else {
+        return $attrib_ref->{type_util_of}{fs}->reader($target);
+    }
+}
+
+# Helper for _run_svn_*, generates the command.
+sub _run_svn_command {
+    my ($attrib_ref, $key, $args_ref, $option_ref) = @_;
+    $args_ref   ||= [];
+    $option_ref ||= {};
+    my @options;
+    while (my ($key, $value) = each(%{$option_ref})) {
+        push(@options, '--' . $key . (defined($value) ? '=' . $value : q{}));
+    }
+    ['svn', $key, @options, @{$args_ref}];
+}
+
+# Runs "svn", sending standard output to a file handle.
+sub _run_svn_handle {
+    my ($attrib_ref, $key, $args_ref, $option_ref) = @_;
+    local($ENV{LANG}) = $ENV{LANG};
+    if (setlocale(LC_ALL, 'en_GB')) {
+        $ENV{LANG} = 'en_GB';
+    }
+    my $handle = File::Temp->new();
+    my $rc = $attrib_ref->{util}->shell(
+        _run_svn_command(@_),
+        {e => \my($err), o => sub {print($handle $_[0])}},
+    );
+    if ($rc || (!tell($handle) && $err)) { # cat, info, etc may return 0 on err
+        chomp($err);
+        die("$err\n");
+    }
+    seek($handle, 0, 0);
+    return $handle;
+}
+
+# Runs a simple "svn" command.
+sub _run_svn_simple {
+    my ($attrib_ref, $key, $args_ref, $option_ref) = @_;
+    local($ENV{LANG}) = $ENV{LANG};
+    if (setlocale(LC_ALL, 'en_GB')) {
+        $ENV{LANG} = 'en_GB';
+    }
+    my $value_hash_ref
+        = $attrib_ref->{util}->shell_simple(_run_svn_command(@_));
+    if ($value_hash_ref->{rc}) {
+        die($value_hash_ref);
+    }
+    $value_hash_ref->{o};
+}
+
+# Runs "svn info".
+sub _svn_info {
+    my ($attrib_ref, $callback_ref, $args_ref, $option_ref) = @_;
+    my $handle = _run_svn_handle($attrib_ref, 'info', $args_ref, $option_ref);
+    my %hash;
+    while (my $line = readline($handle)) {
+        chomp($line);
+        if ($line) {
+            my ($key, $value) = split(qr{:\s*}msx, $line, 2);
+            if (exists($INFO_OF{$key})) {
+                my $id = $INFO_OF{$key};
+                $hash{$id}
+                    = exists($INFO_MOD_OF{$id}) ? $INFO_MOD_OF{$id}->($value)
+                    :                             $value
+                    ;
+            }
+        }
+        else {
+            $callback_ref->(\%hash);
+        }
+    }
+}
+
+# Parse last changed date from "svn info".
+sub _svn_info_last_changed_date {
+    my $text = (split(qr{\s+\(}msx, $_[0], 2))[0];
+    my $head = Time::Piece->strptime(substr($text, 0, -6), '%Y-%m-%d %H:%M:%S');
+    my $tail = substr($text, -5);
+    my ($tz_sign, $tz_h, $tz_m) = $tail =~ qr{([\-\+])(\d\d)(\d\d)}msx;
+    $head->epoch() - int($tz_sign . 1) * ($tz_h * 3600 + $tz_m * 60);
+}
+
+# Return a true value if the location $value exists.
+sub _test_exists {
+    my ($attrib_ref, $value) = @_;
+    my $url;
+    eval {_svn_info($attrib_ref, sub {$url = $_[0]->{URL}}, [$value])};
+    return $url;
+}
+
+# Returns a tidied version of a Subversion URL.
+sub _tidy {
+    my ($url) = @_;
+    my ($leader, $auth, $trailer) = $url =~ $PATTERN_OF{URL_COMPONENTS};
+    if (!$trailer) {
+        return $url;
+    }
+    my @tidied_names;
+    my %handler_of = (
+        q{}   => sub {push(@tidied_names, $_[0])},
+        q{.}  => sub {},
+        q{..} => sub {if (@tidied_names > 1) {pop(@tidied_names)}},
+    );
+    for my $name (split(qr{/+}xms, $trailer)) {
+        my $handler
+            = exists($handler_of{$name}) ? $handler_of{$name} : $handler_of{q{}};
+        $handler->($name);
+    }
+    return $leader . ($auth ? $auth : q{}) . join(q{/}, @tidied_names);
+}
+
+# Returns trunk@HEAD for a URL.
+sub _trunk_at_head {
+    my ($attrib_ref, $target) = @_;
+    if (!$attrib_ref->{util}->uri_match($target)) {
+        return;
+    }
+    _cat($attrib_ref, $target, 'trunk@HEAD');
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Locator::SVN
+
+=head1 SYNOPSIS
+
+    use FCM::Util;
+    $util = FCM::Util->new(\%attrib);
+    $reader = $util->loc_reader($locator);
+
+
+=head1 DESCRIPTION
+
+This is part of L<FCM::Util|FCM::Util>. Provides utilities for Subversion
+targets.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item util
+
+The L<FCM::Util|FCM::Util> object that initialised this object.
+
+=head1 METHODS
+
+=over 4
+
+=item $util->as_invariant($value)
+
+Returns the invariant version of $value. For example, if the current HEAD
+revision is 1234, and $value is C<svn://foo/bar/baz> or
+C<svn://foo/bar/baz@HEAD>, it will return C<svn://foo/bar/baz@1234>.
+
+=item $util->can_work_with($value)
+
+Returns the URL form of $value (true) if $value is a valid SVN target.
+
+=item $util->can_work_with_rev($revision)
+
+Returns true if $revision looks like a legitimate SVN revision specifier.
+
+=item $util->cat($value,@paths)
+
+Join @paths to the end of $value.
+
+=item $util->dir($value)
+
+Returns the directory name of $value.
+
+=item $util->export($value,$dest)
+
+Exports a clean directory tree of $value to $dest.
+
+=item $util->export_ok($value)
+
+Returns true if $value is a URL. (It is not safe to export a working copy.)
+
+=item $util->find($value,$callback)
+
+Searches directory tree of $value.
+
+=item $util->origin($value)
+
+Returns the URL version of $value.
+
+=item $util->parse($value,$revision)
+
+In scalar context, returns a string in C<TARGET@REV> for $value. In list
+context, given C<TARGET@REV> returns (C<TARGET>, C<REV>). If $value has a peg
+revision, it overrides the specified $revision.
+
+=item $util->reader($value)
+
+Returns a file handle for reading the content in $value, if possible.
+
+=item $util->read_property($value,$name)
+
+Returns the value of a property $name of $value.
+
+=item $util->test_exists($value)
+
+Return a true value if the location $value exists.
+
+=item $util->trunk_at_head($value)
+
+Returns "$value/trunk@HEAD' if $value is a URI or undef otherwise.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Reporter.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Reporter.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Reporter.pm	(revision 5095)
@@ -0,0 +1,382 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Reporter;
+use base qw{FCM::Class::CODE};
+
+use Scalar::Util qw{reftype};
+
+use constant {TYPE_OUT => 1, TYPE_ERR => 2};
+
+use constant {  DEFAULT => 1,
+    FAIL  => 0, WARN    => 1,
+    QUIET => 0, LOW     => 1, MEDIUM => 2, HIGH => 3, DEBUG => 4,
+};
+
+use constant {
+    PREFIX_DONE => q{[done] },
+    PREFIX_FAIL => q{[FAIL] },
+    PREFIX_INFO => q{[info] },
+    PREFIX_INIT => q{[init] },
+    PREFIX_NULL => q{},
+    PREFIX_QUIT => q{[quit] },
+    PREFIX_WARN => q{[WARN] },
+};
+
+# Creates the class.
+__PACKAGE__->class(
+    {ctx_of => '%'},
+    {   init => sub {
+            my ($attrib_ref) = @_;
+            %{$attrib_ref->{ctx_of}} = (
+                stderr => FCM::Util::Reporter::Context->new_err(),
+                stdout => FCM::Util::Reporter::Context->new(),
+            );
+        },
+        action_of => {
+            add_ctx           => \&_add_ctx,
+            del_ctx           => \&_del_ctx,
+            get_ctx           => \&_get_ctx,
+            get_ctx_of_stderr => sub {$_[0]->{ctx_of}->{stderr}},
+            get_ctx_of_stdout => sub {$_[0]->{ctx_of}->{stdout}},
+            report            => \&_report,
+        }
+    },
+);
+
+# Adds a named reporter context.
+sub _add_ctx {
+    my ($attrib_ref, $key, @args) = @_;
+    if (exists($attrib_ref->{ctx_of}->{$key})) {
+        return;
+    }
+    $attrib_ref->{ctx_of}->{$key} = FCM::Util::Reporter::Context->new(@args);
+}
+
+# Deletes a named reporter context.
+sub _del_ctx {
+    my ($attrib_ref, $key) = @_;
+    if (!exists($attrib_ref->{ctx_of}->{$key})) {
+        return;
+    }
+    delete($attrib_ref->{ctx_of}->{$key});
+}
+
+# Returns a named reporter context.
+sub _get_ctx {
+    my ($attrib_ref, $key) = @_;
+    if (!exists($attrib_ref->{ctx_of}->{$key})) {
+        return;
+    }
+    $attrib_ref->{ctx_of}->{$key};
+}
+
+# Reports message.
+sub _report {
+    my ($attrib_ref, @args) = @_;
+    if (!@args) {
+        return;
+    }
+    my %option = (
+        delimiter => "\n",
+        level     => DEFAULT,
+        prefix    => undef,
+        type      => TYPE_OUT,
+    );
+    if (ref($args[0]) && reftype($args[0]) eq 'HASH') {
+        %option = (%option, %{shift(@args)});
+    }
+    # Auto remove ctx with closed file handle
+    while (my ($key, $ctx) = each(%{$attrib_ref->{ctx_of}})) {
+        if (!defined(fileno($ctx->get_handle()))) {
+            delete($attrib_ref->{ctx_of}->{$key});
+        }
+    }
+    # Selects handles
+    my @ctx_and_prefix_list
+        =   map  {
+                my $prefix = defined($option{prefix})
+                    ? $option{prefix} : $_->get_prefix();
+                if (ref($prefix) && reftype($prefix) eq 'CODE') {
+                    $prefix = $prefix->($option{level}, $option{type});
+                }
+                [$_, $prefix],
+            }
+            grep {  (!$_->get_type() || $_->get_type() eq $option{type})
+                &&  $_->get_verbosity() >= $option{level}
+            }
+            values(%{$attrib_ref->{ctx_of}});
+    if (!@ctx_and_prefix_list) {
+        return;
+    }
+    for my $arg (@args) {
+        for (@ctx_and_prefix_list) {
+            my ($ctx, $prefix) = @{$_};
+            my $handle = $ctx->get_handle();
+            if ($option{delimiter}) {
+                for my $item (
+                    map {grep {$_ ne "\n"} split(qr{(\n)}msx)} (
+                          !ref($arg)               ? ($arg)
+                        : reftype($arg) eq 'ARRAY' ? @{$arg}
+                        : reftype($arg) eq 'CODE'  ? $arg->($ctx->get_verbosity())
+                        :                            ($arg)
+                    )
+                ) {
+                    print({$handle} $prefix . $item . $option{delimiter});
+                }
+            }
+            else {
+                print({$handle} $arg);
+            }
+        }
+    }
+    1;
+}
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Reporter::Context;
+use base qw{FCM::Class::HASH};
+
+# Creates the class.
+__PACKAGE__->class(
+    {   handle    => {isa => '*', default => \*STDOUT                     },
+        prefix    => {            default => sub {\&_prefix}              },
+        type      => {isa => '$', default => FCM::Util::Reporter->TYPE_OUT},
+        verbosity => {isa => '$', default => FCM::Util::Reporter->DEFAULT },
+    },
+);
+
+# Returns a new reporter context to STDERR.
+sub new_err {
+    my ($class, $attrib_ref) = @_;
+    $class->new({
+        handle => \*STDERR,
+        type   => FCM::Util::Reporter->TYPE_ERR,
+        (defined($attrib_ref) ? %{$attrib_ref} : ()),
+    });
+}
+
+# The default prefix function.
+sub _prefix {
+    my ($level, $type) = @_;
+      $type eq FCM::Util::Reporter->TYPE_OUT ? FCM::Util::Reporter->PREFIX_INFO
+    : $level > FCM::Util::Reporter->FAIL     ? FCM::Util::Reporter->PREFIX_WARN
+    :                                          FCM::Util::Reporter->PREFIX_FAIL
+    ;
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Reporter
+
+=head1 SYNOPSIS
+
+    use FCM::Util::Reporter;
+    $reporter = FCM::Util::Reporter->new({verbosity => $verbosity});
+    $reporter->($message);
+    $reporter->(\@messages);
+    $reporter->(sub {return @some_strings});
+    $reporter->({level => $reporter->MEDIUM}, $message);
+
+=head1 DESCRIPTION
+
+A simple message reporter.
+
+This module is part of L<FCM::Util|FCM::Util>. See also the description of the
+$u->report() method in L<FCM::Util|FCM::Util>.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance of this class, which is a CODE reference. %attrib can
+contain the following:
+
+=over 4
+
+=item ctx_of
+
+A HASH containing a map to the named reporter contexts. At initialisation, a new
+ctx for "stdout" and a new ctx for "stderr" is created automatically.
+
+=back
+
+=item $reporter->add_ctx($key,%option)
+
+Creates a new reporter context, and adds it to the ctx_of HASH, if a context
+with the same $key does not already exist. The %option is given to the
+constructir of L</FCM::Util::Reporter::Context>. Return the context on success.
+
+=item $reporter->del_ctx($key)
+
+Removes a new reporter context named $key. Return the context on success.
+
+=item $reporter->get_ctx($key)
+
+Returns a named reporter context L</FCM::Util::Reporter::Context>.
+
+=item $reporter->get_ctx_of_stderr()
+
+Shorthand for $reporter->get_ctx('stderr').
+
+=item $reporter->get_ctx_of_stdout()
+
+Shorthand for $reporter->get_ctx('stdout').
+
+=item $reporter->report(\%option,$message)
+
+Reports the message. If %option is not given, reports using the default options.
+In the form, the following %options can be specified:
+
+=over 4
+
+=item delimiter
+
+The delimiter of each message in the list. The default is "\n". If the delimiter
+is set to the empty string, the items in $message will be treated as raw
+strings, i.e. it will also ignore any "prefix" options.
+
+=item level
+
+The level of the current message. The default is DEFAULT.
+
+=item prefix
+
+The message prefix. It can be a string or a CODE reference. If it is a string,
+it is simply preprended to the message. If it is a code reference, it is calls
+as $prefix_ref->($option{level}, $option{type}), and its result (if defined) is
+prepended to the message.
+
+=item type
+
+The message type. It can be REPORT_ERR or REPORT_OUT (default).
+
+=back
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item $reporter->FAIL, $reporter->QUIET
+
+The verbosity level 0.
+
+=item $reporter->DEFAULT, $reporter->LOW, $reporter->WARN
+
+The verbosity level 1.
+
+=item $reporter->MEDIUM
+
+The verbosity level 2.
+
+=item $reporter->HIGH
+
+The verbosity level 3.
+
+=item $reporter->DEBUG
+
+The verbosity level 4.
+
+=item $reporter->PREFIX_DONE
+
+The prefix for a task "done" message.
+
+=item $reporter->PREFIX_FAIL
+
+The prefix for a fatal error message.
+
+=item $reporter->PREFIX_INFO
+
+The prefix for an "info" message.
+
+=item $reporter->PREFIX_INIT
+
+The prefix for a task "init" message.
+
+=item $reporter->PREFIX_NULL
+
+An empty string.
+
+=item $reporter->PREFIX_QUIT
+
+The prefix for a quit/abort message.
+
+=item $reporter->PREFIX_WARN
+
+The prefix for a warning message.
+
+=item $reporter->REPORT_ERR
+
+The message type for exception message.
+
+=item $reporter->REPORT_OUT
+
+The message type for info message.
+
+=back
+
+=head1 FCM::Util::Reporter::Context
+
+An instance of this class represents the context for a reporter for the
+L<FCM::Util->report()|FCM::Util>. This class is a sub-class of
+L<FCM::Class::HASH|FCM::Class::HASH>. It has the following attributes:
+
+=over 4
+
+=item handle
+
+The file handle for info messages. (Default=\*STDOUT)
+
+=item prefix
+
+The message prefix. It can be a string or a CODE reference. If it is a string,
+it is simply preprended to the message. If it is a code reference, it is calls
+as $prefix_ref->($option{level}, $option{type}), and its result (if defined) is
+prepended to the message. The default is a CODE that returns PREFIX_INFO for
+TYPE_OUT messages, PREFIX_WARN for TYPE_ERR messages at WARN level or above or
+PREFIX_FAIL for TYPE_ERR messages at FAIL level.
+
+=item type
+
+Reporter type. (Default=TYPE_OUT)
+
+=item verbosity
+
+The verbosity of the reporter. Only messages at a level above or equal to the
+verbosity will be reported. The default is DEFAULT.
+
+=back
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Shell.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Shell.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/Shell.pm	(revision 5095)
@@ -0,0 +1,306 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Shell;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+use FCM::Util::Exception;
+use File::Spec::Functions qw{catfile file_name_is_absolute path};
+use IPC::Open3 qw{open3};
+use List::Util qw{first};
+use Scalar::Util qw{reftype};
+use Text::ParseWords qw{shellwords};
+
+our $BUFFER_SIZE = 4096;  # default buffer size
+our $TIME_OUT    = 0.005; # default time out for selecting a file handle
+
+my $E = 'FCM::Util::Exception';
+my %FUNCTION_OF = (e => \&_do_r, i => \&_do_w, o => \&_do_r);
+my @IOE = qw{i o e};
+my %ACTION_FUNC_FOR
+    = (e => \&_action_func_r, i => \&_action_func_w, o => \&_action_func_r);
+
+# Creates the class.
+__PACKAGE__->class(
+    {   buffer_size => {isa => '$', default => $BUFFER_SIZE},
+        time_out    => {isa => '$', default => $TIME_OUT},
+        util        => '&',
+    },
+    {   action_of => {
+            invoke        => \&_invoke,
+            invoke_simple => \&_invoke_simple,
+            which         => \&_which,
+        },
+    },
+);
+
+# Returns a CODE to deal with non-CODE read action.
+sub _action_func_r {
+    my ($arg_ref) = @_;
+    ${$arg_ref} ||= q{};
+    sub {${$arg_ref} .= $_[0]};
+}
+
+# Returns a CODE to deal with non-CODE write action.
+sub _action_func_w {
+    my ($arg_ref) = @_;
+    my @inputs
+        = ref($arg_ref) && reftype($arg_ref) eq 'ARRAY'  ? @{$arg_ref}
+        : ref($arg_ref) && reftype($arg_ref) eq 'SCALAR' ? (${$arg_ref})
+        :                                                  ()
+        ;
+    sub {shift(@inputs)};
+}
+
+# Gets output $value from a selected handle, and invokes $action->($value).
+sub _do_r {
+    my ($attrib_ref, $ctx) = @_;
+    my $n_bytes;
+    while (
+        my @handles = $ctx->get_select()->can_read($attrib_ref->{time_out})
+    ) {
+        my ($handle) = @handles;
+        my $buffer = q{};
+        my $n = sysread($handle, $buffer, $attrib_ref->{buffer_size});
+        if (!defined($n)) {
+            return;
+        }
+        $n_bytes += $n;
+        if ($n == 0) {
+            close($handle) || return;
+            return 0;
+        }
+        $ctx->get_action()->($buffer);
+    }
+    defined($n_bytes) ? $n_bytes : -1;
+}
+
+# Gets input from $action->() and writes to a selected handle if possible.
+# Handles buffering of STDIN to the command.
+sub _do_w {
+    my ($attrib_ref, $ctx) = @_;
+    my $n_bytes;
+    while (
+        my @handles = $ctx->get_select()->can_write($attrib_ref->{time_out})
+    ) {
+        my ($handle) = @handles;
+        if (!$ctx->get_buf()) {
+            $ctx->set_buf($ctx->get_action()->());
+            if (!defined($ctx->get_buf())) {
+                close($handle) || return;
+                return 0;
+            };
+            $ctx->set_buf_length(length($ctx->get_buf()));
+            $ctx->set_buf_offset(0);
+        }
+        my $n = syswrite(
+            $handle,
+            $ctx->get_buf(),
+            $attrib_ref->{buffer_size},
+            $ctx->get_buf_offset(),
+        );
+        if (!defined($n)) {
+            return;
+        }
+        $n_bytes += $n;
+        $ctx->set_buf_offset($ctx->get_buf_offset() + $n);
+        if ($ctx->get_buf_offset() >= $ctx->get_buf_length()) {
+            $ctx->set_buf(undef);
+            $ctx->set_buf_length(0);
+            $ctx->set_buf_offset(0);
+        }
+    }
+    defined($n_bytes) ? $n_bytes : -1;
+}
+
+# Invokes a command.
+sub _invoke {
+    my ($attrib_ref, $command_ref, $action_ref) = @_;
+    # Ensure that the command is an ARRAY
+    if (!ref($command_ref)) {
+        $command_ref = [shellwords($command_ref)];
+    }
+    # Check that the command exists in the PATH
+    if (!_which($attrib_ref, $command_ref->[0])) {
+        return $E->throw($E->SHELL_WHICH, $command_ref);
+    }
+    # Sets up the STDIN, STDOUT and STDERR to the command
+    my %ctx_of = map {($_, FCM::Util::Shell::Context->new())} @IOE;
+    $action_ref ||= {};
+    while (my ($key, $action) = each(%{$action_ref})) {
+        if (exists($ctx_of{$key})) {
+            if (reftype($action) eq 'CODE') {
+                $ctx_of{$key}->set_action($action);
+            }
+            else {
+                $ctx_of{$key}->set_action($ACTION_FUNC_FOR{$key}->($action));
+            }
+        }
+    }
+    # Calls the command with open3
+    my $timer = $attrib_ref->{util}->timer();
+    my $pid = eval {
+        open3((map {$ctx_of{$_}->get_handle()} @IOE), @{$command_ref});
+    };
+    if (my $e = $@) {
+        return $E->throw($E->SHELL_OPEN3, $command_ref, $e);
+    }
+    # Handles input/output of the command
+    for my $ctx (values(%ctx_of)) {
+        $ctx->get_select()->add($ctx->get_handle());
+    }
+    while (keys(%ctx_of)) {
+        while (my ($key, $ctx) = each(%ctx_of)) {
+            my $status = $FUNCTION_OF{$key}->($attrib_ref, $ctx);
+            if (!defined($status)) {
+                return $E->throw($E->SHELL_OS, $command_ref, $!);
+            }
+            if (!$status) {
+                delete($ctx_of{$key});
+            }
+        }
+    }
+    # Wait for command to finish
+    waitpid($pid, 0);
+    my $rc = $?;
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->SHELL, $command_ref, $rc, $timer->(),
+    );
+    # Handles exceptions and signals
+    if ($rc) {
+        if ($rc == -1) {
+            return $E->throw($E->SHELL_OS, $command_ref, $!);
+        }
+        if ($rc & 127) {
+            return $E->throw($E->SHELL_SIGNAL, $command_ref, $rc & 127);
+        }
+    }
+    return $rc >> 8;
+}
+
+# Wraps _invoke.
+sub _invoke_simple {
+    my ($attrib_ref, $command_ref) = @_;
+    my ($e, $o);
+    my $rc = _invoke($attrib_ref, $command_ref, {e => \$e, o => \$o});
+    return {e => $e, o => $o, rc => $rc};
+}
+
+# Returns the full path to the command $name, if it exists in the PATH.
+sub _which {
+    my ($attrib_ref, $name) = @_;
+    if (file_name_is_absolute($name)) {
+        return $name;
+    }
+    use filetest 'access';
+    first {-f $_ && -x _} map {catfile($_, $name)} path();
+    no filetest 'access';
+}
+
+# ------------------------------------------------------------------------------
+package FCM::Util::Shell::Context;
+use base qw{FCM::Class::HASH};
+
+use IO::Select;
+use Symbol qw{gensym};
+
+# A context to hold the information for the command's STDIN, STDOUT or STDERR.
+# action => CODE to call to get more STDIN for the command or to send
+#           STDOUT/STDERR to when possible.
+# buf*   => A buffer (and its length and the current offset) to hold the STDIN
+#           that is yet to be written to the command.
+# handle => The command STDIN, STDOUT or STDERR.
+# select => The IO::Select object that tells us whether the handle is ready for
+#           I/O or not.
+__PACKAGE__->class(
+    {   action     => {isa => '&'},
+        buf        => {isa => '$'},
+        buf_length => {isa => '$'},
+        buf_offset => {isa => '$'},
+        handle     => {isa => '*', default => \&gensym},
+        'select'   => {isa => 'IO::Select', default => sub {IO::Select->new()}},
+    },
+);
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::Shell
+
+=head1 SYNOPSIS
+
+    use FCM::Util;
+    $util = FCM::Util->new(\%attrib);
+    %action_of = {e => \&e_handler, i => \&i_handler, o => \&o_handler};
+    $rc = $util->shell(\@command, \%action_of);
+    %value_of = %{$util->shell_simple(\@command)};
+
+=head1 DESCRIPTION
+
+Wraps L<IPC::Open3|IPC::Open3> to provide an interface driven by callbacks.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new(\%attrib)
+
+Returns a new instance. The attributes that can be specified in %attrib are:
+
+=over 4
+
+=item {buffer_size}
+
+The size of the read buffer for reading from the standard output and standard
+error output of the command. The default is 4096.
+
+=item {time_out}
+
+The time to wait when selecting a file handle. The default is 0.001.
+
+=item {util}
+
+A CODE reference. The L<FCM::Util|FCM::Util> object that initialised this
+instance.
+
+=back
+
+=back
+
+See the description of the shell(), shell_simpl() and shell_which() methods in
+L<FCM::Util|FCM::Util> for detail.
+
+=head1 SEE ALSO
+
+L<IPC::Open3|IPC::Open3>
+
+Inspired by the CPAN module L<IPC::Cmd|IPC::Cmd> and friends.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/TaskRunner.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/TaskRunner.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM/Util/TaskRunner.pm	(revision 5095)
@@ -0,0 +1,380 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+# ------------------------------------------------------------------------------
+package FCM::Util::TaskRunner;
+use base qw{FCM::Class::CODE};
+
+my $P = 'FCM::Util::TaskRunner::Parallel';
+my $S = 'FCM::Util::TaskRunner::Serial';
+
+__PACKAGE__->class({util => '&'}, {action_of => {main => \&_main}});
+
+sub _main {
+    my ($attrib_ref, $action_ref, $n_workers) = @_;
+    $n_workers ||= 1;
+    my $class = $n_workers > 1 ? $P : $S;
+    $attrib_ref->{runner} = $class->new({
+        action    => $action_ref,
+        n_workers => $n_workers,
+        util      => $attrib_ref->{util},
+    });
+}
+
+# ------------------------------------------------------------------------------
+package FCM::Util::TaskRunner::Serial;
+use base qw{FCM::Class::CODE};
+
+__PACKAGE__->class(
+    {action => '&', util => '&'},
+    {action_of => {destroy => sub {}, main => \&_main}},
+);
+
+sub _main {
+    my ($attrib_ref, $get_ref, $put_ref) = @_;
+    my $n_done = 0;
+    while (my $task = $get_ref->()) {
+        my $timer = $attrib_ref->{util}->timer();
+        eval {
+            $task->set_state($task->ST_WORKING);
+            $attrib_ref->{action}->($task->get_ctx());
+            $task->set_state($task->ST_OK);
+        };
+        if ($@) {
+            $task->set_error($@);
+            $task->set_state($task->ST_FAILED);
+        }
+        $task->set_elapse($timer->());
+        $put_ref->($task);
+        ++$n_done;
+    }
+    $n_done;
+}
+
+# ------------------------------------------------------------------------------
+package FCM::Util::TaskRunner::Parallel;
+use base qw{FCM::Class::CODE};
+
+use FCM::Context::Event;
+use IO::Select;
+use IO::Socket;
+use List::Util qw{first};
+use POSIX qw{WNOHANG};
+use Socket qw{AF_UNIX SOCK_STREAM PF_UNSPEC};
+use Storable qw{freeze thaw};
+
+# Package name of worker event and state
+my $CTX_EVENT = 'FCM::Context::Event';
+my $CTX_STATE = 'FCM::Util::TaskRunner::WorkerState';
+
+# Length of a packed long integer
+my $LEN_OF_LONG = length(pack('N', 0));
+
+# Time out for polling sockets to child processes
+my $TIME_OUT = 0.05;
+
+# Creates the class.
+__PACKAGE__->class(
+    {   action        => '&',
+        n_workers     => '$',
+        worker_states => '@',
+        util          => '&',
+    },
+    {init => \&_init, action_of => {destroy => \&_destroy, main => \&_main}},
+);
+
+# Destroys the child processes.
+sub _destroy {
+    my $attrib_ref = shift();
+    local($SIG{CHLD}) = 'IGNORE';
+    my $select = IO::Select->new();
+    my @worker_states = @{$attrib_ref->{worker_states}};
+    for my $worker_state (@worker_states) {
+        $select->add($worker_state->get_socket());
+    }
+    # TBD: reads $socket for any left over event etc?
+    for my $socket ($select->can_write(0)) {
+        my $worker_state = first {$_->get_socket() eq $socket} @worker_states;
+        _item_send($socket);
+        close($socket);
+        waitpid($worker_state->get_pid(), 0);
+    }
+    while (waitpid(-1, WNOHANG) > 0) {
+    }
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->TASK_WORKERS, 'destroy', $attrib_ref->{n_workers},
+    );
+    1;
+}
+
+# On initialisation.
+sub _init {
+    my $attrib_ref = shift();
+    for my $i (1 .. $attrib_ref->{n_workers}) {
+        my ($from_boss, $from_worker)
+            = IO::Socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
+        if (!defined($from_boss) || !defined($from_worker)) {
+            die("socketpair: $!");
+        }
+        $from_worker->autoflush(1);
+        $from_boss->autoflush(1);
+        if (my $pid = fork()) {
+            # I am the boss
+            if ($pid < 0) {
+                die("fork: $!");
+            }
+            local($SIG{CHLD}, $SIG{INT}, $SIG{KILL}, $SIG{TERM}, $SIG{XCPU});
+            for my $key (qw{CHLD INT KILL TERM XCPU}) {
+                local($SIG{$key}) = sub {_destroy($attrib_ref, @_); die($!)};
+            }
+            close($from_worker);
+            push(
+                @{$attrib_ref->{worker_states}},
+                $CTX_STATE->new($pid, $from_boss),
+            );
+        }
+        elsif (defined($pid)) {
+            # I am a worker
+            close($from_boss);
+            $attrib_ref->{worker_states} = [];
+            open(STDIN, '/dev/null');
+            # Ensures that events are sent back to the boss process
+            my $util_of_event = bless(
+                sub {_item_send($from_worker, @_)},
+                __PACKAGE__ . '::WorkerEvent',
+            );
+            no strict 'refs';
+            *{__PACKAGE__ . '::WorkerEvent::main'}
+                = sub {my $self = shift(); $self->(@_)};
+            use strict 'refs';
+            $attrib_ref->{util}->util_of_event($util_of_event);
+            _worker(
+                $from_worker,
+                $attrib_ref->{action},
+                $attrib_ref->{util},
+            );
+            close($from_worker);
+            exit();
+        }
+        else {
+            die("fork: $!");
+        }
+    }
+    $attrib_ref->{util}->event(
+        FCM::Context::Event->TASK_WORKERS, 'init', $attrib_ref->{n_workers},
+    );
+}
+
+# Main function of the class.
+sub _main {
+    my ($attrib_ref, $get_ref, $put_ref) = @_;
+    my $n_done = 0;
+    my $n_wait = 0;
+    my $done_something = 1;
+    my $get_task_ref = _get_task_func($get_ref, $attrib_ref->{n_workers});
+    my $select = IO::Select->new();
+    my @worker_states = @{$attrib_ref->{worker_states}};
+    for my $worker_state (@worker_states) {
+        $select->add($worker_state->get_socket());
+    }
+    while ($n_wait || $done_something) {
+        $done_something = 0;
+        # Handles tasks back from workers
+        while (my @sockets = $select->can_read($TIME_OUT)) {
+            for my $socket (@sockets) {
+                my $worker_state
+                    = first {$socket eq $_->get_socket()} @worker_states;
+                my $item = _item_receive($socket);
+                if (defined($item)) {
+                    $done_something = 1;
+                    if ($item->isa('FCM::Context::Event')) {
+                        # Item is only an event, handles it
+                        $attrib_ref->{util}->event($item);
+                    }
+                    else {
+                        # Sends something back to the worker immediately
+                        if (defined(my $task = $get_task_ref->())) {
+                            _item_send($socket, $task);
+                        }
+                        else {
+                            --$n_wait;
+                            $worker_state->set_idle(1);
+                        }
+                        $put_ref->($item);
+                        ++$n_done;
+                    }
+                }
+            }
+        }
+        # Sends something to the idle workers
+        my @idle_worker_states = grep {$_->get_idle()} @worker_states;
+        if (@idle_worker_states) {
+            for my $worker_state (@idle_worker_states) {
+                if (defined(my $task = $get_task_ref->())) {
+                    _item_send($worker_state->get_socket(), $task);
+                    ++$n_wait;
+                    $done_something = 1;
+                    $worker_state->set_idle(0);
+                }
+            }
+        }
+        else {
+            $get_task_ref->(); # only adds more tasks to queue
+        }
+    }
+    $n_done;
+}
+
+# Returns a function to fetch more tasks into a queue.
+sub _get_task_func {
+    my ($get_ref, $n_workers) = @_;
+    my $max_n_in_queue = $n_workers * 2;
+    my @queue;
+    sub {
+        while (@queue < $max_n_in_queue && defined(my $task = $get_ref->())) {
+            push(@queue, $task);
+        }
+        if (!defined(wantarray())) {
+            return;
+        }
+        shift(@queue);
+    };
+}
+
+# Receives an item from a socket.
+sub _item_receive {
+    my ($socket) = @_;
+    my $len_of_data = unpack('N', _item_travel($socket, $LEN_OF_LONG));
+    $len_of_data ? thaw(_item_travel($socket, $len_of_data)) : undef;
+}
+
+# Sends an item to a socket.
+sub _item_send {
+    my ($socket, $item) = @_;
+    my $item_as_data = $item ? freeze($item) : q{};
+    my $message = pack('N', length($item_as_data)) . $item_as_data;
+    _item_travel($socket, length($message), $message);
+}
+
+# Helper for _item_receive/_item_send.
+sub _item_travel {
+    my ($socket, $len_to_travel, $data) = @_;
+    my $action
+        = defined($data) ? sub {syswrite($socket, $data, $_[0], $_[1])}
+        :                  sub {sysread( $socket, $data, $_[0], $_[1])}
+        ;
+    $data ||= q{};
+    my $n_bytes = 0;
+    while ($n_bytes < $len_to_travel) {
+        my $len_remain = $len_to_travel - $n_bytes;
+        my $n = $action->($len_remain, $n_bytes);
+        if (!defined($n)) {
+            die($!);
+        }
+        $n_bytes += $n;
+    }
+    $data;
+}
+
+# Performs the function of a worker. Receives a task. Actions it. Sends it back.
+sub _worker {
+    my ($socket, $action, $util) = @_;
+    while (defined(my $task = _item_receive($socket))) {
+        my $timer = $util->timer();
+        eval {
+            $task->set_state($task->ST_WORKING);
+            $action->($task->get_ctx());
+            $task->set_state($task->ST_OK);
+        };
+        if ($@) {
+            $task->set_state($task->ST_FAILED);
+            $task->set_error($@);
+        }
+        $task->set_elapse($timer->());
+        _item_send($socket, $task);
+    }
+    1;
+}
+
+# ------------------------------------------------------------------------------
+# The state of a worker.
+package FCM::Util::TaskRunner::WorkerState;
+use base qw{FCM::Class::HASH};
+
+__PACKAGE__->class(
+    {   'idle'   => {isa => '$', default => 1}, # worker is idle?
+        'pid'    => '$',                        # worker's PID
+        'socket' => '*',                        # socket to worker
+    },
+    {   init_attrib => sub {
+            my ($pid, $socket) = @_;
+            {'pid' => $pid, 'socket' => $socket};
+        },
+    },
+);
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Util::TaskRunner
+
+=head1 SYNOPSIS
+
+    use FCM::Context::Task;
+    use FCM::Util;
+    my $util = FCM::Util->new(\%attrib);
+    # ... time passes
+    my $runner = $util->task_runner(\&do_task, 4); # run with 4 workers
+    # ... time passes
+    my $get_ref = sub {
+        # ... an iterator to return an FCM::Context::Task object
+        # one at a time, returns undef if there is no currently available task
+    };
+    my $put_ref = sub {
+        my ($task) = @_;
+        # ... callback at end of each task
+    };
+    my $n_done = $runner->main($get_ref, $put_ref);
+
+=head1 DESCRIPTION
+
+This module is part of L<FCM::Util|FCM::Util>. See the description of the
+task_runner() method for details.
+
+An instance of this class is a runner of tasks. It can be configured to work in
+serial (default) or parallel. The class is a sub-class of
+L<FCM::Class::CODE|FCM::Class::CODE>.
+
+=head1 SEE ALSO
+
+This module is inspired by the CPAN modules Parallel::Fork::BossWorker and
+Parallel::Fork::BossWorkerAsync.
+
+L<FCM::Context::Task|FCM::Context::Task>,
+L<FCM::Util::TaskManager|FCM::Util::TaskManager>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Base.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Base.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Base.pm	(revision 5095)
@@ -0,0 +1,125 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Base
+#
+# DESCRIPTION
+#   This is base class for all FCM OO packages.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::Base;
+
+# Standard pragma
+use strict;
+use warnings;
+
+use FCM1::Config;
+
+my @scalar_properties = (
+  'config', # instance of FCM1::Config, configuration setting
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::Base->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::Base class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self  = {};
+  for (@scalar_properties) {
+    $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
+  }
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'config') {
+        # Configuration setting of the main program
+        $self->{$name} = FCM1::Config->instance();
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $self->setting (@args); # $self->config->setting
+#   $value = $self->verbose (@args); # $self->config->verbose
+# ------------------------------------------------------------------------------
+
+for my $name (qw/setting verbose/) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+    return $self->config->$name (@_);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $self->cfglabel (@args);
+#
+# DESCRIPTION
+#   This is an alias to $self->config->setting ('CFG_LABEL', @args);
+# ------------------------------------------------------------------------------
+
+sub cfglabel {
+  my $self = shift;
+  return $self->setting ('CFG_LABEL', @_);
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Build.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Build.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Build.pm	(revision 5095)
@@ -0,0 +1,1630 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Build
+#
+# DESCRIPTION
+#   This is the top level class for the FCM build system.
+#
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM1::Build;
+use base qw(FCM1::ConfigSystem);
+
+use Carp             qw{croak}                                       ;
+use Cwd              qw{cwd}                                         ;
+use FCM1::BuildSrc                                                   ;
+use FCM1::BuildTask                                                  ;
+use FCM1::Config                                                     ;
+use FCM1::Dest                                                       ;
+use FCM1::CfgLine                                                    ;
+use FCM1::Timer      qw{timestamp_command}                           ;
+use FCM1::Util       qw{expand_tilde run_command touch_file w_report};
+use File::Basename   qw{dirname}                                     ;
+use File::Spec                                                       ;
+use List::Util       qw{first}                                       ;
+use Text::ParseWords qw{shellwords}                                  ;
+
+# List of scalar property methods for this class
+my @scalar_properties = (
+  'name',    # name of this build
+  'target',  # targets of this build
+);
+
+# List of hash property methods for this class
+my @hash_properties = (
+  'srcpkg',      # source packages of this build
+  'dummysrcpkg', # dummy for handling package inheritance with file extension
+);
+
+# List of compare_setting_X methods
+my @compare_setting_methods = (
+  'compare_setting_bld_blockdata', # program executable blockdata dependency
+  'compare_setting_bld_dep',       # custom dependency setting
+  'compare_setting_bld_dep_excl',  # exclude dependency setting
+  'compare_setting_bld_dep_n',     # no dependency check
+  'compare_setting_bld_dep_pp',    # custom PP dependency setting
+  'compare_setting_bld_dep_exe',   # program executable extra dependency
+  'compare_setting_bld_exe_name',  # program executable rename
+  'compare_setting_bld_pp',        # PP flags
+  'compare_setting_infile_ext',    # input file extension
+  'compare_setting_outfile_ext',   # output file extension
+  'compare_setting_tool',          # build tool settings
+);
+
+my $DELIMITER_LIST = $FCM1::Config::DELIMITER_LIST;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::Build->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::Build class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::ConfigSystem->new (%args);
+
+  $self->{$_} = undef for (@scalar_properties);
+
+  $self->{$_} = {} for (@hash_properties);
+
+  bless $self, $class;
+
+  # List of sub-methods for parse_cfg
+  push @{ $self->cfg_methods }, (qw/target source tool dep misc/);
+
+  # Optional prefix in configuration declaration
+  $self->cfg_prefix ($self->setting (qw/CFG_LABEL BDECLARE/));
+
+  # System type
+  $self->type ('bld');
+
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'target') {
+        # Reference to an array
+        $self->{$name} = [];
+
+      } elsif ($name eq 'name') {
+        # Empty string
+        $self->{$name} = '';
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %hash = %{ $obj->X () };
+#   $obj->X (\%hash);
+#
+#   $value = $obj->X ($index);
+#   $obj->X ($index, $value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @hash_properties.
+#
+#   If no argument is set, this method returns a hash containing a list of
+#   objects. If an argument is set and it is a reference to a hash, the objects
+#   are replaced by the specified hash.
+#
+#   If a scalar argument is specified, this method returns a reference to an
+#   object, if the indexed object exists or undef if the indexed object does
+#   not exist. If a second argument is set, the $index element of the hash will
+#   be set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+for my $name (@hash_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my ($self, $arg1, $arg2) = @_;
+
+    # Ensure property is defined as a reference to a hash
+    $self->{$name} = {} if not defined ($self->{$name});
+
+    # Argument 1 can be a reference to a hash or a scalar index
+    my ($index, %hash);
+
+    if (defined $arg1) {
+      if (ref ($arg1) eq 'HASH') {
+        %hash = %$arg1;
+
+      } else {
+        $index = $arg1;
+      }
+    }
+
+    if (defined $index) {
+      # A scalar index is defined, set and/or return the value of an element
+      $self->{$name}{$index} = $arg2 if defined $arg2;
+
+      return (
+        exists $self->{$name}{$index} ? $self->{$name}{$index} : undef
+      );
+
+    } else {
+      # A scalar index is not defined, set and/or return the hash
+      $self->{$name} = \%hash if defined $arg1;
+      return $self->{$name};
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, $new_lines) = $self->X ($old_lines);
+#
+# DESCRIPTION
+#   This method compares current settings with those in the cache, where X is
+#   one of @compare_setting_methods.
+#
+#   If setting has changed:
+#   * For bld_blockdata, bld_dep_ext and bld_exe_name, it sets the re-generate
+#     make-rule flag to true.
+#   * For bld_dep_excl, in a standalone build, the method will remove the
+#     dependency cache files for affected sub-packages. It returns an error if
+#     the current build inherits from previous builds.
+#   * For bld_pp, it updates the PP setting for affected sub-packages.
+#   * For infile_ext, in a standalone build, the method will remove all the
+#     sub-package cache files and trigger a re-build by removing most
+#     sub-directories created by the previous build. It returns an error if the
+#     current build inherits from previous builds.
+#   * For outfile_ext, in a standalone build, the method will remove all the
+#     sub-package dependency cache files. It returns an error if the current
+#     build inherits from previous builds.
+#   * For tool, it updates the "flags" files for any changed tools.
+# ------------------------------------------------------------------------------
+
+for my $name (@compare_setting_methods) {
+  no strict 'refs';
+
+  *$name = sub {
+    my ($self, $old_lines) = @_;
+
+    (my $prefix = uc ($name)) =~ s/^COMPARE_SETTING_//;
+
+    my ($changed, $new_lines) =
+      $self->compare_setting_in_config ($prefix, $old_lines);
+
+    my $rc = scalar (keys %$changed);
+
+    if ($rc and $old_lines) {
+      $self->srcpkg ('')->is_updated (1);
+
+      if ($name =~ /^compare_setting_bld_dep(?:_excl|_n|_pp)?$/) {
+        # Mark affected packages as being updated
+        for my $key (keys %$changed) {
+          for my $pkg (values %{ $self->srcpkg }) {
+            next unless $pkg->is_in_package ($key);
+            $pkg->is_updated (1);
+          }
+        }
+
+      } elsif ($name eq 'compare_setting_bld_pp') {
+        # Mark affected packages as being updated
+        for my $key (keys %$changed) {
+          for my $pkg (values %{ $self->srcpkg }) {
+            next unless $pkg->is_in_package ($key);
+            next unless $self->srcpkg ($key)->is_type_any (
+              keys %{ $self->setting ('BLD_TYPE_DEP_PP') }
+            ); # Is a type requiring pre-processing
+
+            $pkg->is_updated (1);
+          }
+        }
+
+      } elsif ($name eq 'compare_setting_infile_ext') {
+        # Re-set input file type if necessary
+        for my $key (keys %$changed) {
+          for my $pkg (values %{ $self->srcpkg }) {
+            next unless $pkg->src and $pkg->ext and $key eq $pkg->ext;
+
+            $pkg->type (undef);
+          }
+        }
+
+        # Mark affected packages as being updated
+        for my $pkg (values %{ $self->srcpkg }) {
+          $pkg->is_updated (1);
+        }
+
+      } elsif ($name eq 'compare_setting_outfile_ext') {
+        # Mark affected packages as being updated
+        for my $pkg (values %{ $self->srcpkg }) {
+          $pkg->is_updated (1);
+        }
+
+      } elsif ($name eq 'compare_setting_tool') {
+        # Update the "flags" files for changed tools
+        for my $name (sort keys %$changed) {
+          my ($tool, @names) = split /__/, $name;
+          my $pkg  = join ('__', @names);
+          my @srcpkgs
+            = $self->srcpkg($pkg)      ? ($self->srcpkg($pkg))
+            : $self->dummysrcpkg($pkg) ? @{$self->dummysrcpkg($pkg)->children()}
+            :                            ()
+            ;
+          for my $srcpkg (@srcpkgs) {
+            my $file = File::Spec->catfile (
+              $self->dest->flagsdir, $srcpkg->flagsbase ($tool)
+            );
+            &touch_file ($file) or croak $file, ': cannot update, abort';
+
+            print $file, ': updated', "\n" if $self->verbose > 2;
+          }
+        }
+      }
+    }
+
+    return ($rc, $new_lines);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, $new_lines) = $self->compare_setting_dependency ($old_lines, $flag);
+#
+# DESCRIPTION
+#   This method uses the previous settings to determine the dependencies of
+#   current source files.
+# ------------------------------------------------------------------------------
+
+sub compare_setting_dependency {
+  my ($self, $old_lines, $flag) = @_;
+
+  my $prefix = $flag ? 'DEP_PP' : 'DEP';
+  my $method = $flag ? 'ppdep'  : 'dep';
+
+  my $rc = 0;
+  my $new_lines = [];
+
+  # Separate old lines
+  my %old;
+  if ($old_lines) {
+    for my $line (@$old_lines) {
+      next unless $line->label_starts_with ($prefix);
+      $old{$line->label_from_field (1)} = $line;
+    }
+  }
+
+  # Go through each source to see if the cache is up to date
+  my $count = 0;
+  my %mtime;
+  for my $srcpkg (values %{ $self->srcpkg }) {
+    next unless $srcpkg->cursrc and $srcpkg->type;
+
+    my $key = $srcpkg->pkgname;
+    my $out_of_date = $srcpkg->is_updated;
+
+    # Check modification time of cache and source file if not out of date
+    if (exists $old{$key}) {
+      if (not $out_of_date) {
+        $mtime{$old{$key}->src} = (stat ($old{$key}->src))[9]
+          if not exists ($mtime{$old{$key}->src});
+
+        $out_of_date = 1 if $mtime{$old{$key}->src} < $srcpkg->curmtime;
+      }
+    }
+    else {
+      $out_of_date = 1;
+    }
+
+    if ($out_of_date) {
+      # Re-scan dependency
+      $srcpkg->is_updated(1);
+      my ($source_is_read, $dep_hash_ref) = $srcpkg->get_dep($flag);
+      if ($source_is_read) {
+        $count++;
+      }
+      $srcpkg->$method($dep_hash_ref);
+      $rc = 1;
+    }
+    else {
+      # Use cached dependency
+      my ($progname, %hash) = split (
+        /$FCM1::Config::DELIMITER_PATTERN/, $old{$key}->value
+      );
+      $srcpkg->progname ($progname) if $progname and not $flag;
+      $srcpkg->$method (\%hash);
+    }
+
+    # New lines values: progname[::dependency-name::type][...]
+    my @value = ((defined $srcpkg->progname ? $srcpkg->progname : ''));
+    for my $name (sort keys %{ $srcpkg->$method }) {
+      push @value, $name, $srcpkg->$method ($name);
+    }
+
+    push @$new_lines, FCM1::CfgLine->new (
+      LABEL => $prefix . $FCM1::Config::DELIMITER . $key,
+      VALUE => join ($FCM1::Config::DELIMITER, @value),
+    );
+  }
+
+  print 'No. of file', ($count > 1 ? 's' : ''), ' scanned for',
+        ($flag ? ' PP': ''), ' dependency: ', $count, "\n"
+    if $self->verbose and $count;
+
+  return ($rc, $new_lines);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, $new_lines) = $self->compare_setting_srcpkg ($old_lines);
+#
+# DESCRIPTION
+#   This method uses the previous settings to determine the type of current
+#   source files.
+# ------------------------------------------------------------------------------
+
+sub compare_setting_srcpkg {
+  my ($self, $old_lines) = @_;
+
+  my $prefix = 'SRCPKG';
+
+  # Get relevant items from old lines, stripping out $prefix
+  my %old;
+  if ($old_lines) {
+    for my $line (@$old_lines) {
+      next unless $line->label_starts_with ($prefix);
+      $old{$line->label_from_field (1)} = $line;
+    }
+  }
+
+  # Check for change, use previous setting if exist
+  my $out_of_date = 0;
+  my %mtime;
+  for my $key (keys %{ $self->srcpkg }) {
+    if (exists $old{$key}) {
+      next unless $self->srcpkg ($key)->cursrc;
+
+      my $type = defined $self->setting ('BLD_TYPE', $key)
+                 ? $self->setting ('BLD_TYPE', $key) : $old{$key}->value;
+
+      $self->srcpkg ($key)->type ($type);
+
+      if ($type ne $old{$key}->value) {
+        $self->srcpkg ($key)->is_updated (1);
+        $out_of_date = 1;
+      }
+
+      if (not $self->srcpkg ($key)->is_updated) {
+        $mtime{$old{$key}->src} = (stat ($old{$key}->src))[9]
+          if not exists ($mtime{$old{$key}->src});
+
+        $self->srcpkg ($key)->is_updated (1)
+          if $mtime{$old{$key}->src} < $self->srcpkg ($key)->curmtime;
+      }
+
+    } else {
+      $self->srcpkg ($key)->is_updated (1);
+      $out_of_date = 1;
+    }
+  }
+
+  # Check for deleted keys
+  for my $key (keys %old) {
+    next if $self->srcpkg ($key);
+
+    $out_of_date = 1;
+  }
+
+  # Return reference to an array of new lines
+  my $new_lines = [];
+  for my $key (keys %{ $self->srcpkg }) {
+    push @$new_lines, FCM1::CfgLine->new (
+      LABEL => $prefix . $FCM1::Config::DELIMITER . $key,
+      VALUE => $self->srcpkg ($key)->type,
+    );
+  }
+
+  return ($out_of_date, $new_lines);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, $new_lines) = $self->compare_setting_target ($old_lines);
+#
+# DESCRIPTION
+#   This method compare the previous target settings with current ones.
+# ------------------------------------------------------------------------------
+
+sub compare_setting_target {
+  my ($self, $old_lines) = @_;
+
+  my $prefix = 'TARGET';
+  my $old;
+  if ($old_lines) {
+    for my $line (@$old_lines) {
+      next unless $line->label_starts_with ($prefix);
+      $old = $line->value;
+      last;
+    }
+  }
+
+  my $new = join (' ', sort @{ $self->target });
+
+  return (
+    (defined ($old) ? $old ne $new : 1),
+    [FCM1::CfgLine->new (LABEL => $prefix, VALUE => $new)],
+  );
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_fortran_interface_generator ();
+#
+# DESCRIPTION
+#   This method invokes the Fortran interface generator for all Fortran free
+#   format source files. It returns true on success.
+# ------------------------------------------------------------------------------
+
+sub invoke_fortran_interface_generator {
+  my $self = shift;
+
+  my $pdoneext = $self->setting (qw/OUTFILE_EXT PDONE/);
+
+  # Set up build task to generate interface files for all selected Fortran 9x
+  # sources
+  my %task = ();
+  SRC_FILE:
+  for my $srcfile (values %{ $self->srcpkg }) {
+    if (!defined($srcfile->interfacebase())) {
+      next SRC_FILE;
+    }
+    my $target  = $srcfile->interfacebase . $pdoneext;
+
+    $task{$target} = FCM1::BuildTask->new (
+      TARGET     => $target,
+      TARGETPATH => $self->dest->donepath,
+      SRCFILE    => $srcfile,
+      DEPENDENCY => [$srcfile->flagsbase ('GENINTERFACE')],
+      ACTIONTYPE => 'GENINTERFACE',
+    );
+
+    # Set up build tasks for each source file/package flags file for interface
+    # generator tool
+    for my $i (1 .. @{ $srcfile->pkgnames }) {
+      my $target = $srcfile->flagsbase ('GENINTERFACE', -$i);
+      my $depend = $i < @{ $srcfile->pkgnames }
+                   ? $srcfile->flagsbase ('GENINTERFACE', -$i - 1)
+                   : undef;
+
+      $task{$target} = FCM1::BuildTask->new (
+        TARGET     => $target,
+        TARGETPATH => $self->dest->flagspath,
+        DEPENDENCY => [defined ($depend) ? $depend : ()],
+        ACTIONTYPE => 'UPDATE',
+      ) if not exists $task{$target};
+    }
+  }
+
+  # Set up build task to update the flags file for interface generator tool
+  $task{$self->srcpkg ('')->flagsbase ('GENINTERFACE')} = FCM1::BuildTask->new (
+    TARGET     => $self->srcpkg ('')->flagsbase ('GENINTERFACE'),
+    TARGETPATH => $self->dest->flagspath,
+    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 'No. of generated Fortran interface', ($count > 1 ? 's' : ''), ': ',
+        $count, "\n"
+    if $self->verbose and $count;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_make (%args);
+#
+# DESCRIPTION
+#   This method invokes the make stage of the build system. It returns true 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.
+#   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).
+#   TARGETS - Specify targets to be built. If set, these targets will be built
+#             instead of the ones specified in the build configuration file.
+# ------------------------------------------------------------------------------
+
+sub invoke_make {
+  my ($self, %args) = @_;
+  $args{TARGETS} ||= ['all'];
+  $args{JOBS}    ||= 1;
+  my @command = (
+    $self->setting(qw/TOOL MAKE/),
+    shellwords($self->setting(qw/TOOL MAKEFLAGS/)),
+    # -f Makefile
+    ($self->setting(qw/TOOL MAKE_FILE/), $self->dest()->bldmakefile()),
+    # -j N
+    ($args{JOBS} ? ($self->setting(qw/TOOL MAKE_JOB/), $args{JOBS}) : ()),
+    # -s
+    ($self->verbose() < 3 ? $self->setting(qw/TOOL MAKE_SILENT/) : ()),
+    @{$args{TARGETS}}
+  );
+  my $old_cwd = $self->_chdir($self->dest()->rootdir());
+  run_command(
+    \@command, ERROR => 'warn', RC => \my($code), TIME => $self->verbose() >= 3,
+  );
+  $self->_chdir($old_cwd);
+
+  my $rc = !$code;
+  if ($rc && $args{ARCHIVE}) {
+    $rc = $self->dest()->archive();
+  }
+  $rc &&= $self->dest()->create_bldrunenvsh();
+  while (my ($key, $source) = each(%{$self->srcpkg()})) {
+    $rc &&= defined($source->write_lib_dep_excl());
+  }
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_pre_process ();
+#
+# DESCRIPTION
+#   This method invokes the pre-process stage of the build system. It
+#   returns true on success.
+# ------------------------------------------------------------------------------
+
+sub invoke_pre_process {
+  my $self = shift;
+   
+  # Check whether pre-processing is necessary
+  my $invoke = 0;
+  for (values %{ $self->srcpkg }) {
+    next unless $_->get_setting ('BLD_PP');
+    $invoke = 1;
+    last;
+  }
+  return 1 unless $invoke;
+
+  # Scan header dependency
+  my $rc = $self->compare_setting (
+    METHOD_LIST => ['compare_setting_dependency'],
+    METHOD_ARGS => ['BLD_TYPE_DEP_PP'],
+    CACHEBASE   => $self->setting ('CACHE_DEP_PP'),
+  );
+
+  return $rc if not $rc;
+
+  my %task     = ();
+  my $pdoneext = $self->setting (qw/OUTFILE_EXT PDONE/);
+
+  # Set up tasks for each source file
+  for my $srcfile (values %{ $self->srcpkg }) {
+    if ($srcfile->is_type_all (qw/CPP INCLUDE/)) {
+      # Set up a copy build task for each include file
+      $task{$srcfile->base} = FCM1::BuildTask->new (
+        TARGET     => $srcfile->base,
+        TARGETPATH => $self->dest->incpath,
+        SRCFILE    => $srcfile,
+        DEPENDENCY => [keys %{ $srcfile->ppdep }],
+        ACTIONTYPE => 'COPY',
+      );
+
+    } elsif ($srcfile->lang ('TOOL_SRC_PP')) {
+      next unless $srcfile->get_setting ('BLD_PP');
+
+      # Set up a PP build task for each source file
+      my $target = $srcfile->base . $pdoneext;
+
+      # 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} = FCM1::BuildTask->new (
+        TARGET     => $target,
+        TARGETPATH => $self->dest->donepath,
+        SRCFILE    => $srcfile,
+        DEPENDENCY => [$srcfile->flagsbase ('PPKEYS'), keys %{ $srcfile->ppdep }],
+        ACTIONTYPE => 'PP',
+      );
+
+      # Set up update ppkeys/flags build tasks for each source file/package
+      my $ppkeys = $self->setting (
+        'TOOL_SRC_PP', $srcfile->lang ('TOOL_SRC_PP'), 'PPKEYS'
+      );
+
+      for my $i (1 .. @{ $srcfile->pkgnames }) {
+        my $target = $srcfile->flagsbase ($ppkeys, -$i);
+        my $depend = $i < @{ $srcfile->pkgnames }
+                     ? $srcfile->flagsbase ($ppkeys, -$i - 1)
+                     : undef;
+
+        $task{$target} = FCM1::BuildTask->new (
+          TARGET     => $target,
+          TARGETPATH => $self->dest->flagspath,
+          DEPENDENCY => [defined ($depend) ? $depend : ()],
+          ACTIONTYPE => 'UPDATE',
+        ) if not exists $task{$target};
+      }
+    }
+  }
+
+  # Set up update global ppkeys build tasks
+  for my $lang (keys %{ $self->setting ('TOOL_SRC_PP') }) {
+    my $target = $self->srcpkg ('')->flagsbase (
+      $self->setting ('TOOL_SRC_PP', $lang, 'PPKEYS')
+    );
+
+    $task{$target} = FCM1::BuildTask->new (
+      TARGET     => $target,
+      TARGETPATH => $self->dest->flagspath,
+      ACTIONTYPE => 'UPDATE',
+    );
+  }
+
+  # Build all PP tasks
+  my $count = 0;
+  for my $task (values %task) {
+    next unless $task->actiontype eq 'PP';
+
+    my $rc = $task->action (TASKLIST => \%task);
+    $task->srcfile->is_updated ($rc);
+    $count++ if $rc;
+  }
+
+  print 'No. of pre-processed file', ($count > 1 ? 's' : ''), ': ', $count, "\n"
+    if $self->verbose and $count;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_scan_dependency ();
+#
+# DESCRIPTION
+#   This method invokes the scan dependency stage of the build system. It
+#   returns true on success.
+# ------------------------------------------------------------------------------
+
+sub invoke_scan_dependency {
+  my $self = shift;
+
+  # Scan/retrieve dependency
+  # ----------------------------------------------------------------------------
+  my $rc = $self->compare_setting (
+    METHOD_LIST => ['compare_setting_dependency'],
+    CACHEBASE   => $self->setting ('CACHE_DEP'),
+  );
+
+  # Check whether make file is out of date
+  # ----------------------------------------------------------------------------
+  my $out_of_date = ! -f $self->dest->bldmakefile;
+
+  if ($rc and not $out_of_date) {
+    for (qw/CACHE CACHE_DEP/) {
+      my $cache_mtime = (stat (File::Spec->catfile (
+        $self->dest->cachedir, $self->setting ($_),
+      )))[9];
+      my $mfile_mtime = (stat ($self->dest->bldmakefile))[9];
+
+      next if not defined $cache_mtime;
+      next if $cache_mtime < $mfile_mtime;
+      $out_of_date = 1;
+      last;
+    }
+  }
+
+  if ($rc and not $out_of_date) {
+    for (values %{ $self->srcpkg }) {
+      next unless $_->is_updated;
+      $out_of_date = 1;
+      last;
+    }
+  }
+
+  if ($rc and $out_of_date) {
+    # Write Makefile
+    # --------------------------------------------------------------------------
+    # Register non-word package name
+    my $unusual = 0;
+    for my $key (sort keys %{ $self->srcpkg }) {
+      next if $self->srcpkg ($key)->src;
+      next if $key =~ /^\w*$/;
+
+      $self->setting (
+        ['FCM_PCK_OBJECTS', $key], 'FCM_PCK_OBJECTS' . $unusual++,
+      );
+    }
+
+    # Write different parts in the Makefile
+    my $makefile = '# Automatic Makefile' . "\n\n";
+    $makefile .= 'FCM_BLD_NAME = ' . $self->name . "\n" if $self->name;
+    $makefile .= 'FCM_BLD_CFG = ' . $self->cfg->actual_src . "\n";
+    $makefile .= 'export FCM_VERBOSE ?= ' . $self->verbose . "\n\n";
+    $makefile .= "export OBJECTS\n";
+    $makefile .= $self->dest->write_rules;
+    $makefile .= $self->_write_makefile_perl5lib;
+    $makefile .= $self->_write_makefile_tool;
+    $makefile .= $self->_write_makefile_vpath;
+    $makefile .= $self->_write_makefile_target;
+
+    # Write rules for each source package
+    # Ensure that container packages come before files - this allows $(OBJECTS)
+    # and its dependent variables to expand correctly
+    my @srcpkg = sort {
+      if ($self->srcpkg ($a)->libbase and $self->srcpkg ($b)->libbase) {
+        $b cmp $a;
+
+      } elsif ($self->srcpkg ($a)->libbase) {
+        -1;
+
+      } elsif ($self->srcpkg ($b)->libbase) {
+        1;
+
+      } else {
+        $a cmp $b;
+      }
+    } keys %{ $self->srcpkg };
+
+    for (@srcpkg) {
+      $makefile .= $self->srcpkg ($_)->write_rules if $self->srcpkg ($_)->rules;
+    }
+    $makefile .= '# EOF' . "\n";
+
+    # Update Makefile
+    open OUT, '>', $self->dest->bldmakefile
+      or croak $self->dest->bldmakefile, ': cannot open (', $!, '), abort';
+    print OUT $makefile;
+    close OUT
+      or croak $self->dest->bldmakefile, ': cannot close (', $!, '), abort';
+
+    print $self->dest->bldmakefile, ': updated', "\n" if $self->verbose;
+
+    # Check for duplicated targets
+    # --------------------------------------------------------------------------
+    # Get list of types that cannot have duplicated targets
+    my @no_duplicated_target_types = split (
+      /$DELIMITER_LIST/,
+      $self->setting ('BLD_TYPE_NO_DUPLICATED_TARGET'),
+    );
+
+    my %targets;
+    for my $name (sort keys %{ $self->srcpkg }) {
+      next unless $self->srcpkg ($name)->rules;
+
+      for my $key (sort keys %{ $self->srcpkg ($name)->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
+              $self->srcpkg ($name)->is_type_any (@no_duplicated_target_types) and
+              $targets{$key}->is_type_any (@no_duplicated_target_types)) {
+            $status = 'ERROR';
+            $rc = 0;
+          }
+
+          # Report the warning/error
+          w_report $status, ': ', $key, ': duplicated targets for building:';
+          w_report '       ', $targets{$key}->src;
+          w_report '       ', $self->srcpkg ($name)->src;
+
+        } else {
+          $targets{$key} = $self->srcpkg ($name);
+        }
+      }
+    }
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_setup_build ();
+#
+# DESCRIPTION
+#   This method invokes the setup_build stage of the build system. It returns
+#   true on success.
+# ------------------------------------------------------------------------------
+
+sub invoke_setup_build {
+  my $self = shift;
+
+  my $rc = 1;
+
+  # Extract archived sub-directories if necessary
+  $rc = $self->dest->dearchive if $rc;
+
+  # Compare cache
+  $rc = $self->compare_setting (METHOD_LIST => [
+    'compare_setting_target', # targets
+    'compare_setting_srcpkg', # source package type
+    @compare_setting_methods,
+  ]) if $rc;
+
+  # Set up runtime dependency scan patterns
+  my %dep_pattern = %{ $self->setting ('BLD_DEP_PATTERN') };
+  for my $key (keys %dep_pattern) {
+    my $pattern = $dep_pattern{$key};
+
+    while ($pattern =~ /##([\w:]+)##/g) {
+      my $match = $1;
+      my $val   = $self->setting (split (/$FCM1::Config::DELIMITER/, $match));
+
+      last unless defined $val;
+      $val =~ s/\./\\./;
+
+      $pattern =~ s/##$match##/$val/;
+    }
+
+    $self->setting (['BLD_DEP_PATTERN', $key], $pattern)
+      unless $pattern eq $dep_pattern{$key};
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_system (%args);
+#
+# DESCRIPTION
+#   This method invokes the build system. It returns true on success. See also
+#   the header for invoke_make for further information on arguments.
+#
+# ARGUMENTS
+#   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
+# ------------------------------------------------------------------------------
+
+sub invoke_system {
+  my $self = shift;
+  my %args = @_;
+
+  # Parse arguments
+  # ----------------------------------------------------------------------------
+  # Default: run all 5 stages
+  my $stage = (exists $args{STAGE} and $args{STAGE}) ? $args{STAGE} : 5;
+
+  # Resolve named stages
+  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,
+    );
+
+    # Does it match a recognised stage?
+    for my $name (keys %stagenames) {
+      next unless $stage =~ /$name/i;
+
+      $stage = $stagenames{$name};
+      last;
+    }
+
+    # Specified stage name not recognised, default to 5
+    if ($stage !~ /^\d$/) {
+      w_report 'WARNING: ', $stage, ': invalid build stage, default to 5.';
+      $stage = 5;
+    }
+  }
+
+  # Run the method associated with each stage
+  # ----------------------------------------------------------------------------
+  my $rc = 1;
+
+  my @stages = (
+    ['Setup build'               , 'invoke_setup_build'],
+    ['Pre-process'               , 'invoke_pre_process'],
+    ['Scan dependency'           , 'invoke_scan_dependency'],
+    ['Generate Fortran interface', 'invoke_fortran_interface_generator'],
+    ['Make'                      , 'invoke_make'],
+  );
+
+  for my $i (1 .. 5) {
+    last if (not $rc) or $i > $stage;
+
+    my ($name, $method) = @{ $stages[$i - 1] };
+    $rc = $self->invoke_stage ($name, $method, %args) if $rc and $stage >= $i;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_dep (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the dependency settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_dep {
+  my ($self, $cfg_lines) = @_;
+
+  my $rc = 1;
+
+  # EXCL_DEP, EXE_DEP and BLOCKDATA declarations
+  # ----------------------------------------------------------------------------
+  for my $name (qw/BLD_BLOCKDATA BLD_DEP BLD_DEP_EXCL BLD_DEP_EXE/) {
+    for my $line (grep {$_->slabel_starts_with_cfg ($name)} @$cfg_lines) {
+      # Separate label into a list, delimited by double-colon, remove 1st field
+      my @flds = $line->slabel_fields;
+      shift @flds;
+
+      if ($name =~ /^(?:BLD_DEP|BLD_DEP_EXCL|BLD_DEP_PP)$/) {
+        # BLD_DEP_*: label fields may contain sub-package
+        my $pk = @flds ? join ('__', @flds) : '';
+
+        # Check whether sub-package is valid
+        if ($pk and not ($self->srcpkg ($pk) or $self->dummysrcpkg ($pk))) {
+          $line->error ($line->label . ': invalid sub-package in declaration.');
+          $rc = 0;
+          next;
+        }
+
+        # Setting is stored in an array reference
+        $self->setting ([$name, $pk], [])
+          if not defined $self->setting ($name, $pk);
+
+        # Add current declaration to the array if necessary
+        my $list  = $self->setting ($name, $pk);
+        my $value = $name eq 'BLD_DEP_EXCL' ? uc ($line->value) : $line->value;
+        push @$list, $value if not grep {$_ eq $value} @$list;
+
+      } else {
+        # EXE_DEP and BLOCKDATA: label field may be an executable target
+        my $target = @flds ? $flds[0] : '';
+
+        # The value contains a list of objects and/or sub-package names
+        my @deps   = split /\s+/, $line->value;
+
+        if (not @deps) {
+          if ($name eq 'BLD_BLOCKDATA') {
+            # The objects containing a BLOCKDATA program unit must be declared
+            $line->error ($line->label . ': value not set.');
+            $rc = 0;
+            next;
+
+          } else {
+            # If $value is a null string, target(s) depends on all objects
+            push @deps, '';
+          }
+        }
+
+        for my $dep (@deps) {
+          $dep =~ s/$FCM1::Config::DELIMITER_PATTERN/__/g;
+        }
+
+        $self->setting ([$name, $target], join (' ', sort @deps));
+      }
+
+      $line->parsed (1);
+    }
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_dest (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the build destination settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_dest {
+  my ($self, $cfg_lines) = @_;
+
+  my $rc = $self->SUPER::parse_cfg_dest ($cfg_lines);
+
+  # Set up search paths
+  for my $name (@FCM1::Dest::paths) {
+    (my $label = uc ($name)) =~ s/PATH//;
+
+    $self->setting (['PATH', $label], $self->dest->$name);
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_misc (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses misc build settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_misc {
+    my ($self, $cfg_lines_ref) = @_;
+    my $rc = 1;
+    my %item_of = (
+        BLD_DEP_N    => [\&_parse_cfg_misc_dep_n   , 1   ], # boolean
+        BLD_EXE_NAME => [\&_parse_cfg_misc_exe_name      ],
+        BLD_LIB      => [\&_parse_cfg_misc_dep_n         ],
+        BLD_PP       => [\&_parse_cfg_misc_dep_n   , 1   ], # boolean
+        BLD_TYPE     => [\&_parse_cfg_misc_dep_n         ],
+        INFILE_EXT   => [\&_parse_cfg_misc_file_ext, 0, 1], # uc($value)
+        OUTFILE_EXT  => [\&_parse_cfg_misc_file_ext, 1, 0], # uc($ns)
+    );
+    while (my ($key, $item) = each(%item_of)) {
+        my ($handler, @extra_arguments) = @{$item};
+        for my $line (@{$cfg_lines_ref}) {
+            if ($line->slabel_starts_with_cfg($key)) {
+                if ($handler->($self, $key, $line, @extra_arguments)) {
+                    $line->parsed(1);
+                }
+                else {
+                    $rc = 0;
+                }
+            }
+        }
+    }
+    return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# parse_cfg_misc: handler of BLD_EXE_NAME or similar.
+sub _parse_cfg_misc_exe_name {
+    my ($self, $key, $line) = @_;
+    my ($prefix, $name, @fields) = $line->slabel_fields();
+    if (!$name || @fields) {
+        $line->error(sprintf('%s: expects a single label name field.', $key));
+        return 0;
+    }
+    $self->setting([$key, $name], $line->value());
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# parse_cfg_misc: handler of BLD_DEP_N or similar.
+sub _parse_cfg_misc_dep_n {
+    my ($self, $key, $line, $value_is_boolean) = @_;
+    my ($prefix, @fields) = $line->slabel_fields();
+    my $ns = @fields ? join(q{__}, @fields) : q{};
+    if ($ns && !$self->srcpkg($ns) && !$self->dummysrcpkg($ns)) {
+        $line->error($line->label() . ': invalid sub-package in declaration.');
+        return 0;
+    }
+    my @srcpkgs
+        = $self->dummysrcpkg($ns) ? @{$self->dummysrcpkg($ns)->children()}
+        :                           $self->srcpkg($ns)
+        ;
+    my $value = $value_is_boolean ? $line->bvalue() : $line->value();
+    for my $srcpkg (@srcpkgs) {
+        $self->setting([$key, $srcpkg->pkgname()], $value);
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# parse_cfg_misc: handler of INFILE_EXT/OUTFILE_EXT or similar.
+sub _parse_cfg_misc_file_ext {
+    my ($self, $key, $line, $ns_in_uc, $value_in_uc) = @_;
+    my ($prefix, $ns) = $line->slabel_fields();
+    my $value = $value_in_uc ? uc($line->value()) : $line->value();
+    $self->setting([$key, ($ns_in_uc ? uc($ns) : $ns)], $value);
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_source (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the source package settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_source {
+  my ($self, $cfg_lines) = @_;
+
+  my $rc  = 1;
+  my %src = ();
+
+  # Automatic source directory search?
+  # ----------------------------------------------------------------------------
+  my $search = 1;
+
+  for my $line (grep {$_->slabel_starts_with_cfg ('SEARCH_SRC')} @$cfg_lines) {
+    $search = $line->bvalue;
+    $line->parsed (1);
+  }
+
+  # Search src/ sub-directory if necessary
+  %src = %{ $self->dest->get_source_files } if $search;
+
+  # SRC declarations
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg ('FILE')} @$cfg_lines) {
+    # Expand ~ notation and path relative to srcdir of destination
+    my $value = $line->value;
+    $value = File::Spec->rel2abs (&expand_tilde ($value), $self->dest->srcdir);
+
+    if (! -e $value) {
+      $line->error ($value . ': source does not exist or is not readable.');
+      next;
+    }
+
+    # Package name
+    my @names = $line->slabel_fields;
+    shift @names;
+
+    # If package name not set, determine using the path if possible
+    if (not @names) {
+      my $package = $self->dest->get_pkgname_of_path ($value);
+      @names = @$package if defined $package;
+    }
+
+    if (not @names) {
+      $line->error ($self->cfglabel ('FILE') .
+                    ': package not specified/cannot be determined.');
+      next;
+    }
+
+    $src{join ('__', @names)} = $value;
+
+    $line->parsed (1);
+  }
+
+  # For directories, get non-recursive file listing, and add to %src
+  # ----------------------------------------------------------------------------
+  for my $key (keys %src) {
+    next unless -d $src{$key};
+
+    opendir DIR, $src{$key} or die $src{$key}, ': cannot read directory';
+    while (my $base = readdir 'DIR') {
+      next if $base =~ /^\./;
+
+      my $file = File::Spec->catfile ($src{$key}, $base);
+      next if ! -f $file;
+
+      my $name = join ('__', ($key, $base));
+      $src{$name} = $file unless exists $src{$name};
+    }
+    closedir DIR;
+
+    delete $src{$key};
+  }
+
+  # Set up source packages
+  # ----------------------------------------------------------------------------
+  my %pkg = ();
+  for my $name (keys %src) {
+    $pkg{$name} = FCM1::BuildSrc->new (PKGNAME => $name, SRC => $src{$name});
+  }
+
+  # INHERIT::SRC declarations
+  # ----------------------------------------------------------------------------
+  my %can_inherit = ();
+  for my $line (
+    grep {$_->slabel_starts_with_cfg(qw/INHERIT FILE/)} @{$cfg_lines}
+  ) {
+    my ($key1, $key2, @ns) = $line->slabel_fields();
+    $can_inherit{join('__', @ns)} = $line->bvalue();
+    $line->parsed(1);
+  }
+
+  # Inherit packages, if it is OK to do so
+  for my $inherited_build (reverse(@{$self->inherit()})) {
+    SRCPKG:
+    while (my ($key, $srcpkg) = each(%{$inherited_build->srcpkg()})) {
+      if (exists($pkg{$key}) || !$srcpkg->src()) {
+        next SRCPKG;
+      }
+      my $known_key = first {exists($can_inherit{$_})} @{$srcpkg->pkgnames()};
+      if (defined($known_key) && !$can_inherit{$known_key}) {
+        next SRCPKG;
+      }
+      $pkg{$key} = $srcpkg;
+    }
+  }
+
+  # Get list of intermediate "packages"
+  # ----------------------------------------------------------------------------
+  for my $name (keys %pkg) {
+    # Name of current package
+    my @names = split /__/, $name;
+
+    my $cur = $name;
+
+    while ($cur) {
+      # Name of parent package
+      pop @names;
+      my $parent = @names ? join ('__', @names) : '';
+
+      # If parent package does not exist, create it
+      $pkg{$parent} = FCM1::BuildSrc->new (PKGNAME => $parent)
+        unless exists $pkg{$parent};
+
+      # Current package is a child of the parent package
+      push @{ $pkg{$parent}->children }, $pkg{$cur}
+        unless grep {$_->pkgname eq $cur} @{ $pkg{$parent}->children };
+
+      # Go up a package
+      $cur = $parent;
+    }
+  }
+
+  $self->srcpkg (\%pkg);
+
+  # Dummy: e.g. "foo/bar/baz.egg" belongs to the "foo/bar/baz" dummy.
+  # ----------------------------------------------------------------------------
+  SRCPKG:
+  while (my ($name, $srcpkg) = each(%pkg)) {
+    if (!$srcpkg->src()) { # ensure that $srcpkg represents a source file
+      next SRCPKG;
+    }
+    my @names = split('__', $name);
+    if (@names) {
+      $names[-1] =~ s{\.\w+ \z}{}msx;
+    }
+    my $dummy_name = join('__', @names);
+    if ($dummy_name eq $name || defined($self->srcpkg($dummy_name))) {
+      next SRCPKG;
+    }
+    if (!defined($self->dummysrcpkg($dummy_name))) {
+      $self->dummysrcpkg($dummy_name, FCM1::BuildSrc->new(PKGNAME => $dummy_name));
+    }
+    push(@{$self->dummysrcpkg($dummy_name)->children()}, $srcpkg);
+  }
+
+  # Make sure a package is defined
+  # ----------------------------------------------------------------------------
+  if (not %{$self->srcpkg}) {
+    w_report 'ERROR: ', $self->cfg->actual_src, ': no source file to build.';
+    $rc = 0;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_target (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the target settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_target {
+  my ($self, $cfg_lines) = @_;
+
+  # NAME declaraions
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg ('NAME')} @$cfg_lines) {
+    $self->name ($line->value);
+    $line->parsed (1);
+  }
+
+  # TARGET declarations
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg ('TARGET')} @$cfg_lines) {
+    # Value is a space delimited list
+    push @{ $self->target }, split (/\s+/, $line->value);
+    $line->parsed (1);
+  }
+
+  # INHERIT::TARGET declarations
+  # ----------------------------------------------------------------------------
+  # By default, do not inherit target
+  my $inherit_flag = 0;
+
+  for (grep {$_->slabel_starts_with_cfg (qw/INHERIT TARGET/)} @$cfg_lines) {
+    $inherit_flag = $_->bvalue;
+    $_->parsed (1);
+  }
+
+  # Inherit targets from inherited build, if $inherit_flag is set to true
+  # ----------------------------------------------------------------------------
+  if ($inherit_flag) {
+    for my $use (reverse @{ $self->inherit }) {
+      unshift @{ $self->target }, @{ $use->target };
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_tool (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the tool settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_tool {
+  my ($self, $cfg_lines) = @_;
+
+  my $rc = 1;
+
+  my %tools         = %{ $self->setting ('TOOL') };
+  my @package_tools = split(/$DELIMITER_LIST/, $self->setting('TOOL_PACKAGE'));
+
+  # TOOL declaration
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg ('TOOL')} @$cfg_lines) {
+    # Separate label into a list, delimited by double-colon, remove TOOL
+    my @flds = $line->slabel_fields;
+    shift @flds;
+
+    # Check that there is a field after TOOL
+    if (not @flds) {
+      $line->error ('TOOL: not followed by a valid label.');
+      $rc = 0;
+      next;
+    }
+
+    # The first field is the tool iteself, identified in uppercase
+    $flds[0] = uc ($flds[0]);
+
+    # Check that the tool is recognised
+    if (not exists $tools{$flds[0]}) {
+      $line->error ($flds[0] . ': not a valid TOOL.');
+      $rc = 0;
+      next;
+    }
+
+    # Check sub-package declaration
+    if (@flds > 1 and not grep {$_ eq $flds[0]} @package_tools) {
+      $line->error ($flds[0] . ': sub-package not accepted with this TOOL.');
+      $rc = 0;
+      next;
+    }
+
+    # Name of declared package
+    my $pk = join ('__', @flds[1 .. $#flds]);
+
+    # Check whether package exists
+    if (not ($self->srcpkg ($pk) or $self->dummysrcpkg ($pk))) {
+      $line->error ($line->label . ': invalid sub-package in declaration.');
+      $rc = 0;
+      next;
+    }
+
+    $self->setting (['TOOL', join ('__', @flds)], $line->value);
+    $line->parsed (1);
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_write_makefile_perl5lib ();
+#
+# DESCRIPTION
+#   This method returns a makefile $string for defining $PERL5LIB.
+# ------------------------------------------------------------------------------
+
+sub _write_makefile_perl5lib {
+  my $self = shift;
+
+  my $classpath = File::Spec->catfile (split (/::/, ref ($self))) . '.pm';
+
+  my $libdir  = dirname (dirname ($INC{$classpath}));
+  my @libpath = split (/:/, (exists $ENV{PERL5LIB} ? $ENV{PERL5LIB} : ''));
+
+  my $string = ((grep {$_ eq $libdir} @libpath)
+                ? ''
+                : 'export PERL5LIB := ' . $libdir .
+                  (exists $ENV{PERL5LIB} ? ':$(PERL5LIB)' : '') . "\n\n");
+
+  return $string;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_write_makefile_target ();
+#
+# DESCRIPTION
+#   This method returns a makefile $string for defining the default targets.
+# ------------------------------------------------------------------------------
+
+sub _write_makefile_target {
+  my $self = shift;
+
+  # Targets of the build
+  # ----------------------------------------------------------------------------
+  my @targets = @{ $self->target };
+  if (not @targets) {
+    # Build targets not specified by user, default to building all main programs
+    my @programs = ();
+
+    # Get all main programs from all packages
+    for my $pkg (values %{ $self->srcpkg }) {
+      push @programs, $pkg->exebase if $pkg->exebase;
+    }
+
+    @programs = sort (@programs);
+
+    if (@programs) {
+      # Build main programs, if there are any
+      @targets = @programs;
+
+    } else {
+      # No main program in source tree, build the default library
+      @targets = ($self->srcpkg ('')->libbase);
+    }
+  }
+
+  my $return = 'FCM_BLD_TARGETS = ' . join (' ', @targets) . "\n\n";
+
+  # Default targets
+  $return .= '.PHONY : all' . "\n\n";
+  $return .= 'all : $(FCM_BLD_TARGETS)' . "\n\n";
+
+  # Targets for copy dummy
+  $return .= sprintf("%s:\n\ttouch \$@\n\n", $self->setting(qw/BLD_CPDUMMY/));
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_write_makefile_tool ();
+#
+# DESCRIPTION
+#   This method returns a makefile $string for defining the build tools.
+# ------------------------------------------------------------------------------
+
+sub _write_makefile_tool {
+  my $self = shift;
+
+  # List of build tools
+  my $tool = $self->setting ('TOOL');
+
+  # List of tools local to FCM, (will not be exported)
+  my %localtool = map {($_, 1)} split ( # map into a hash table
+    /$DELIMITER_LIST/, $self->setting ('TOOL_LOCAL'),
+  );
+
+  # Export required tools
+  my $count = 0;
+  my $return = '';
+  for my $name (sort keys %$tool) {
+    # Ignore local tools
+    next if exists $localtool{(split (/__/, $name))[0]};
+
+    if ($name =~ /^\w+$/) {
+      # Tools with normal name, just export it as an environment variable
+      $return .= 'export ' . $name . ' = ' . $tool->{$name} . "\n";
+
+    } else {
+      # Tools with unusual characters, export using a label/value pair
+      $return .= 'export FCM_UNUSUAL_TOOL_LABEL' . $count . ' = ' . $name . "\n";
+      $return .= 'export FCM_UNUSUAL_TOOL_VALUE' . $count . ' = ' .
+                 $tool->{$name} . "\n";
+      $count++;
+    }
+  }
+
+  $return .= "\n";
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_write_makefile_vpath ();
+#
+# DESCRIPTION
+#   This method returns a makefile $string for defining vpath directives.
+# ------------------------------------------------------------------------------
+
+sub _write_makefile_vpath {
+  my $self = shift();
+  my $FMT = 'vpath %%%s $(FCM_%sPATH)';
+  my %SETTING_OF = %{$self->setting('BLD_VPATH')};
+  my %EXT_OF = %{$self->setting('OUTFILE_EXT')};
+  # Note: each setting can be either an empty string or a comma-separated list
+  # of output file extension keys.
+  join(
+    "\n",
+    (
+      map
+      {
+        my $key = $_;
+        my @types = split(qr{$DELIMITER_LIST}msx, $SETTING_OF{$key});
+          @types ? (map {sprintf($FMT, $EXT_OF{$_}, $key)} sort @types)
+        :          sprintf($FMT, q{}, $key)
+        ;
+      }
+      sort keys(%SETTING_OF)
+    ),
+  ) . "\n\n";
+}
+
+# Wraps chdir. Returns the old working directory.
+sub _chdir {
+  my ($self, $path) = @_;
+  if ($self->verbose() >= 3) {
+    printf("cd %s\n", $path);
+  }
+  my $old_cwd = cwd();
+  chdir($path) || croak(sprintf("%s: cannot change directory ($!)\n", $path));
+  $old_cwd;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Build/Fortran.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Build/Fortran.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Build/Fortran.pm	(revision 5095)
@@ -0,0 +1,549 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package FCM1::Build::Fortran;
+
+use Text::Balanced qw{extract_bracketed extract_delimited};
+
+# Actions of this class
+my %ACTION_OF = (extract_interface => \&_extract_interface);
+
+# Regular expressions
+# Matches a variable attribute
+my $RE_ATTR = qr{
+    allocatable|dimension|external|intent|optional|parameter|pointer|save|target
+}imsx;
+# Matches a name
+my $RE_NAME = qr{[A-Za-z]\w*}imsx;
+# Matches a specification type
+my $RE_SPEC = qr{
+    character|complex|double\s*precision|integer|logical|real|type
+}imsx;
+# Matches the identifier of a program unit that does not have arguments
+my $RE_UNIT_BASE = qr{block\s*data|module|program}imsx;
+# Matches the identifier of a program unit that has arguments
+my $RE_UNIT_CALL = qr{function|subroutine}imsx;
+# Matches the identifier of any program unit
+my $RE_UNIT      = qr{$RE_UNIT_BASE|$RE_UNIT_CALL}msx;
+my %RE = (
+    # A comment line
+    COMMENT     => qr{\A\s*(?:!|\z)}msx,
+    # A trailing comment, capture the expression before the comment
+    COMMENT_END => qr{\A([^'"]*?)\s*!.*\z}msx,
+    # A contination marker, capture the expression before the marker
+    CONT        => qr{\A(.*)&\s*\z}msx,
+    # A contination marker at the beginning of a line, capture the marker and
+    # the expression after the marker
+    CONT_LEAD   => qr{\A(\s*&)(.*)\z}msx,
+    # Capture a variable identifier, removing any type component expression
+    NAME_COMP   => qr{\b($RE_NAME)(?:\s*\%\s*$RE_NAME)*\b}msx,
+    # Matches the first identifier in a line
+    NAME_LEAD   => qr{\A\s*$RE_NAME\s*}msx,
+    # Captures a name identifier after a comma, and the expression after
+    NAME_LIST   => qr{\A(?:.*?)\s*,\s*($RE_NAME)\b(.*)\z}msx,
+    # Captures the next quote character
+    QUOTE       => qr{\A[^'"]*(['"])}msx,
+    # Matches an attribute declaration
+    TYPE_ATTR   => qr{\A\s*($RE_ATTR)\b}msx,
+    # Matches a type declaration
+    TYPE_SPEC   => qr{\A\s*($RE_SPEC)\b}msx,
+    # Captures the expression after one or more program unit attributes
+    UNIT_ATTR   => qr{\A\s*(?:(?:elemental|recursive|pure)\s+)+(.*)\z}imsx,
+    # Captures the identifier and the symbol of a program unit with no arguments
+    UNIT_BASE   => qr{\A\s*($RE_UNIT_BASE)\s+($RE_NAME)\s*\z}imsx,
+    # Captures the identifier and the symbol of a program unit with arguments
+    UNIT_CALL   => qr{\A\s*($RE_UNIT_CALL)\s+($RE_NAME)\b}imsx,
+    # Captures the end of a program unit, its identifier and its symbol
+    UNIT_END    => qr{\A\s*(end)(?:\s+($RE_NAME)(?:\s+($RE_NAME))?)?\s*\z}imsx,
+    # Captures the expression after a program unit type specification
+    UNIT_SPEC   => qr{\A\s*$RE_SPEC\b(.*)\z}imsx,
+);
+
+# Keywords in type declaration statements
+my %TYPE_DECL_KEYWORD_SET = map { ($_, 1) } qw{
+    allocatable
+    dimension
+    in
+    inout
+    intent
+    kind
+    len
+    optional
+    out
+    parameter
+    pointer
+    save
+    target
+};
+
+# Creates and returns an instance of this class.
+sub new {
+    my ($class) = @_;
+    bless(
+        sub {
+            my $key = shift();
+            if (!exists($ACTION_OF{$key})) {
+                return;
+            }
+            $ACTION_OF{$key}->(@_);
+        },
+        $class,
+    );
+}
+
+# Methods.
+for my $key (keys(%ACTION_OF)) {
+    no strict qw{refs};
+    *{$key} = sub { my $self = shift(); $self->($key, @_) };
+}
+
+# Extracts the calling interfaces of top level subroutines and functions from
+# the $handle for reading Fortran sources.
+sub _extract_interface {
+    my ($handle) = @_;
+    map { _present_line($_) } @{_reduce_to_interface(_load($handle))};
+}
+
+# Reads $handle for the next Fortran statement, handling continuations.
+sub _load {
+    my ($handle) = @_;
+    my $ctx = {signature_token_set_of => {}, statements => []};
+    my $state = {
+        in_contains  => undef, # in a "contains" section of a program unit
+        in_interface => undef, # in an "interface" block
+        in_quote     => undef, # in a multi-line quote
+        stack        => [],    # program unit stack
+    };
+    my $NEW_STATEMENT = sub {
+        {   name        => q{}, # statement name, e.g. function, integer, ...
+            lines       => [],  # original lines in the statement
+            line_number => 0,   # line number (start) in the original source
+            symbol      => q{}, # name of a program unit (signature, end)
+            type        => q{}, # e.g. signature, use, type, attr, end
+            value       => q{}, # the actual value of the statement
+        };
+    };
+    my $statement;
+LINE:
+    while (my $line = readline($handle)) {
+        if (!defined($statement)) {
+            $statement = $NEW_STATEMENT->();
+        }
+        my $value = $line;
+        chomp($value);
+        # Pre-processor directives and continuation
+        if (!$statement->{line_number} && index($value, '#') == 0) {
+            $statement->{line_number} = $.;
+            $statement->{name}        = 'cpp';
+        }
+        if ($statement->{name} eq 'cpp') {
+            push(@{$statement->{lines}}, $line);
+            $statement->{value} .= $value;
+            if (rindex($value, '\\') != length($value) - 1) {
+                $statement = undef;
+            }
+            next LINE;
+        }
+        # Normal Fortran
+        if ($value =~ $RE{COMMENT}) {
+            next LINE;
+        }
+        if (!$statement->{line_number}) {
+            $statement->{line_number} = $.;
+        }
+        my ($cont_head, $cont_tail);
+        if ($statement->{line_number} != $.) { # is a continuation
+            ($cont_head, $cont_tail) = $value =~ $RE{CONT_LEAD};
+            if ($cont_head) {
+                $value = $cont_tail;
+            }
+        }
+        # Correctly handle ! and & in quotes
+        my ($head, $tail) = (q{}, $value);
+        if ($state->{in_quote} && index($value, $state->{in_quote}) >= 0) {
+            my $index = index($value, $state->{in_quote});
+            $head = substr($value, 0, $index + 1);
+            $tail
+                = length($value) > $index + 1
+                ? substr($value, $index + 2)
+                : q{};
+            $state->{in_quote} = undef;
+        }
+        if (!$state->{in_quote}) {
+            while ($tail) {
+                if (index($tail, q{!}) >= 0) {
+                    if (!($tail =~ s/$RE{COMMENT_END}/$1/)) {
+                        ($head, $tail, $state->{in_quote})
+                            = _load_extract_quote($head, $tail);
+                    }
+                }
+                else {
+                    while (index($tail, q{'}) > 0
+                        || index($tail, q{"}) > 0)
+                    {
+                        ($head, $tail, $state->{in_quote})
+                            = _load_extract_quote($head, $tail);
+                    }
+                    $head .= $tail;
+                    $tail = q{};
+                }
+            }
+        }
+        $cont_head ||= q{};
+        push(@{$statement->{lines}}, $cont_head . $head . $tail . "\n");
+        $statement->{value} .= $head . $tail;
+        # Process a statement only if it is marked with a continuation
+        if (!($statement->{value} =~ s/$RE{CONT}/$1/)) {
+            $statement->{value} =~ s{\s+\z}{}msx;
+            if (_process($statement, $ctx, $state)) {
+                push(@{$ctx->{statements}}, $statement);
+            }
+            $statement = undef;
+        }
+    }
+    return $ctx;
+}
+
+# Helper, removes a quoted string from $tail.
+sub _load_extract_quote {
+    my ($head, $tail) = @_;
+    my ($extracted, $remainder, $prefix)
+        = extract_delimited($tail, q{'"}, qr{[^'"]*}msx, q{});
+    if ($extracted) {
+        return ($head . $prefix . $extracted, $remainder);
+    }
+    else {
+        my ($quote) = $tail =~ $RE{QUOTE};
+        return ($head . $tail, q{}, $quote);
+    }
+}
+
+# Study statements and put attributes into array $statements
+sub _process {
+    my ($statement, $ctx, $state) = @_;
+    my $name;
+
+    # End Interface
+    if ($state->{in_interface}) {
+        if ($statement->{value} =~ qr{\A\s*end\s*interface\b}imsx) {
+            $state->{in_interface} = 0;
+        }
+        return;
+    }
+
+    # End Program Unit
+    if (@{$state->{stack}} && $statement->{value} =~ qr{\A\s*end\b}imsx) {
+        my ($end, $type, $symbol) = lc($statement->{value}) =~ $RE{UNIT_END};
+        if (!$end) {
+            return;
+        }
+        my ($top_type, $top_symbol) = @{$state->{stack}->[-1]};
+        if (!$type
+            || $top_type eq $type && (!$symbol || $top_symbol eq $symbol))
+        {
+            pop(@{$state->{stack}});
+            if ($state->{in_contains} && !@{$state->{stack}}) {
+                $state->{in_contains} = 0;
+            }
+            if (!$state->{in_contains}) {
+                $statement->{name}   = $top_type;
+                $statement->{symbol} = $top_symbol;
+                $statement->{type}   = 'end';
+                return $statement;
+            }
+        }
+        return;
+    }
+
+    # Interface/Contains
+    ($name) = $statement->{value} =~ qr{\A\s*(contains|interface)\b}imsx;
+    if ($name) {
+        $state->{'in_' . lc($name)} = 1;
+        return;
+    }
+
+    # Program Unit
+    my ($type, $symbol, @tokens) = _process_prog_unit($statement->{value});
+    if ($type) {
+        push(@{$state->{stack}}, [$type, $symbol]);
+        if ($state->{in_contains}) {
+            return;
+        }
+        $statement->{name}   = lc($type);
+        $statement->{type}   = 'signature';
+        $statement->{symbol} = lc($symbol);
+        $ctx->{signature_token_set_of}{$symbol}
+            = {map { (lc($_) => 1) } @tokens};
+        return $statement;
+    }
+    if ($state->{in_contains}) {
+        return;
+    }
+
+    # Use
+    if ($statement->{value} =~ qr{\A\s*(use)\b}imsx) {
+        $statement->{name} = 'use';
+        $statement->{type} = 'use';
+        return $statement;
+    }
+
+    # Type Declarations
+    ($name) = $statement->{value} =~ $RE{TYPE_SPEC};
+    if ($name) {
+        $name =~ s{\s}{}gmsx;
+        $statement->{name} = lc($name);
+        $statement->{type} = 'type';
+        return $statement;
+    }
+
+    # Attribute Statements
+    ($name) = $statement->{value} =~ $RE{TYPE_ATTR};
+    if ($name) {
+        $statement->{name} = $name;
+        $statement->{type} = 'attr';
+        return $statement;
+    }
+}
+
+# Parse a statement for program unit header. Returns a list containing the type,
+# the symbol and the signature tokens of the program unit.
+sub _process_prog_unit {
+    my ($string) = @_;
+    my ($type, $symbol, @args) = (q{}, q{});
+    # Is it a blockdata, module or program?
+    ($type, $symbol) = $string =~ $RE{UNIT_BASE};
+    if ($type) {
+        $type = lc($type);
+        $type =~ s{\s*}{}gmsx;
+        return ($type, $symbol);
+    }
+    # Remove the attribute and type declaration of a procedure
+    $string =~ s/$RE{UNIT_ATTR}/$1/;
+    my ($match) = $string =~ $RE{UNIT_SPEC};
+    if ($match) {
+        $string = $match;
+        extract_bracketed($string);
+    }
+    # Is it a function or subroutine?
+    ($type, $symbol) = lc($string) =~ $RE{UNIT_CALL};
+    if (!$type) {
+        return;
+    }
+    my $extracted = extract_bracketed($string, q{()}, qr{[^(]*}msx);
+
+    # Get signature tokens from SUBROUTINE/FUNCTION
+    if ($extracted) {
+        $extracted =~ s{\s}{}gmsx;
+        @args = split(q{,}, substr($extracted, 1, length($extracted) - 2));
+        if ($type eq 'function') {
+            my $result = extract_bracketed($string, q{()}, qr{[^(]*}msx);
+            if ($result) {
+                $result =~ s{\A\(\s*(.*?)\s*\)\z}{$1}msx; # remove braces
+                push(@args, $result);
+            }
+            else {
+                push(@args, $symbol);
+            }
+        }
+    }
+    return (lc($type), lc($symbol), map { lc($_) } @args);
+}
+
+# Reduces the list of statements to contain only the interface block.
+sub _reduce_to_interface {
+    my ($ctx) = @_;
+    my (%token_set, @interface_statements);
+STATEMENT:
+    for my $statement (reverse(@{$ctx->{statements}})) {
+        if ($statement->{type} eq 'end'
+            && grep { $_ eq $statement->{name} } qw{subroutine function})
+        {
+            push(@interface_statements, $statement);
+            %token_set
+                = %{$ctx->{signature_token_set_of}{$statement->{symbol}}};
+            next STATEMENT;
+        }
+        if ($statement->{type} eq 'signature'
+            && grep { $_ eq $statement->{name} } qw{subroutine function})
+        {
+            push(@interface_statements, $statement);
+            %token_set = ();
+            next STATEMENT;
+        }
+        if ($statement->{type} eq 'use') {
+            my ($head, $tail)
+                = split(qr{\s*:\s*}msx, lc($statement->{value}), 2);
+            if ($tail) {
+                my @imports = map { [split(qr{\s*=>\s*}msx, $_, 2)] }
+                    split(qr{\s*,\s*}msx, $tail);
+                my @useful_imports
+                    = grep { exists($token_set{$_->[0]}) } @imports;
+                if (!@useful_imports) {
+                    next STATEMENT;
+                }
+                if (@imports != @useful_imports) {
+                    my @token_strings
+                        = map { $_->[0] . ($_->[1] ? ' => ' . $_->[1] : q{}) }
+                        @useful_imports;
+                    my ($last, @rest) = reverse(@token_strings);
+                    my @token_lines
+                        = (reverse(map { $_ . q{,&} } @rest), $last);
+                    push(
+                        @interface_statements,
+                        {   lines => [
+                                sprintf("%s:&\n", $head),
+                                (map { sprintf(" & %s\n", $_) } @token_lines),
+                            ]
+                        },
+                    );
+                    next STATEMENT;
+                }
+            }
+            push(@interface_statements, $statement);
+            next STATEMENT;
+        }
+        if ($statement->{type} eq 'attr') {
+            my ($spec, @tokens) = ($statement->{value} =~ /$RE{NAME_COMP}/g);
+            if (grep { exists($token_set{$_}) } @tokens) {
+                for my $token (@tokens) {
+                    $token_set{$token} = 1;
+                }
+                push(@interface_statements, $statement);
+                next STATEMENT;
+            }
+        }
+        if ($statement->{type} eq 'type') {
+            my ($variable_string, $spec_string)
+                = reverse(split('::', lc($statement->{value}), 2));
+            if ($spec_string) {
+                $spec_string =~ s{$RE{NAME_LEAD}}{}msx;
+            }
+            else {
+                # The first expression in the statement is the type + attrib
+                $variable_string =~ s{$RE{NAME_LEAD}}{}msx;
+                $spec_string = extract_bracketed($variable_string, '()',
+                    qr{[\s\*]*}msx);
+            }
+            # Useful tokens are those that comes after a comma
+            my $tail = q{,} . lc($variable_string);
+            my @tokens;
+            while ($tail) {
+                if ($tail =~ qr{\A\s*['"]}msx) {
+                    extract_delimited($tail, q{'"}, qr{\A[^'"]*}msx, q{});
+                }
+                elsif ($tail =~ qr{\A\s*\(}msx) {
+                    extract_bracketed($tail, '()', qr{\A[^(]*}msx);
+                }
+                else {
+                    my $token;
+                    ($token, $tail) = $tail =~ $RE{NAME_LIST};
+                    if ($token && $token_set{$token}) {
+                        @tokens = ($variable_string =~ /$RE{NAME_COMP}/g);
+                        $tail = q{};
+                    }
+                }
+            }
+            if (@tokens && $spec_string) {
+                my @spec_tokens = (lc($spec_string) =~ /$RE{NAME_COMP}/g);
+                push(
+                    @tokens,
+                    (   grep { !exists($TYPE_DECL_KEYWORD_SET{$_}) }
+                            @spec_tokens
+                    ),
+                );
+            }
+            if (grep { exists($token_set{$_}) } @tokens) {
+                for my $token (@tokens) {
+                    $token_set{$token} = 1;
+                }
+                push(@interface_statements, $statement);
+                next STATEMENT;
+            }
+        }
+    }
+    if (!@interface_statements) {
+        return [];
+    }
+    [   {lines => ["interface\n"]},
+        reverse(@interface_statements),
+        {lines => ["end interface\n"]},
+    ];
+}
+
+# Processes and returns the line of the statement.
+sub _present_line {
+    my ($statement) = @_;
+    map {
+        s{\s+}{ }gmsx;      # collapse multiple spaces
+        s{\s+\z}{\n}msx;    # remove trailing spaces
+        $_;
+    } @{$statement->{lines}};
+}
+
+# ------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM1::Build::Fortran
+
+=head1 SYNOPSIS
+
+    use FCM1::Build::Fortran;
+    my $fortran_util = FCM1::Build::Fortran->new();
+    open(my($handle), '<', $path_to_a_fortran_source_file);
+    print($fortran_util->extract_interface($handle)); # prints interface
+    close($handle);
+
+=head1 DESCRIPTION
+
+A class to analyse Fortran source. Currently, it has a single method to extract
+the calling interfaces of top level subroutines and functions in a Fortran
+source.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->new()
+
+Creates and returns an instance of this class.
+
+=item $instance->extract_interface($handle)
+
+Extracts the calling interfaces of top level subroutines and functions in a
+Fortran source that can be read from $handle. Returns an interface block as a
+list of lines.
+
+=back
+
+=head1 ACKNOWLEDGEMENT
+
+This module is inspired by the logic developed by the European Centre
+for Medium-Range Weather Forecasts (ECMWF).
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/BuildSrc.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/BuildSrc.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/BuildSrc.pm	(revision 5095)
@@ -0,0 +1,1509 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::BuildSrc
+#
+# DESCRIPTION
+#   This is a class to group functionalities of source in a build.
+#
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM1::BuildSrc;
+use base qw{FCM1::Base};
+
+use Carp qw{croak};
+use Cwd qw{cwd};
+use FCM1::Build::Fortran;
+use FCM1::CfgFile;
+use FCM1::CfgLine;
+use FCM1::Config;
+use FCM1::Timer qw{timestamp_command};
+use FCM1::Util qw{find_file_in_path run_command};
+use File::Basename qw{basename dirname};
+use File::Spec;
+
+# List of scalar property methods for this class
+my @scalar_properties = (
+  'children',   # list of children packages
+  'is_updated', # is this source (or its associated settings) updated?
+  'mtime',      # modification time of src
+  'ppmtime',    # modification time of ppsrc
+  'ppsrc',      # full path of the pre-processed source
+  'pkgname',    # package name of the source
+  'progname',   # program unit name in the source
+  'src',        # full path of the source
+  'type',       # type of the source
+);
+
+# List of hash property methods for this class
+my @hash_properties = (
+  'dep',   # dependencies
+  'ppdep', # pre-process dependencies
+  'rules', # make rules
+);
+
+# Error message formats
+my %ERR_MESS_OF = (
+  CHDIR       => '%s: cannot change directory (%s), abort',
+  OPEN        => '%s: cannot open (%s), abort',
+  CLOSE_PIPE  => '%s: failed (%d), abort',
+);
+
+# Event message formats and levels
+my %EVENT_SETTING_OF = (
+  CHDIR            => ['%s: change directory'                   , 2],
+  F_INTERFACE_NONE => ['%s: Fortran interface generation is off', 3],
+  GET_DEPENDENCY   => ['%s: %d line(s), %d auto dependency(ies)', 3],
+);
+
+my %RE_OF = (
+  F_PREFIX => qr{
+    (?:
+      (?:ELEMENTAL|PURE(?:\s+RECURSIVE)?|RECURSIVE(?:\s+PURE)?)
+      \s+
+    )?
+  }imsx,
+  F_SPEC => qr{
+    (?:
+      (?:CHARACTER|COMPLEX|DOUBLE\s*PRECISION|INTEGER|LOGICAL|REAL|TYPE)
+      (?: \s* \( .+ \) | \s* \* \d+ \s*)??
+      \s+
+    )?
+  }imsx,
+);
+
+{
+  # Returns a singleton instance of FCM1::Build::Fortran.
+  my $FORTRAN_UTIL;
+  sub _get_fortran_util {
+    $FORTRAN_UTIL ||= FCM1::Build::Fortran->new();
+    return $FORTRAN_UTIL;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::BuildSrc->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::BuildSrc class. See
+#   above for allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my ($class, %args) = @_;
+  my $self = bless(FCM1::Base->new(%args), $class);
+  for my $key (@scalar_properties, @hash_properties) {
+    $self->{$key}
+      = exists($args{uc($key)}) ? $args{uc($key)}
+      :                           undef
+      ;
+  }
+  $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+
+      if ($name eq 'ppsrc') {
+        $self->ppmtime (undef);
+
+      } elsif ($name eq 'src') {
+        $self->mtime (undef);
+      }
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'children') {
+        # Reference to an empty array
+        $self->{$name} = [];
+        
+      } elsif ($name =~ /^(?:is_cur|pkgname|ppsrc|src)$/) {
+        # Empty string
+        $self->{$name} = '';
+        
+      } elsif ($name eq 'mtime') {
+        # Modification time
+        $self->{$name} = (stat $self->src)[9] if $self->src;
+        
+      } elsif ($name eq 'ppmtime') {
+        # Modification time
+        $self->{$name} = (stat $self->ppsrc)[9] if $self->ppsrc;
+        
+      } elsif ($name eq 'type') {
+        # Attempt to get the type if src is set
+        $self->{$name} = $self->get_type if $self->src;
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %hash = %{ $obj->X () };
+#   $obj->X (\%hash);
+#
+#   $value = $obj->X ($index);
+#   $obj->X ($index, $value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @hash_properties.
+#
+#   If no argument is set, this method returns a hash containing a list of
+#   objects. If an argument is set and it is a reference to a hash, the objects
+#   are replaced by the specified hash.
+#
+#   If a scalar argument is specified, this method returns a reference to an
+#   object, if the indexed object exists or undef if the indexed object does
+#   not exist. If a second argument is set, the $index element of the hash will
+#   be set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+for my $name (@hash_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my ($self, $arg1, $arg2) = @_;
+
+    # Ensure property is defined as a reference to a hash
+    if (not defined $self->{$name}) {
+      if ($name eq 'rules') {
+        $self->{$name} = $self->get_rules;
+
+      } else {
+        $self->{$name} = {};
+      }
+    }
+
+    # Argument 1 can be a reference to a hash or a scalar index
+    my ($index, %hash);
+
+    if (defined $arg1) {
+      if (ref ($arg1) eq 'HASH') {
+        %hash = %$arg1;
+
+      } else {
+        $index = $arg1;
+      }
+    }
+
+    if (defined $index) {
+      # A scalar index is defined, set and/or return the value of an element
+      $self->{$name}{$index} = $arg2 if defined $arg2;
+
+      return (
+        exists $self->{$name}{$index} ? $self->{$name}{$index} : undef
+      );
+
+    } else {
+      # A scalar index is not defined, set and/or return the hash
+      $self->{$name} = \%hash if defined $arg1;
+      return $self->{$name};
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   This method returns/sets property X, all derived from src, where X is:
+#     base  - (read-only) basename of src
+#     dir   - (read-only) dirname of src
+#     ext   - (read-only) file extension of src
+#     root  - (read-only) basename of src without the file extension
+# ------------------------------------------------------------------------------
+
+sub base {
+  return &basename ($_[0]->src);
+}
+
+# ------------------------------------------------------------------------------
+
+sub dir {
+  return &dirname ($_[0]->src);
+}
+
+# ------------------------------------------------------------------------------
+
+sub ext {
+  return substr $_[0]->base, length ($_[0]->root);
+}
+
+# ------------------------------------------------------------------------------
+
+sub root {
+  (my $root = $_[0]->base) =~ s/\.\w+$//;
+  return $root;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   This method returns/sets property X, all derived from ppsrc, where X is:
+#     ppbase  - (read-only) basename of ppsrc
+#     ppdir   - (read-only) dirname of ppsrc
+#     ppext   - (read-only) file extension of ppsrc
+#     pproot  - (read-only) basename of ppsrc without the file extension
+# ------------------------------------------------------------------------------
+
+sub ppbase {
+  return &basename ($_[0]->ppsrc);
+}
+
+# ------------------------------------------------------------------------------
+
+sub ppdir {
+  return &dirname ($_[0]->ppsrc);
+}
+
+# ------------------------------------------------------------------------------
+
+sub ppext {
+  return substr $_[0]->ppbase, length ($_[0]->pproot);
+}
+
+# ------------------------------------------------------------------------------
+
+sub pproot {
+  (my $root = $_[0]->ppbase) =~ s/\.\w+$//;
+  return $root;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#
+# DESCRIPTION
+#   This method returns/sets property X, derived from src or ppsrc, where X is:
+#     curbase  - (read-only) basename of cursrc
+#     curdir   - (read-only) dirname of cursrc
+#     curext   - (read-only) file extension of cursrc
+#     curmtime - (read-only) modification time of cursrc
+#     curroot  - (read-only) basename of cursrc without the file extension
+#     cursrc   - ppsrc or src
+# ------------------------------------------------------------------------------
+
+for my $name (qw/base dir ext mtime root src/) {
+  no strict 'refs';
+
+  my $subname = 'cur' . $name;
+
+  *$subname = sub {
+    my $self = shift;
+    my $method = $self->ppsrc ? 'pp' . $name : $name;
+    return $self->$method (@_);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $base = $obj->X ();
+#
+# DESCRIPTION
+#   This method returns a basename X for the source, where X is:
+#     donebase      - "done" file name
+#     etcbase       - target for copying data files
+#     exebase       - executable name for source containing a main program
+#     interfacebase - Fortran interface file name
+#     libbase       - library file name
+#     objbase       - object name for source containing compilable source
+#   If the source file contains a compilable procedure, this method returns
+#   the name of the object file.
+# ------------------------------------------------------------------------------
+
+sub donebase {
+  my $self   = shift;
+
+  my $return;
+  if ($self->is_type_all ('SOURCE')) {
+    if ($self->objbase and not $self->is_type_all ('PROGRAM')) {
+      $return = ($self->progname ? $self->progname : lc ($self->curroot)) .
+                $self->setting (qw/OUTFILE_EXT DONE/);
+    }
+
+  } elsif ($self->is_type_all ('INCLUDE')) {
+    $return = $self->curbase . $self->setting (qw/OUTFILE_EXT IDONE/);
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+
+sub etcbase {
+  my $self = shift;
+
+  my $return = @{ $self->children }
+               ? $self->pkgname . $self->setting (qw/OUTFILE_EXT ETC/)
+               : undef;
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+
+sub exebase {
+  my $self = shift;
+
+  my $return;
+  if ($self->objbase and $self->is_type_all ('PROGRAM')) {
+    if ($self->setting ('BLD_EXE_NAME', $self->curroot)) {
+      $return = $self->setting ('BLD_EXE_NAME', $self->curroot);
+
+    } else {
+      $return = $self->curroot . $self->setting (qw/OUTFILE_EXT EXE/);
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+
+sub interfacebase {
+  my $self = shift();
+  if (
+        defined($self->get_setting(qw/TOOL GENINTERFACE/))
+    &&  uc($self->get_setting(qw/TOOL GENINTERFACE/)) ne 'NONE'
+    &&  $self->progname()
+    &&  $self->is_type_all(qw/SOURCE/)
+    &&  $self->is_type_any(qw/FORTRAN9X FPP9X/)
+    &&  !$self->is_type_any(qw/PROGRAM MODULE BLOCKDATA/)
+  ) {
+    my $flag = lc($self->get_setting(qw/TOOL INTERFACE/));
+    my $ext  = $self->setting(qw/OUTFILE_EXT INTERFACE/);
+
+    return (($flag eq 'program' ? $self->progname() : $self->curroot()) . $ext);
+  }
+  return;
+}
+
+# ------------------------------------------------------------------------------
+
+sub objbase {
+  my $self = shift;
+
+  my $return;
+
+  if ($self->is_type_all ('SOURCE')) {
+    my $ext = $self->setting (qw/OUTFILE_EXT OBJ/);
+
+    if ($self->is_type_any (qw/FORTRAN FPP/)) {
+      $return = lc ($self->progname) . $ext if $self->progname;
+
+    } else {
+      $return = lc ($self->curroot) . $ext;
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->flagsbase ($flag, [$index,]);
+#
+# DESCRIPTION
+#   Returns the base name of the flags file for the current package namespace
+#   for a given $flag. The returned base name should look like
+#   "LABEL___PACKAGE__NAME__SPACE.flags", where "LABEL" is normally the $flag,
+#   and "PACKAGE__NAME__SPACE" is the current package namespace without the file
+#   extension. If $flag is FLAGS or PPKEYS and $self->lang() is defined, it
+#   will attempt to determine the correct label for the language. E.g. If
+#   $self->lang() is 'C', the label will be "CFLAGS". If $index is set, returns
+#   the base name of the flags file for the $index'th element in package name
+#   space (as described in "pkgnames" method) instead of the current package
+#   name space.
+# ------------------------------------------------------------------------------
+
+sub flagsbase {
+  my ($self, $flag, $index) = @_;
+  my $name = $index ? $self->pkgnames()->[$index] : $self->pkgname();
+  my @names = split('__', $name);
+  if (@names && $self->src() && $name eq $self->pkgname()) {
+    $names[-1] =~ s{\.\w+ \z}{}msx;
+  }
+  my $label = $flag;
+  if ($self->lang() && ($flag eq 'FLAGS' || $flag eq 'PPKEYS')) {
+    if (!exists($self->setting('TOOL_SRC')->{$self->lang()}{$flag})) {
+      return;
+    }
+    $label = $self->setting('TOOL_SRC')->{$self->lang()}{$flag};
+  }
+  join('__', $label, @names) . $self->setting(qw/OUTFILE_EXT FLAGS/);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->libbase ([$prefix], [$suffix]);
+#
+# DESCRIPTION
+#   This method returns the property libbase (derived from pkgname) the base
+#   name of the library archive. $prefix and $suffix defaults to 'lib' and '.a'
+#   respectively.
+# ------------------------------------------------------------------------------
+
+sub libbase {
+  my ($self, $prefix, $suffix) = @_;
+  $prefix ||= 'lib';
+  $suffix ||= $self->setting(qw/OUTFILE_EXT LIB/);
+  if ($self->src()) { # applies to directories only
+    return;
+  }
+  my $name = $self->setting('BLD_LIB', $self->pkgname());
+  if (!defined($name)) {
+    $name = $self->pkgname();
+  }
+  $prefix . $name . $suffix;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->lang ([$setting]);
+#
+# DESCRIPTION
+#   This method returns the property lang (derived from type) the programming
+#   language name if type matches one supported in the TOOL_SRC setting. If
+#   $setting is specified, use $setting instead of TOOL_SRC.
+# ------------------------------------------------------------------------------
+
+sub lang {
+  my ($self, $setting) = @_;
+
+  my @keys = keys %{ $self->setting ($setting ? $setting : 'TOOL_SRC') };
+
+  my $return = undef;
+  for my $key (@keys) {
+    next unless $self->is_type_all ('SOURCE', $key);
+    $return = $key;
+    last;
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->pkgnames;
+#
+# DESCRIPTION
+#   This method returns a list of container packages, derived from pkgname:
+# ------------------------------------------------------------------------------
+
+sub pkgnames {
+  my $self = shift;
+
+  my $return = [];
+  if ($self->pkgname) {
+    my @names = split (/__/, $self->pkgname);
+
+    for my $i (0 .. $#names) {
+      push @$return, join ('__', (@names[0 .. $i]));
+    }
+
+    unshift @$return, '';
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %dep = %{$obj->get_dep()};
+#   %dep = %{$obj->get_dep($flag)};
+#
+# DESCRIPTION
+#   This method scans the current source file for dependencies and returns the
+#   dependency hash (keys = dependencies, values = dependency types). If $flag
+#   is specified, the config setting for $flag is used to determine the types of
+#   types. Otherwise, those specified in 'BLD_TYPE_DEP' is used.
+# ------------------------------------------------------------------------------
+
+sub get_dep {
+  my ($self, $flag) = @_;
+  # Work out list of exclude for this file, using its sub-package name
+  my %EXCLUDE_SET = map {($_, 1)} @{$self->get_setting('BLD_DEP_EXCL')};
+  # Determine what dependencies are supported by this known type
+  my %DEP_TYPE_OF = %{$self->setting($flag ? $flag : 'BLD_TYPE_DEP')};
+  my %PATTERN_OF = %{$self->setting('BLD_DEP_PATTERN')};
+  my @dep_types = ();
+  if (!$self->get_setting('BLD_DEP_N')) {
+    DEP_TYPE:
+    while (my ($key, $dep_type_string) = each(%DEP_TYPE_OF)) {
+      # Check if current file is a type of file requiring dependency scan
+      if (!$self->is_type_all($key)) {
+        next DEP_TYPE;
+      }
+      # Get list of dependency type for this file
+      for my $dep_type (split(/$FCM1::Config::DELIMITER/, $dep_type_string)) {
+        if (exists($PATTERN_OF{$dep_type}) && !exists($EXCLUDE_SET{$dep_type})) {
+          push(@dep_types, $dep_type);
+        }
+      }
+    }
+  }
+
+  # Automatic dependencies
+  my %dep_of;
+  my $can_get_symbol # Also scan for program unit name in Fortran source
+      =  !$flag
+      && $self->is_type_all('SOURCE')
+      && $self->is_type_any(qw/FPP FORTRAN/)
+      ;
+  my $has_read_file;
+  if ($can_get_symbol || @dep_types) {
+    my $handle = _open($self->cursrc());
+    LINE:
+    while (my $line = readline($handle)) {
+      chomp($line);
+      if ($line =~ qr{\A \s* \z}msx) { # empty lines
+        next LINE;
+      }
+      if ($can_get_symbol) {
+        my $symbol = _get_dep_symbol($line);
+        if ($symbol) {
+          $self->progname($symbol);
+          $can_get_symbol = 0;
+          next LINE;
+        }
+      }
+      DEP_TYPE:
+      for my $dep_type (@dep_types) {
+        my ($match) = $line =~ /$PATTERN_OF{$dep_type}/i;
+        if (!$match) {
+          next DEP_TYPE;
+        }
+        # $match may contain multiple items delimited by space
+        for my $item (split(qr{\s+}msx, $match)) {
+          my $key = uc($dep_type . $FCM1::Config::DELIMITER . $item);
+          if (!exists($EXCLUDE_SET{$key})) {
+            $dep_of{$item} = $dep_type;
+          }
+        }
+        next LINE;
+      }
+    }
+    $self->_event('GET_DEPENDENCY', $self->pkgname(), $., scalar(keys(%dep_of)));
+    close($handle);
+    $has_read_file = 1;
+  }
+
+  # Manual dependencies
+  my $manual_deps_ref
+      = $self->setting('BLD_DEP' . ($flag ? '_PP' : ''), $self->pkgname());
+  if (defined($manual_deps_ref)) {
+    for (@{$manual_deps_ref}) {
+      my ($dep_type, $item) = split(/$FCM1::Config::DELIMITER/, $_, 2);
+      $dep_of{$item} = $dep_type;
+    }
+  }
+
+  return ($has_read_file, \%dep_of);
+}
+
+# Returns, if possible, the program unit declared in the $line.
+sub _get_dep_symbol {
+  my $line = shift();
+  for my $pattern (
+    qr{\A \s* $RE_OF{F_PREFIX} SUBROUTINE              \s+ ([A-Za-z]\w*)}imsx,
+    qr{\A \s* MODULE (?!\s+PROCEDURE)                  \s+ ([A-Za-z]\w*)}imsx,
+    qr{\A \s* PROGRAM                                  \s+ ([A-Za-z]\w*)}imsx,
+    qr{\A \s* $RE_OF{F_PREFIX} $RE_OF{F_SPEC} FUNCTION \s+ ([A-Za-z]\w*)}imsx,
+    qr{\A \s* BLOCK\s*DATA                             \s+ ([A-Za-z]\w*)}imsx,
+  ) {
+    my ($match) = $line =~ $pattern;
+    if ($match) {
+      return lc($match);
+    }
+  }
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @out = @{ $obj->get_fortran_interface () };
+#
+# DESCRIPTION
+#   This method invokes the Fortran 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 get_fortran_interface {
+  my $self = shift();
+  my %ACTION_OF = (
+    q{}    => \&_get_fortran_interface_by_internal_code,
+    f90aib => \&_get_fortran_interface_by_f90aib,
+    none   => sub {$self->_event('F_INTERFACE_NONE', $self->root()); []},
+  );
+  my $key = lc($self->get_setting(qw/TOOL GENINTERFACE/));
+  if (!$key || !exists($ACTION_OF{$key})) {
+    $key = q{};
+  }
+  $ACTION_OF{$key}->($self->cursrc());
+}
+
+# Generates Fortran interface block using "f90aib".
+sub _get_fortran_interface_by_f90aib {
+  my $path = shift();
+  my $command = sprintf(q{f90aib <'%s' 2>'%s'}, $path, File::Spec->devnull());
+  my $pipe = _open($command, '-|');
+  my @lines = readline($pipe);
+  close($pipe) || croak($ERR_MESS_OF{CLOSE_PIPE}, $command, $?);
+  \@lines;
+}
+
+# Generates Fortran interface block using internal code.
+sub _get_fortran_interface_by_internal_code {
+  my $path = shift();
+  my $handle = _open($path);
+  my @lines = _get_fortran_util()->extract_interface($handle);
+  close($handle);
+  \@lines;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @out = @{ $obj->get_pre_process () };
+#
+# DESCRIPTION
+#   This method invokes the pre-processor on the source file and returns a
+#   reference to an array containing the lines of the pre-processed source on
+#   success.
+# ------------------------------------------------------------------------------
+
+sub get_pre_process {
+  my $self = shift;
+
+  # Supported source files
+  my $lang = $self->lang ('TOOL_SRC_PP');
+  return unless $lang;
+
+  # List of include directories
+  my @inc = @{ $self->setting (qw/PATH INC/) };
+
+  # Build the pre-processor command according to file type
+  my %tool        = %{ $self->setting ('TOOL') };
+  my %tool_src_pp = %{ $self->setting ('TOOL_SRC_PP', $lang) };
+
+  # The pre-processor command and its options
+  my @command = ($tool{$tool_src_pp{COMMAND}});
+  my @ppflags = split /\s+/, $self->get_setting ('TOOL', $tool_src_pp{FLAGS});
+
+  # List of defined macros, add "-D" in front of each macro
+  my @ppkeys  = split /\s+/, $self->get_setting ('TOOL', $tool_src_pp{PPKEYS});
+  @ppkeys     = map {($tool{$tool_src_pp{DEFINE}} . $_)} @ppkeys;
+
+  # Add "-I" in front of each include directories
+  @inc        = map {($tool{$tool_src_pp{INCLUDE}} . $_)} @inc;
+
+  push @command, (@ppflags, @ppkeys, @inc, $self->base);
+
+  # Change to container directory of source file
+  my $old_cwd = $self->_chdir($self->dir());
+
+  # Execute the command, getting the output lines
+  my $verbose = $self->verbose;
+  my @outlines = &run_command (
+    \@command, METHOD => 'qx', PRINT => $verbose > 1, TIME => $verbose > 2,
+  );
+
+  # Change back to original directory
+  $self->_chdir($old_cwd);
+
+  return \@outlines;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rules = %{ $self->get_rules };
+#
+# DESCRIPTION
+#   This method returns a reference to 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 get_rules {
+  my $self = shift;
+
+  my $rules;
+  my %outfile_ext = %{ $self->setting ('OUTFILE_EXT') };
+
+  if ($self->is_type_all (qw/SOURCE/)) {
+    # Source file
+    # --------------------------------------------------------------------------
+    # Determine whether the language of the source file is supported
+    my %tool_src = %{ $self->setting ('TOOL_SRC') };
+
+    return () unless $self->lang;
+
+    # Compile object
+    # --------------------------------------------------------------------------
+    if ($self->objbase) {
+      # Depends on the source file
+      my @dep = ($self->rule_src);
+
+      # 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;
+        }
+      }
+
+      $rules->{$self->objbase} = {ACTION => 'COMPILE', DEP => \@dep};
+
+      # Touch flags-files
+      # ------------------------------------------------------------------------
+      for my $flag (@flags) {
+        next unless $self->flagsbase ($flag);
+
+        $rules->{$self->flagsbase ($flag)} = {
+          ACTION => 'TOUCH',
+          DEP    => [
+            $self->flagsbase ($tool_src{$self->lang}{$flag}, -2),
+          ],
+          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->setting ('BLD_BLOCKDATA') };
+      my @blkobj    = ();
+
+      if ($self->is_type_any (qw/FPP FORTRAN/) and keys %blockdata) {
+        # List of BLOCKDATA object files
+        if (exists $blockdata{$self->exebase}) {
+          @blkobj = split /\s+/, $blockdata{$self->exebase};
+
+        } elsif (exists $blockdata{''}) {
+          @blkobj = split /\s+/, $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->setting ('BLD_DEP_EXE') };
+      if (keys %exe_dep) {
+        my @exe_deps;
+        if (exists $exe_dep{$self->exebase}) {
+          @exe_deps = split /\s+/, $exe_dep{$self->exebase};
+
+        } elsif (exists $exe_dep{''}) {
+          @exe_deps = $exe_dep{''} ? split (/\s+/, $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->setting ('FCM_PCK_OBJECTS', $name)) {
+              # sub-package name contains unusual characters
+              $var = $self->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};
+        }
+      }
+
+      $rules->{$self->exebase} = {
+        ACTION => 'LOAD', DEP => \@dep, BLOCKDATA => \@blkobj,
+      };
+
+      # Touch Linker flags-file
+      # ------------------------------------------------------------------------
+      for my $flag (qw/LD LDFLAGS/) {
+        $rules->{$self->flagsbase ($flag)} = {
+          ACTION => 'TOUCH',
+          DEP    => [$self->flagsbase ($flag, -2)],
+          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};
+        }
+      }
+
+      $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';
+      }
+
+      $rules->{$self->interfacebase} = {ACTION => '', DEP => \@dep};
+    }
+
+  } elsif ($self->is_type_all ('INCLUDE')) {
+    # Copy include target
+    # --------------------------------------------------------------------------
+    my @dep = ($self->rule_src);
+
+    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;
+      }
+    }
+
+    $rules->{$self->curbase} = {
+      ACTION => 'CP', DEP => \@dep, DEST => '$(FCM_INCDIR)',
+    };
+
+    # Touch IDONE file
+    # --------------------------------------------------------------------------
+    if ($self->donebase) {
+      my @dep = ($self->rule_src);
+
+      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};
+        }
+      }
+
+      $rules->{$self->donebase} = {
+        ACTION => 'TOUCH', DEP => \@dep, DEST => '$(FCM_DONEDIR)',
+      };
+    }
+
+  } elsif ($self->is_type_any (qw/EXE SCRIPT/)) {
+    # Copy executable file
+    # --------------------------------------------------------------------------
+    my @dep = ($self->rule_src);
+
+    # Depends on dummy copy file, if file is an "always build type"
+    push @dep, $self->setting (qw/BLD_CPDUMMY/)
+      if $self->is_type_any (split (
+        /$FCM1::Config::DELIMITER_LIST/, $self->setting ('BLD_TYPE_ALWAYS_BUILD')
+      ));
+
+    # Depends on other executable files
+    for my $name (sort keys %{ $self->dep }) {
+      push @dep, $name if $self->dep ($name) eq 'EXE';
+    }
+
+    $rules->{$self->curbase} = {
+      ACTION => 'CP', DEP => \@dep, DEST => '$(FCM_BINDIR)',
+    };
+
+  } elsif (@{ $self->children }) {
+    # Targets for top level and package flags files and dummy dependencies
+    # --------------------------------------------------------------------------
+    my %tool_src   = %{ $self->setting ('TOOL_SRC') };
+    my %flags_tool = (LD => '', LDFLAGS => '');
+
+    for my $key (keys %tool_src) {
+      $flags_tool{$tool_src{$key}{FLAGS}} = $tool_src{$key}{COMMAND}
+        if exists $tool_src{$key}{FLAGS};
+
+      $flags_tool{$tool_src{$key}{PPKEYS}} = ''
+        if exists $tool_src{$key}{PPKEYS};
+    }
+
+    for my $name (sort keys %flags_tool) {
+      my @dep = $self->pkgname eq '' ? () : $self->flagsbase ($name, -2);
+      push @dep, $self->flagsbase ($flags_tool{$name})
+        if $self->pkgname eq '' and $flags_tool{$name};
+
+      $rules->{$self->flagsbase ($flags_tool{$name})} = {
+        ACTION => 'TOUCH',
+        DEST   => '$(FCM_FLAGSDIR)',
+      } if $self->pkgname eq '' and $flags_tool{$name};
+
+      $rules->{$self->flagsbase ($name)} = {
+        ACTION => 'TOUCH',
+        DEP    => \@dep,
+        DEST   => '$(FCM_FLAGSDIR)',
+      };
+    }
+
+    # Package object and library
+    # --------------------------------------------------------------------------
+    {
+      my @dep;
+      # Add objects from children
+      for my $child (sort {$a->pkgname cmp $b->pkgname} @{ $self->children }) {
+        push @dep, $child->rule_obj_var (1)
+          if $child->libbase and $child->rules ($child->libbase);
+        push @dep, $child->objbase
+          if $child->cursrc and $child->objbase and
+             not $child->is_type_any (qw/PROGRAM BLOCKDATA/);
+      }
+
+      if (@dep) {
+        $rules->{$self->libbase} = {ACTION => 'AR', DEP => \@dep};
+      }
+    }
+
+    # Package data files
+    # --------------------------------------------------------------------------
+    {
+      my @dep;
+      for my $child (@{ $self->children }) {
+        push @dep, $child->rule_src if $child->src and not $child->type;
+      }
+
+      if (@dep) {
+        push @dep, $self->setting (qw/BLD_CPDUMMY/);
+        $rules->{$self->etcbase} = {
+          ACTION => 'CP_DATA', DEP => \@dep, DEST => '$(FCM_ETCDIR)',
+        };
+      }
+    }
+  }
+
+  return $rules;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->get_setting ($setting[, @prefix]);
+#
+# DESCRIPTION
+#   This method gets the correct $setting for the current source by following
+#   its package name. If @prefix is set, get the setting with the given prefix.
+# ------------------------------------------------------------------------------
+
+sub get_setting {
+  my ($self, $setting, @prefix) = @_;
+
+  my $val;
+  for my $name (reverse @{ $self->pkgnames }) {
+    my @names = split /__/, $name;
+    $val = $self->setting ($setting, join ('__', (@prefix, @names)));
+
+    $val = $self->setting ($setting, join ('__', (@prefix, @names)))
+      if (not defined $val) and @names and $names[-1] =~ s/\.[^\.]+$//;
+    last if defined $val;
+  }
+
+  return $val;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $type = $self->get_type();
+#
+# DESCRIPTION
+#   This method determines whether the source is a type known to the
+#   build system. If so, it returns the type flags delimited by "::".
+# ------------------------------------------------------------------------------
+
+sub get_type {
+  my $self = shift();
+  my @IGNORE_LIST
+    = split(/$FCM1::Config::DELIMITER_LIST/, $self->setting('INFILE_IGNORE'));
+  if (grep {$self->curbase() eq $_} @IGNORE_LIST) {
+    return q{};
+  }
+  # User defined
+  my $type = $self->setting('BLD_TYPE', $self->pkgname());
+  # Extension
+  if (!defined($type)) {
+    my $ext = $self->curext() ? substr($self->curext(), 1) : q{};
+    $type = $self->setting('INFILE_EXT', $ext);
+  }
+  # Pattern of name
+  if (!defined($type)) {
+    my %NAME_PATTERN_TO_TYPE_HASH = %{$self->setting('INFILE_PAT')};
+    PATTERN:
+    while (my ($pattern, $value) = each(%NAME_PATTERN_TO_TYPE_HASH)) {
+      if ($self->curbase() =~ $pattern) {
+        $type = $value;
+        last PATTERN;
+      }
+    }
+  }
+  # Pattern of #! line
+  if (!defined($type) && -s $self->cursrc() && -T _) {
+    my $handle = _open($self->cursrc());
+    my $line = readline($handle);
+    close($handle);
+    my %SHEBANG_PATTERN_TO_TYPE_HASH = %{$self->setting('INFILE_TXT')};
+    PATTERN:
+    while (my ($pattern, $value) = each(%SHEBANG_PATTERN_TO_TYPE_HASH)) {
+      if ($line =~ qr{^\#!.*$pattern}msx) {
+        $type = $value;
+        last PATTERN;
+      }
+    }
+  }
+  if (!$type) {
+    return $type;
+  }
+  # Extra type information for selected file types
+  my %EXTRA_FOR = (
+    qr{\b (?:FORTRAN|FPP) \b}msx => \&_get_type_extra_for_fortran,
+    qr{\b C \b}msx               => \&_get_type_extra_for_c,
+  );
+  EXTRA:
+  while (my ($key, $code_ref) = each(%EXTRA_FOR)) {
+    if ($type =~ $key) {
+      my $handle = _open($self->cursrc());
+      LINE:
+      while (my $line = readline($handle)) {
+        my $extra = $code_ref->($line);
+        if ($extra) {
+          $type .= $FCM1::Config::DELIMITER . $extra;
+          last LINE;
+        }
+      }
+      close($handle);
+      last EXTRA;
+    }
+  }
+  return $type;
+}
+
+sub _get_type_extra_for_fortran {
+  my ($match) = $_[0] =~ qr{\A \s* (PROGRAM|MODULE|BLOCK\s*DATA) \b}imsx;
+  if (!$match) {
+    return;
+  }
+  $match =~ s{\s}{}g;
+  uc($match)
+}
+
+sub _get_type_extra_for_c {
+  ($_[0] =~ qr{int\s+main\s*\(}msx) ? 'PROGRAM' : undef;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $obj->is_in_package ($name);
+#
+# DESCRIPTION
+#   This method returns true if current package is in the package $name.
+# ------------------------------------------------------------------------------
+
+sub is_in_package {
+  my ($self, $name) = @_;
+  
+  my $return = 0;
+  for (@{ $self->pkgnames }) {
+    next unless /^$name(?:\.\w+)?$/;
+    $return = 1;
+    last;
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $obj->is_type_all ($arg, ...);
+#   $flag = $obj->is_type_any ($arg, ...);
+#
+# DESCRIPTION
+#   This method returns a flag for the following:
+#     is_type_all - does type match all of the arguments?
+#     is_type_any - does type match any of the arguments?
+# ------------------------------------------------------------------------------
+
+for my $name ('all', 'any') {
+  no strict 'refs';
+
+  my $subname = 'is_type_' . $name;
+
+  *$subname = sub {
+    my ($self, @intypes) = @_;
+
+    my $rc = 0;
+    if ($self->type) {
+      my %types = map {($_, 1)} split /$FCM1::Config::DELIMITER/, $self->type;
+
+      for my $intype (@intypes) {
+        $rc = exists $types{$intype};
+        last if ($name eq 'all' and not $rc) or ($name eq 'any' and $rc);
+      }
+    }
+
+    return $rc;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $obj->rule_obj_var ([$read]);
+#
+# DESCRIPTION
+#   This method returns a string containing the make rule object variable for
+#   the current package. If $read is set, return $($string)
+# ------------------------------------------------------------------------------
+
+sub rule_obj_var {
+  my ($self, $read) = @_;
+
+  my $return;
+  if ($self->setting ('FCM_PCK_OBJECTS', $self->pkgname)) {
+    # Package name registered in unusual list
+    $return = $self->setting ('FCM_PCK_OBJECTS', $self->pkgname);
+
+  } else {
+    # Package name not registered in unusual list
+    $return = $self->pkgname
+              ? join ('__', ('OBJECTS', $self->pkgname)) : 'OBJECTS';
+  }
+
+  $return = $read ? '$(' . $return . ')' : $return;
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $obj->rule_src ();
+#
+# DESCRIPTION
+#   This method returns a string containing the location of the source file
+#   relative to the build root. This string will be suitable for use in a
+#   "Make" rule file for FCM.
+# ------------------------------------------------------------------------------
+
+sub rule_src {
+  my $self = shift;
+
+  my $return = $self->cursrc;
+  LABEL: for my $name (qw/SRC PPSRC/) {
+    for my $i (0 .. @{ $self->setting ('PATH', $name) } - 1) {
+      my $dir = $self->setting ('PATH', $name)->[$i];
+      next unless index ($self->cursrc, $dir) == 0;
+
+      $return = File::Spec->catfile (
+        '$(FCM_' . $name . 'DIR' . ($i ? $i : '') . ')',
+        File::Spec->abs2rel ($self->cursrc, $dir),
+      );
+      last LABEL;
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->write_lib_dep_excl ();
+#
+# DESCRIPTION
+#   This method writes a set of exclude dependency configurations for the
+#   library of this package.
+# ------------------------------------------------------------------------------
+
+sub write_lib_dep_excl {
+  my $self = shift();
+  if (!find_file_in_path($self->libbase(), $self->setting(qw/PATH LIB/))) {
+    return 0;
+  }
+
+  my $ETC_DIR = $self->setting(qw/PATH ETC/)->[0];
+  my $CFG_EXT = $self->setting(qw/OUTFILE_EXT CFG/);
+  my $LABEL_OF_EXCL_DEP = $self->cfglabel('BLD_DEP_EXCL');
+  my @SETTINGS = (
+       #dependency   #source file type list       #dependency name function
+       ['H'        , [qw{INCLUDE CPP          }], sub {$_[0]->base()}         ],
+       ['INTERFACE', [qw{INCLUDE INTERFACE    }], sub {$_[0]->base()}         ],
+       ['INC'      , [qw{INCLUDE              }], sub {$_[0]->base()}         ],
+       ['USE'      , [qw{SOURCE FORTRAN MODULE}], sub {$_[0]->root()}         ],
+       ['INTERFACE', [qw{SOURCE FORTRAN       }], sub {$_[0]->interfacebase()}],
+       ['OBJ'      , [qw{SOURCE               }], sub {$_[0]->root()}         ],
+  );
+
+  my $cfg = FCM1::CfgFile->new();
+  my @stack = ($self);
+  NODE:
+  while (my $node = pop(@stack)) {
+    # Is a directory
+    if (@{$node->children()}) {
+      push(@stack, reverse(@{$node->children()}));
+      next NODE;
+    }
+    # Is a typed file
+    if (
+          $node->cursrc()
+      &&  $node->type()
+      &&  !$node->is_type_any(qw{PROGRAM BLOCKDATA})
+    ) {
+      for (@SETTINGS) {
+        my ($key, $type_list_ref, $name_func_ref) = @{$_};
+        my $name = $name_func_ref->($node);
+        if ($name && $node->is_type_all(@{$type_list_ref})) {
+          push(
+            @{$cfg->lines()},
+            FCM1::CfgLine->new(
+              label => $LABEL_OF_EXCL_DEP,
+              value => $key . $FCM1::Config::DELIMITER . $name,
+            ),
+          );
+          next NODE;
+        }
+      }
+    }
+  }
+
+  # Write to configuration file
+  $cfg->print_cfg(
+    File::Spec->catfile($ETC_DIR, $self->libbase('lib', $CFG_EXT)),
+  );
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $obj->write_rules ();
+#
+# DESCRIPTION
+#   This method returns a string containing the "Make" rules for building the
+#   source file.
+# ------------------------------------------------------------------------------
+
+sub write_rules {
+  my $self  = shift;
+  my $mk    = '';
+
+  for my $target (sort keys %{ $self->rules }) {
+    my $rule = $self->rules ($target);
+    next unless defined ($rule->{ACTION});
+
+    if ($rule->{ACTION} eq 'AR') {
+      my $var = $self->rule_obj_var;
+      $mk .= ($var eq 'OBJECTS' ? 'export ' : '') . $var . ' =';
+      $mk .= ' ' . join (' ', @{ $rule->{DEP} });
+      $mk .= "\n\n";
+    }
+
+    $mk .= $target . ':';
+    
+    if ($rule->{ACTION} eq 'AR') {
+      $mk .= ' ' . $self->rule_obj_var (1);
+
+    } else {
+      for my $dep (@{ $rule->{DEP} }) {
+        $mk .= ' ' . $dep;
+      }
+    }
+
+    $mk .= "\n";
+
+    if (exists $rule->{ACTION}) {
+      if ($rule->{ACTION} eq 'AR') {
+        $mk .= "\t" . 'fcm_internal archive $@ $^' . "\n";
+
+      } elsif ($rule->{ACTION} eq 'CP') {
+        $mk .= "\t" . 'cp $< ' . $rule->{DEST} . "\n";
+        $mk .= "\t" . 'chmod u+w ' .
+               File::Spec->catfile ($rule->{DEST}, '$@') . "\n";
+
+      } elsif ($rule->{ACTION} eq 'CP_DATA') {
+        $mk .= "\t" . 'cp $^ ' . $rule->{DEST} . "\n";
+        $mk .= "\t" . 'touch ' .
+               File::Spec->catfile ($rule->{DEST}, '$@') . "\n";
+
+      } elsif ($rule->{ACTION} eq 'COMPILE') {
+        if ($self->lang) {
+          $mk .= "\t" . 'fcm_internal compile:' . substr ($self->lang, 0, 1) .
+                 ' ' . $self->pkgnames->[-2] . ' $< $@';
+          $mk .= ' 1' if ($self->flagsbase ('PPKEYS') and not $self->ppsrc);
+          $mk .= "\n";
+        }
+
+      } elsif ($rule->{ACTION} eq 'LOAD') {
+        if ($self->lang) {
+          $mk .= "\t" . 'fcm_internal load:' . substr ($self->lang, 0, 1) .
+                 ' ' . $self->pkgnames->[-2] . ' $< $@';
+          $mk .= ' ' . join (' ', @{ $rule->{BLOCKDATA} })
+            if @{ $rule->{BLOCKDATA} };
+          $mk .= "\n";
+        }
+
+      } elsif ($rule->{ACTION} eq 'TOUCH') {
+        $mk .= "\t" . 'touch ' .
+               File::Spec->catfile ($rule->{DEST}, '$@') . "\n";
+      }
+    }
+
+    $mk .= "\n";
+  }
+
+  return $mk;
+}
+
+# Wraps "chdir". Returns old directory.
+sub _chdir {
+  my ($self, $dir) = @_;
+  my $old_cwd = cwd();
+  $self->_event('CHDIR', $dir);
+  chdir($dir) || croak(sprintf($ERR_MESS_OF{CHDIR}, $dir));
+  $old_cwd;
+}
+
+# Wraps an event.
+sub _event {
+  my ($self, $key, @args) = @_;
+  my ($format, $level) = @{$EVENT_SETTING_OF{$key}};
+  $level ||= 1;
+  if ($self->verbose() >= $level) {
+    printf($format . ".\n", @args);
+  }
+}
+
+# Wraps "open".
+sub _open {
+  my ($path, $mode) = @_;
+  $mode ||= '<';
+  open(my $handle, $mode, $path) || croak(sprintf($ERR_MESS_OF{OPEN}, $path, $!));
+  $handle;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/BuildTask.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/BuildTask.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/BuildTask.pm	(revision 5095)
@@ -0,0 +1,353 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::BuildTask
+#
+# DESCRIPTION
+#   This class hosts information of a build task in the FCM build system.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::BuildTask;
+@ISA = qw(FCM1::Base);
+
+# Standard pragma
+use strict;
+use warnings;
+
+# Standard modules
+use Carp;
+use File::Compare;
+use File::Copy;
+use File::Basename;
+use File::Path;
+use File::Spec::Functions;
+
+# FCM component modules
+use FCM1::Base;
+use FCM1::Timer;
+use FCM1::Util;
+
+# List of property methods for this class
+my @scalar_properties = (
+  'actiontype',  # type of action
+  'dependency',  # list of dependencies for this target
+  'srcfile',     # reference to input FCM1::BuildSrc instance
+  'output',      # output file
+  'outputmtime', # output file modification time
+  'target',      # target name for this task
+  'targetpath',  # search path for the target
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::BuildTask->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::BuildTask class. See
+#   above for allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  bless $self, $class;
+
+  for my $name (@scalar_properties) {
+    $self->{$name} = exists $args{uc ($name)} ? $args{uc ($name)} : undef;
+  }
+
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+
+      if ($name eq 'output') {
+        $self->{outputmtime} = $_[0] ? (stat $_[0]) [9] : undef;
+      }
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'dependency' or $name eq 'targetpath') {
+        # Reference to an array
+        $self->{$name} = [];
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->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 FCM1::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->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 @pknames  = split '__', (@{ $self->srcfile->pkgnames })[-2];
+      my @path     = map {
+        catfile ($_, @pknames);
+      } @{ $self->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->verbose > 2;
+      mkpath $destdir;
+    }
+
+    # List of actions
+    if ($self->actiontype eq 'UPDATE') {
+      # Action is UPDATE: Update file
+      # ------------------------------------------------------------------------
+      print 'Update: ', $new_output, "\n" if $self->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 -f $self->output)
+                          ? compare ($self->output, $self->srcfile->src)
+                          : 1;
+
+      if ($copy_required) {
+        # Set up copy command
+        my $srcfile = $self->srcfile->src;
+        my $destfile = catfile ($destdir, basename($srcfile));
+        print 'Copy: ', $srcfile, "\n", '  to: ', $destfile, "\n"
+          if $self->verbose > 2;
+        &copy ($srcfile, $destfile)
+          or die $srcfile, ': copy to ', $destfile, ' failed (', $!, '), abort';
+        chmod (((stat ($srcfile))[2] & 07777), $destfile);
+
+        $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->get_pre_process;
+        $base     = $self->srcfile->root . lc ($self->srcfile->ext);
+
+        # Get search path for the existing pre-processed file
+        my @pknames  = split '__', (@{ $self->srcfile->pkgnames })[-2];
+        @path        = map {
+          catfile ($_, @pknames);
+        } @{ $self->setting (qw/PATH PPSRC/) };
+
+      } else { # if ($self->actiontype eq 'GENINTERFACE')
+        # Invoke the interface generator
+        # ----------------------------------------------------------------------
+        # Get new interface lines
+        $newlines = $self->srcfile->get_fortran_interface;
+
+        # Get search path for the existing interface file
+        $base     = $self->srcfile->interfacebase;
+        @path     = @{ $self->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 -f $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->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->verbose > 1;
+
+        # Set the name of the pre-processed file
+        $self->srcfile->ppsrc ($newfile) if $self->actiontype eq 'PP';
+
+      } 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->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: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CfgFile.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CfgFile.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CfgFile.pm	(revision 5095)
@@ -0,0 +1,599 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::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.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::CfgFile;
+@ISA = qw(FCM1::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Basename;
+use File::Path;
+use File::Spec;
+
+# FCM component modules
+use FCM1::Base;
+use FCM1::CfgLine;
+use FCM1::Config;
+use FCM1::Keyword;
+use FCM1::Util;
+
+# List of property methods for this class
+my @scalar_properties = (
+  'actual_src', # actual source of configuration file
+  'lines',      # list of lines, FCM1::CfgLine objects
+  'pegrev',     # peg revision of configuration file
+  'src',        # source of configuration file
+  'type',       # type of configuration file
+  'version',    # version of configuration file
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::CfgFile->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::CfgFile class. See above
+#   for allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  bless $self, $class;
+
+  for (@scalar_properties) {
+    $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
+  }
+
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    if (not defined $self->{$name}) {
+      if ($name eq 'lines') {
+        $self->{$name} = [];
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $mtime = $obj->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 = $obj->read_cfg($is_for_inheritance);
+#
+# 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, $is_for_inheritance) = @_;
+
+  my @lines = $self->_get_cfg_lines($is_for_inheritance);
+
+  # List of CFG types that need INC declarations expansion
+  my %exp_inc    = ();
+  for (split (/$FCM1::Config::DELIMITER_LIST/, $self->setting ('CFG_EXP_INC'))) {
+    $exp_inc{uc ($_)} = 1;
+  }
+
+  # List of CFG labels that are reserved keywords
+  my %cfg_keywords = ();
+  for (split (/$FCM1::Config::DELIMITER_LIST/, $self->setting ('CFG_KEYWORD'))) {
+    $cfg_keywords{$self->cfglabel ($_)} = 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->lines->[$cont] : undef;
+    my $warning = 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
+        my $warn;
+        ($value, $warn) = $self->_expand_variable ($value, 1) if $value;
+        $warning .= ($warning ? ', ' : '') . $warn if $warn;
+
+        # Expand internal variables
+        ($value, $warn) = $self->_expand_variable ($value, 0) if $value;
+        $warning .= ($warning ? ', ' : '') . $warn if $warn;
+
+        # Get "line" that begins the current continuation
+        my $v = $start->value . $value;
+        $v =~ s/\\$//;
+        $start->value ($v);
+
+      } 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
+          my $warn;
+          ($value, $warn) = $self->_expand_variable ($value, 1) if $value;
+          $warning .= ($warning ? ', ' : '') . $warn if $warn;
+
+          # Expand internal variables
+          ($value, $warn) = $self->_expand_variable ($value, 0) if $value;
+          $warning .= ($warning ? ', ' : '') . $warn if $warn;
+        }
+      }
+
+      # Determine whether current line ends with a continuation mark
+      if ($value =~ s/\\$//) {
+        $cont = scalar (@{ $self->lines }) unless defined $cont;
+
+      } else {
+        $cont = undef;
+      }
+    }
+
+    if (    defined($self->type())
+        &&  exists($exp_inc{uc($self->type())})
+        &&  uc($start ? $start->label() : $label) eq $self->cfglabel('INC')
+        &&  !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 = FCM1::CfgFile->new (
+          SRC => expand_tilde ($src), TYPE => $self->type,
+        );
+
+        $cfg->read_cfg;
+
+        # Add lines to the lines array in the current configuration file
+        $comment = 'INC ' . $src . ' ';
+        push @{$self->lines}, FCM1::CfgLine->new (
+          comment => $comment . '# Start',
+          number  => ($start ? $start->number : $line_num),
+          src     => $self->actual_src,
+          warning => $warning,
+        );
+        push @{ $self->lines }, @{ $cfg->lines };
+        push @{$self->lines}, FCM1::CfgLine->new (
+          comment => $comment . '# End',
+          src     => $self->actual_src,
+        );
+
+      } else {
+        push @{$self->lines}, FCM1::CfgLine->new (
+          number  => $line_num,
+          src     => $self->actual_src,
+          warning => 'empty INC declaration.'
+        );
+      }
+
+    } else {
+      # Push label:value pair into lines array
+      push @{$self->lines}, FCM1::CfgLine->new (
+        label   => $label,
+        value   => ($label ? $value : ''),
+        comment => $comment,
+        number  => $line_num,
+        src     => $self->actual_src,
+        warning => $warning,
+      );
+    }
+
+    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 (index (uc ($slabel), $self->cfglabel ('CFGFILE')) == 0) {
+      my @words = split /$FCM1::Config::DELIMITER_PATTERN/, $slabel;
+      shift @words;
+
+      my $name = @words ? lc ($words[0]) : 'type';
+
+      if ($self->can ($name)) {
+        $self->$name ($svalue);
+      }
+    }
+
+    # Set internal variable
+    $slabel =~ s/^\%//; # Remove leading "%" from label
+
+    $self->config->variable ($slabel, $svalue)
+      unless exists $cfg_keywords{$slabel};
+  }
+
+  # Report and reset warnings
+  # ----------------------------------------------------------------------------
+  for my $line (@{ $self->lines }) {
+    w_report $line->format_warning if $line->warning;
+    $line->warning (undef);
+  }
+
+  return @{ $self->lines };
+
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->print_cfg ($file, [$force]);
+#
+# 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 unless $force is
+#   specified. 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, $file, $force) = @_;
+
+  # 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 }) {
+    $out .= $line->print_line ($max_label_len - length ($line->label) + 1);
+    $out .= "\n";
+  }
+
+  if ($out) {
+    my $out_handle = 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->verbose;
+        mkpath $dirname;
+      }
+      croak $dirname, ': cannot write to config file directory, abort'
+        unless -d $dirname;
+
+      if (-f $file and not $force) {
+        # Read old config file to see if content has changed
+        open(my $handle, '<', $file) || croak("$file: $!\n");
+        my $in_lines = '';
+        while (my $line = readline($handle)) {
+          $in_lines .= $line;
+        }
+        close($handle);
+
+        # Return if content is up-to-date
+        if ($in_lines eq $out) {
+          print 'No change in ', lc ($self->type), ' cfg: ', $file, "\n"
+            if $self->verbose > 1 and $self->type;
+          return 1;
+        }
+
+        # If config file already exists, make sure it is writable
+        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   = File::Spec->catfile (
+            $dirname, $timestamp . basename ($file)
+          );
+          rename $file, $oldfile;
+          print 'Rename existing ', lc ($self->type), ' cfg: ',
+                $oldfile, "\n" if $self->verbose > 1;
+        }
+      }
+
+      # Open file and select file handle
+      open(my $handle, '>', $file) || croak("$file: $!\n");
+      $out_handle = $handle;
+    }
+
+    # Print output
+    print($out_handle $out);
+
+    # Close file if necessary
+    if ($file) {
+      close($out_handle) || croak("$file: $!\n");
+
+      if ($self->type and $self->verbose > 1) {
+        print 'Generated ', lc ($self->type), ' cfg: ', $file, "\n";
+
+      } elsif ($self->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($is_for_inheritance);
+#
+# DESCRIPTION
+#   This internal method opens the configuration file and returns its contents
+#   as an array of lines. If the $self->src() is given as a URI, the method
+#   tries to read it with "svn cat". Otherwise, the method tries to read
+#   $self->src() with open() and readline(). If $self->type() is not a known
+#   type, $self->src() can only be a regular file and $is_for_inheritance is
+#   ignored.  Otherwise, $self->src() can be a regular file or a directory and
+#   $is_for_inheritance is used to determine the behaviour for searching the
+#   directory for a configuration file. If $is_for_inheritance is set, the
+#   config file may be located at "$src/cfg/$type.cfg". If $is_for_inheritance
+#   is not set, the config file may be located at "$src/$type.cfg" or
+#   "$src/cfg/$type.cfg".
+# ------------------------------------------------------------------------------
+
+sub _get_cfg_lines {
+  my ($self, $is_for_inheritance) = @_;
+  my $DIAG = sub {};
+  my @paths_refs = ([]);
+  if ($self->type() && exists($self->setting('CFG_NAME')->{uc($self->type())})) {
+    my $base = $self->setting('CFG_NAME')->{uc($self->type())};
+    if (!$is_for_inheritance) {
+      push(@paths_refs, [$base]);
+    }
+    push(@paths_refs, [$self->setting(qw/DIR CFG/), $base]);
+    if ($self->verbose()) {
+      $DIAG = sub {printf("Config file (%s): %s\n", $self->type(), @_)};
+    }
+  }
+  if ($self->src() =~ qr{\A([A-Za-z][\w\+-\.]*):}xms) {
+    # $self->src() is a URI, try "svn cat"
+    my $src = FCM1::Util::tidy_url(FCM1::Keyword::expand($self->src()));
+    my ($uri, $rev) = $src =~ qr{\A(.+?)(?:\@([^\@]+))?\z}msx;
+    $rev ||= 'HEAD';
+    for my $paths_ref (@paths_refs) {
+      my $path = join('/', $uri, @{$paths_ref}) . '@' . $rev;
+      local($@);
+      my @lines = eval {
+        run_command([qw/svn cat/, $path], METHOD => 'qx', DEVNULL => 1);
+      };
+      if (!$@) {
+        $self->pegrev($rev);
+        $self->actual_src($path);
+        $DIAG->($path);
+        return @lines;
+      }
+    }
+  }
+  else {
+    # $self->src() is not a URI, assume that it resides in the file system
+    for my $paths_ref (@paths_refs) {
+      my $path = File::Spec->catfile($self->src(), @{$paths_ref});
+      if (-e $path && !-d $path) { # "-f $path" returns false for "/dev/null"
+        open(my $handle, '<', $path)
+          || croak("$path: cannot open config file, abort: $!");
+        my @lines = readline($handle);
+        close($handle);
+        $self->actual_src($path);
+        $DIAG->($path);
+        return @lines;
+      }
+    }
+  }
+  croak(sprintf("%s: cannot locate config file, abort", $self->src()));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_expand_variable ($string, $env[, \%recursive_set]);
+#
+# DESCRIPTION
+#   This internal method expands variables in $string. If $env is true, it
+#   expands environment variables. Otherwise, it expands local variables. If
+#   %recursive_set is specified, it indicates that this method is being called
+#   recursively. In which case, it must not attempt to expand a variable that
+#   exists in the keys of %recursive_set.
+# ------------------------------------------------------------------------------
+
+sub _expand_variable {
+  my ($self, $string, $env, $recursive_set_ref) = @_;
+
+  # Pattern for environment/local variable
+  my @patterns = $env
+    ? (qr#\$([A-Z][A-Z0-9_]+)#, qr#\$\{([A-Z][A-Z0-9_]+)\}#)
+    : (qr#%(\w+(?:::[\w\.-]+)*)#, qr#%\{(\w+(?:(?:::|/)[\w\.-]+)*)\}#);
+
+  my $ret = '';
+  my $warning = undef;
+  while ($string) {
+    # Find the first match in $string
+    my ($prematch, $match, $postmatch, $var_label);
+    for my $pattern (@patterns) {
+      next unless $string =~ /$pattern/;
+      if ((not defined $prematch) or length ($`) < length ($prematch)) {
+        $prematch = $`;
+        $match = $&;
+        $var_label = $1;
+        $postmatch = $';
+      }
+    }
+
+    if ($match) {
+      $ret .= $prematch;
+      $string = $postmatch;
+
+      # Get variable value from environment or local configuration
+      my $variable = $env
+                     ? (exists $ENV{$var_label} ? $ENV{$var_label} : undef)
+                     : $self->config->variable ($var_label);
+
+      if ($env and $var_label eq 'HERE' and not defined $variable) {
+        $variable = dirname ($self->actual_src);
+        $variable = File::Spec->rel2abs ($variable) if not &is_url ($variable);
+      }
+
+      # Substitute match with value of variable
+      if (defined $variable) {
+        my %set = (($recursive_set_ref ? %{$recursive_set_ref} : ()));
+        if (exists($set{$var_label})) {
+          $warning .= ', ' if $warning;
+          $warning .= $match . ': cyclic dependency, variable not expanded';
+          $ret .= $variable;
+
+        } else {
+          my ($r, $w)
+            = $self->_expand_variable($variable, $env, {%set, $var_label => 1});
+          $ret .= $r;
+          if ($w) {
+            $warning .= ', ' if $warning;
+            $warning .= $w;
+          }
+        }
+
+      } else {
+        $warning .= ', ' if $warning;
+        $warning .= $match . ': variable not expanded';
+        $ret .= $match;
+      }
+
+    } else {
+      $ret .= $string;
+      $string = "";
+    }
+  }
+
+  return ($ret, $warning);
+}
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CfgLine.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CfgLine.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CfgLine.pm	(revision 5095)
@@ -0,0 +1,346 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::CfgLine
+#
+# DESCRIPTION
+#   This class is used for grouping the settings in each line of a FCM
+#   configuration file.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::CfgLine;
+@ISA = qw(FCM1::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use File::Basename;
+
+# In-house modules
+use FCM1::Base;
+use FCM1::Config;
+use FCM1::Util;
+
+# List of property methods for this class
+my @scalar_properties = (
+  'bvalue',  # line value, in boolean
+  'comment', # (in)line comment
+  'error',   # error message for incorrect usage while parsing the line
+  'label',   # line label
+  'line',    # content of the line
+  'number',  # line number in source file
+  'parsed',  # has this line been parsed (by the extract/build system)?
+  'prefix',  # optional prefix for line label
+  'slabel',  # label without the optional prefix
+  'src',     # name of source file
+  'value',   # line value
+  'warning', # warning message for deprecated usage
+);
+
+# Useful variables
+our $COMMENT_RULER = '-' x 78;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @cfglines = FCM1::CfgLine->comment_block (@comment);
+#
+# DESCRIPTION
+#   This method returns a list of FCM1::CfgLine objects representing a comment
+#   block with the comment string @comment.
+# ------------------------------------------------------------------------------
+
+sub comment_block {
+  my @return = (
+    FCM1::CfgLine->new (comment => $COMMENT_RULER),
+    (map {FCM1::CfgLine->new (comment => $_)} @_),
+    FCM1::CfgLine->new (comment => $COMMENT_RULER),
+    FCM1::CfgLine->new (),
+  );
+
+  return @return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::CfgLine->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::CfgLine class. See above
+#   for allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  for (@scalar_properties) {
+    $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
+    $self->{$_} = $args{$_} if exists $args{$_};
+  }
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    if (@_) {
+      $self->{$name} = $_[0];
+
+      if ($name eq 'line' or $name eq 'label') {
+        $self->{slabel} = undef;
+
+      } elsif ($name eq 'line' or $name eq 'value') {
+        $self->{bvalue} = undef;
+      }
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name =~ /^(?:comment|error|label|line|prefix|src|value)$/) {
+        # Blank
+        $self->{$name} = '';
+
+      } elsif ($name eq 'slabel') {
+        if ($self->prefix and $self->label_starts_with ($self->prefix)) {
+          $self->{$name} = $self->label_from_field (1);
+
+        } else {
+          $self->{$name} = $self->label;
+        }
+
+      } elsif ($name eq 'bvalue') {
+        if (defined ($self->value)) {
+          $self->{$name} = ($self->value =~ /^(\s*|false|no|off|0*)$/i)
+                           ? 0 : $self->value;
+        }
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @fields = $obj->label_fields ();
+#   @fields = $obj->slabel_fields ();
+#
+# DESCRIPTION
+#   These method returns a list of fields in the (s)label.
+# ------------------------------------------------------------------------------
+
+for my $name (qw/label slabel/) {
+  no strict 'refs';
+
+  my $sub_name = $name . '_fields';
+  *$sub_name = sub  {
+    return (split (/$FCM1::Config::DELIMITER_PATTERN/, $_[0]->$name));
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $obj->label_from_field ($index);
+#   $string = $obj->slabel_from_field ($index);
+#
+# DESCRIPTION
+#   These method returns the (s)label from field $index onwards.
+# ------------------------------------------------------------------------------
+
+for my $name (qw/label slabel/) {
+  no strict 'refs';
+
+  my $sub_name = $name . '_from_field';
+  *$sub_name = sub  {
+    my ($self, $index) = @_;
+    my $method = $name . '_fields';
+    my @fields = $self->$method;
+    return join ($FCM1::Config::DELIMITER, @fields[$index .. $#fields]);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $obj->label_starts_with (@fields);
+#   $flag = $obj->slabel_starts_with (@fields);
+#
+# DESCRIPTION
+#   These method returns a true if (s)label starts with the labels in @fields
+#   (ignore case).
+# ------------------------------------------------------------------------------
+
+for my $name (qw/label slabel/) {
+  no strict 'refs';
+
+  my $sub_name = $name . '_starts_with';
+  *$sub_name = sub  {
+    my ($self, @fields) = @_;
+    my $return = 1;
+
+    my $method = $name . '_fields';
+    my @all_fields = $self->$method;
+
+    for my $i (0 .. $#fields) {
+      next if $all_fields[$i] && lc($fields[$i]) eq lc($all_fields[$i]);
+      $return = 0;
+      last;
+    }
+
+    return $return;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $obj->label_starts_with_cfg (@fields);
+#   $flag = $obj->slabel_starts_with_cfg (@fields);
+#
+# DESCRIPTION
+#   These method returns a true if (s)label starts with the configuration file
+#   labels in @fields (ignore case).
+# ------------------------------------------------------------------------------
+
+for my $name (qw/label slabel/) {
+  no strict 'refs';
+
+  my $sub_name = $name . '_starts_with_cfg';
+  *$sub_name = sub  {
+    my ($self, @fields) = @_;
+
+    for my $field (@fields) {
+      $field = $self->cfglabel ($field);
+    }
+
+    my $method = $name . '_starts_with';
+    return $self->$method (@fields);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $mesg = $obj->format_error ();
+#
+# DESCRIPTION
+#   This method returns a string containing a formatted error message for
+#   anything reported to the current line.
+# ------------------------------------------------------------------------------
+
+sub format_error {
+  my ($self) = @_;
+  my $mesg = '';
+
+  $mesg .= $self->format_warning;
+
+  if ($self->error or not $self->parsed) {
+    $mesg = 'ERROR: ' . $self->src . ': LINE ' . $self->number . ':' . "\n";
+    if ($self->error) {
+      $mesg .= '       ' . $self->error;
+
+    } else {
+      $mesg .= '       ' . $self->label . ': label not recognised.';
+    }
+  }
+
+  return $mesg;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $mesg = $obj->format_warning ();
+#
+# DESCRIPTION
+#   This method returns a string containing a formatted warning message for
+#   any warning reported to the current line.
+# ------------------------------------------------------------------------------
+
+sub format_warning {
+  my ($self) = @_;
+  my $mesg = '';
+
+  if ($self->warning) {
+    $mesg = 'WARNING: ' . $self->src . ': LINE ' . $self->number . ':' . "\n";
+    $mesg .= '         ' . $self->warning;
+  }
+
+  return $mesg;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $line = $obj->print_line ([$space]);
+#
+# DESCRIPTION
+#   This method returns a configuration line using $self->label, $self->value
+#   and $self->comment. The value in $self->line is re-set. If $space is set
+#   and is a positive integer, it sets the spacing between the label and the
+#   value in the line. The default is 1.
+# ------------------------------------------------------------------------------
+
+sub print_line {
+  my ($self, $space) = @_;
+
+  # Set space between label and value, default to 1 character
+  $space = 1 unless $space and $space =~ /^[1-9]\d*$/;
+
+  my $line = '';
+
+  # Add label and value, if label is set
+  if ($self->label) {
+    $line .= $self->label . ' ' x $space;
+    $line .= $self->value if defined $self->value;
+  }
+
+  # Add comment if necessary
+  my $comment = $self->comment;
+  $comment =~ s/^\s*//;
+
+  if ($comment) {
+    $comment = '# ' . $comment if $comment !~ /^#/;
+    $line .= ' ' if $line;
+    $line .= $comment;
+  }
+
+  return $self->line ($line);
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Cm.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Cm.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Cm.pm	(revision 5095)
@@ -0,0 +1,2272 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Cm
+#
+# DESCRIPTION
+#   This module contains the FCM code management functionalities and wrappers
+#   to Subversion commands.
+#
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Cm;
+use base qw{Exporter};
+
+our @EXPORT_OK = qw(cm_check_missing cm_check_unknown cm_switch cm_update);
+
+use Cwd qw{cwd};
+use FCM::System::Exception;
+use FCM1::Config;
+use FCM1::CmBranch;
+use FCM1::CmUrl;
+use FCM1::Keyword;
+use FCM1::Util      qw{
+    get_url_of_wc
+    get_url_peg_of_wc
+    is_url
+    is_wc
+    tidy_url
+};
+use File::Basename qw{basename dirname};
+use File::Path qw{mkpath rmtree};
+use File::Spec;
+use Text::ParseWords qw{shellwords};
+
+# ------------------------------------------------------------------------------
+
+# CLI message handler
+our $CLI_MESSAGE = \&_cli_message;
+
+# List of CLI messages
+our %CLI_MESSAGE_FOR = (
+    q{}           => "%s",
+    BRANCH_LIST   => "%s at %s: %d branch(es) found for %s.\n",
+    CHDIR_WCT     => "%s: working directory changed to top of working copy.\n",
+    CF            => "Conflicts in: %s\n",
+    MERGE_ACTUAL  => "-" x 74 . "actual\n%s" . "-" x 74 . "actual\n",
+    MERGE_COMPARE => "Merge: %s\n c.f.: %s\n",
+    MERGE_OK      => "Merge succeeded.\n",
+    MERGE_DRYRUN  => "-" x 73 . "dry-run\n%s" . "-" x 73 . "dry-run\n",
+    MERGE_REVS    => "Eligible merge(s) from %s: %s\n",
+    OUT_DIR       => "Output directory: %s\n",
+    PATCH_DONE    => "%s: patch generated.\n",
+    PATCH_REV     => "Patch created for changeset %s\n",
+    SEPARATOR     => q{-} x 80 . "\n",
+    STATUS        => "%s: status of %s:\n%s\n",
+);
+
+# CLI abort and error messages
+our %CLI_MESSAGE_FOR_ABORT = (
+    FAIL => "%s: command failed.\n",
+    NULL => "%s: command will result in no change.\n",
+    USER => "%s: abort by user.\n",
+);
+
+# CLI abort and error messages
+our %CLI_MESSAGE_FOR_ERROR = (
+    CHDIR               => "%s: cannot change to directory.\n",
+    CLI                 => "%s",
+    CLI_HELP            => "Type 'fcm help %s' for usage.\n",
+    CLI_MERGE_ARG1      => "Arg 1 must be the source in auto/custom mode.\n",
+    CLI_MERGE_ARG2      => "Arg 2 must be the source in custom mode"
+                           . " if --revision not set.\n",
+    CLI_OPT_ARG         => "--%s: invalid argument [%s].\n",
+    CLI_OPT_WITH_OPT    => "--%s: must be specified with --%s.\n",
+    CLI_USAGE           => "incorrect value for the %s argument",
+    DIFF_PROJECTS       => "%s (target) and %s (source) are not related.\n",
+    INVALID_BRANCH      => "%s: not a valid URL of a standard FCM branch.\n",
+    INVALID_PROJECT     => "%s: not a valid URL of a standard FCM project.\n",
+    INVALID_TARGET      => "%s: not a valid working copy or URL.\n",
+    INVALID_URL         => "%s: not a valid URL.\n",
+    INVALID_WC          => "%s: not a valid working copy.\n",
+    MERGE_REV_INVALID   => "%s: not a revision in the available merge list.\n",
+    MERGE_SELF          => "%s: cannot be merged to its own working copy: %s.\n",
+    MERGE_UNRELATED     => "%s: target and %s: source not directly related.\n",
+    MERGE_UNSAFE        => "%s: source contains changes outside the target"
+                           . " sub-directory. Please merge with a full tree.\n",
+    MKPATH              => "%s: cannot create directory.\n",
+    NOT_EXIST           => "%s: does not exist.\n",
+    PARENT_NOT_EXIST    => "%s: parent %s no longer exists.\n",
+    RMTREE              => "%s: cannot remove.\n",
+    ST_CI_MESG_FILE     => "Attempt to add commit message file:\n%s",
+    ST_CONFLICT         => "File(s) in conflicts:\n%s",
+    ST_MISSING          => "File(s) missing:\n%s",
+    ST_OOD              => "File(s) out of date:\n%s",
+    SWITCH_UNSAFE       => "%s: merge template exists."
+                           . " Please remove before retrying.\n",
+    WC_INVALID_BRANCH   => "%s: not a working copy of a standard FCM branch.\n",
+    WC_URL_NOT_EXIST    => "%s: working copy URL does not exists at HEAD.\n",
+);
+
+# List of CLI prompt messages
+our %CLI_MESSAGE_FOR_PROMPT = (
+    CF_OVERWRITE      => qq{%s: existing changes will be overwritten.\n}
+                         . qq{ Do you wish to continue?},
+    CI                => qq{Would you like to commit this change?},
+    CI_BRANCH_SHARED  => qq{\n}
+                         . qq{*** WARNING: YOU ARE COMMITTING TO A %s BRANCH.\n}
+                         . qq{*** Please ensure that you have the}
+                         . qq{ owner's permission.\n\n}
+                         . qq{Would you like to commit this change?},
+    CI_BRANCH_USER    => qq{\n}
+                         . qq{*** WARNING: YOU ARE COMMITTING TO A BRANCH}
+                         . qq{ NOT OWNED BY YOU.\n}
+                         . qq{*** Please ensure that you have the}
+                         . qq{ owner's permission.\n\n}
+                         . qq{Would you like to commit this change?},
+    CI_TRUNK          => qq{\n}
+                         . qq{*** WARNING: YOU ARE COMMITTING TO THE TRUNK.\n}
+                         . qq{*** Please ensure that your change conforms to}
+                         . qq{ your project's working practices.\n\n}
+                         . qq{Would you like to commit this change?},
+    CONTINUE          => qq{%s: continue?},
+    MERGE             => qq{Would you like to go ahead with the merge?},
+    MERGE_REV         => qq{Enter a revision},
+    MKPATCH_OVERWRITE => qq{%s: output location exists. OK to overwrite?},
+    RUN_SVN_COMMAND   => qq{Would you like to run "svn %s"?},
+);
+
+# List of CLI warning messages
+our %CLI_MESSAGE_FOR_WARNING = (
+    BRANCH_SUBDIR   => "%s: is a sub-directory of a branch in a FCM project.\n",
+    CF_BINARY       => "%s: ignoring binary file, please resolve manually.\n",
+    INVALID_BRANCH  => $CLI_MESSAGE_FOR_ERROR{INVALID_BRANCH},
+    ST_IN_TRAC_DIFF => "%s: local changes cannot be displayed in Trac.\n"
+);
+
+# CLI prompt handler and title prefix
+our $CLI_PROMPT = \&_cli_prompt;
+our $CLI_PROMPT_PREFIX = q{fcm };
+
+# Event handlers
+our %CLI_HANDLER_OF = (
+    'WC_STATUS'      => \&_cli_handler_of_wc_status,
+    'WC_STATUS_PATH' => \&_cli_handler_of_wc_status_path,
+);
+
+# Common patterns
+our %PATTERN_OF = (
+    # A CLI option
+    CLI_OPT => qr{
+        \A            (?# beginning)
+        (--\w[\w-]*=) (?# capture 1, a long option label)
+        (.*)          (?# capture 2, the value of the option)
+        \z            (?# end)
+    }xms,
+    # A CLI revision option
+    CLI_OPT_REV => qr{
+        \A                      (?# beginning)
+        (--revision(?:=|\z)|-r) (?# capture 1, --revision, --revision= or -r)
+        (.*)                    (?# capture 2, trailing value)
+        \z                      (?# end)
+    }xms,
+    # A CLI revision option range
+    CLI_OPT_REV_RANGE => qr{
+        \A                  (?# beginning)
+        (                   (?# capture 1, begin)
+            (?:\{[^\}]+\}+) (?# a date in curly braces)
+            |               (?# or)
+            [^:]+           (?# anything but a colon)
+        )                   (?# capture 1, end)
+        (?::(.*))?          (?# colon, and capture 2 til the end)
+        \z                  (?# end)
+    }xms,
+    # A FCM branch path look-alike, should be configurable in the future
+    FCM_BRANCH_PATH => qr{
+        \A                            (?# beginning)
+        /*                            (?# some slashes)
+        (?:                           (?# group 1, begin)
+            (?:trunk/*(?:@\d+)?\z)    (?# trunk at a revision)
+            |                         (?# or)
+            (?:trunk|branches|tags)/+ (?# trunk, branch or tags)
+        )                             (?# group 1, end)
+    }xms,
+    # Last line of output from "svn status -u"
+    ST_AGAINST_REV => qr{
+        \A                           (?# beginning)
+        Status\sagainst\srevision:.* (?# output of svn status -u)
+        \z                           (?# end)
+    }xms,
+    # Extract path from "svn status"
+    ST_PATH => qr{
+        \A   (?# beginning)
+        .{6} (?# 6 columns)
+        \s+  (?# spaces)
+        (.+) (?# capture 1, target path)
+        \z   (?# end)
+    }xms,
+    # A legitimate "svn" revision
+    SVN_REV => qr{
+        \A                                      (?# beginning)
+        (?:\d+|HEAD|BASE|COMMITTED|PREV|\{.+\}) (?# digit, reserved words, date)
+        \z                                      (?# end)
+    }ixms,
+);
+
+# Status matchers
+our %ST_MATCHER_FOR = (
+    CONFLICT => sub {substr($_[0], 0, 1) eq 'C' || substr($_[0], 6, 1) eq 'C'},
+    MISSING  => sub {substr($_[0], 0, 1) eq '!'},
+    MODIFIED => sub {substr($_[0], 0, 7) =~ qr{\S}xms},
+    OOD      => sub {substr($_[0], 8, 1) eq '*'},
+    UNKNOWN  => sub {substr($_[0], 0, 1) eq '?'},
+);
+
+# Set the FCM::Util object by FCM::System::CM.
+our $UTIL;
+sub set_util {
+    $UTIL = shift();
+}
+
+# Set the commit message utility provided by FCM::System::CM.
+our $COMMIT_MESSAGE_UTIL;
+sub set_commit_message_util {
+    $COMMIT_MESSAGE_UTIL = shift();
+    FCM1::CmBranch::set_commit_message_util($COMMIT_MESSAGE_UTIL);
+}
+
+# Set the SVN utility provided by FCM::System::CM.
+our $SVN;
+sub set_svn_util {
+    $SVN = shift();
+    FCM1::CmUrl::set_svn_util($SVN);
+    FCM1::CmBranch::set_svn_util($SVN);
+}
+
+# Returns the branch URL as an instance of FCM1::CmUrl.
+sub _branch_url {
+    my $arg = shift();
+    my $url
+        = $arg && is_url($arg) ? FCM1::CmUrl->new(URL => $arg)
+        : $arg && is_wc($arg)  ? FCM1::CmUrl->new(URL => get_url_of_wc($arg))
+        : !$arg && is_wc()     ? FCM1::CmUrl->new(URL => get_url_of_wc())
+        :                        undef
+        ;
+    if (!$url) {
+        return _cm_err(FCM1::Cm::Exception->INVALID_TARGET, $arg ? $arg : q{.});
+    }
+    $url;
+}
+
+# Branch delete.
+sub cm_branch_delete {
+    my ($option_ref, $arg) = @_;
+    my $branch = cm_branch_info($option_ref, $arg);
+    $branch->del(
+        PASSWORD            => $option_ref->{password},
+        NON_INTERACTIVE     => $option_ref->{'non-interactive'},
+        SVN_NON_INTERACTIVE => $option_ref->{'svn-non-interactive'},
+    );
+    if (!$arg && $option_ref->{'switch'}) {
+        cm_switch($option_ref, $branch->layout()->get_config()->{'dir-trunk'});
+    }
+}
+
+# Branch diff.
+sub cm_branch_diff {
+    my ($option_ref, $target) = @_;
+    local(%ENV) = %ENV;
+    $ENV{FCM_GRAPHIC_DIFF} ||= $UTIL->external_cfg_get('graphic-diff');
+    my @diff_cmd
+        = $option_ref->{graphical}  ? (qw{
+            --config-option config:working-copy:exclusive-locking-clients=
+            --diff-cmd fcm_graphic_diff
+        })
+        : $option_ref->{'diff-cmd'} ? ('--diff-cmd', $option_ref->{'diff-cmd'})
+        :                             ()
+        ;
+    if ($option_ref->{extensions}) {
+        push(@diff_cmd, '--extensions', shellwords($option_ref->{extensions}));
+    }
+
+    # Target can be a URL/path, default to $PWD.
+    $target ||= q{.};
+    my $target_is_path = !is_url($target);
+
+    # Get repository and branch information
+    my $url = bless(_branch_url($target), 'FCM1::CmBranch');
+
+    # Check that URL is a standard FCM branch
+    if (!$url->is_branch()) {
+        return _cm_err(FCM1::Cm::Exception->INVALID_BRANCH, $url->url_peg());
+    }
+
+    # Save and remove sub-directory part of the URL
+    my $subdir = $url->subdir();
+    $url->url_peg($url->branch_url_peg());
+
+    # Check that $url exists
+    if (!$url->url_exists()) {
+        return _cm_err(FCM1::Cm::Exception->INVALID_URL, $url->url_peg());
+    }
+
+    # Compare current branch with its parent
+    my $parent = FCM1::CmBranch->new(URL => $url->parent()->url());
+    if ($url->pegrev()) {
+      $parent->url_peg($parent->url() . '@' . $url->pegrev());
+    }
+
+    if (!$parent->url_exists()) {
+        return _cm_err(
+            FCM1::Cm::Exception->PARENT_NOT_EXIST, $url->url_peg(), $parent->url(),
+        );
+    }
+
+    my $base = $parent->base_of_merge_from($url);
+
+    # Ensure the correct diff (syntax) is displayed
+    # Reinstate the sub-tree part into the URL
+    if ($subdir) {
+      $url->url_peg($url->branch_url() . '/' . $subdir . '@' . $url->pegrev());
+      $base->url_peg($base->branch_url() . '/' . $subdir . '@' . $base->pegrev());
+    }
+
+    if ($option_ref->{trac} || $option_ref->{wiki}) {
+        if ($target_is_path && _svn_status_get([$target])) {
+            $CLI_MESSAGE->('ST_IN_TRAC_DIFF', $target);
+        }
+
+        # Trac wiki syntax
+        my $wiki_syntax = 'diff:' . $base->path_peg() . '//' . $url->path_peg();
+
+        if ($option_ref->{wiki}) {
+            $CLI_MESSAGE->(q{}, "$wiki_syntax\n");
+        }
+        else { # if $option_ref->{trac}
+            my $browser = $UTIL->external_cfg_get('browser');
+            my $trac_url = FCM1::Keyword::get_browser_url($url->project_url());
+            # FIXME: assuming that the browser URL uses the InterTrac syntax
+            $trac_url =~ s{/intertrac/.*$}{/search?q=$wiki_syntax}xms;
+            my %value_of = %{$UTIL->shell_simple([$browser, $trac_url])};
+            if ($value_of{rc}) {
+                return FCM::System::Exception->throw(
+                    FCM::System::Exception->SHELL,
+                    {command_list => [$browser, $trac_url], %value_of},
+                    $value_of{e},
+                );
+            }
+        }
+    }
+    else {
+        $SVN->call(
+            'diff', @diff_cmd,
+            ($option_ref->{summarize} ? ('--summarize') : ()),
+		    ($option_ref->{xml} ? ('--xml') : ()),
+            '--old', $base->url_peg(),
+            '--new', ($target_is_path ? $target : $url->url_peg()),
+        );
+    }
+}
+
+# Branch info.
+sub cm_branch_info {
+    my ($option_ref, $arg) = @_;
+    my $url = _branch_url($arg);
+    FCM1::Config->instance()->verbose($option_ref->{verbose} ? 1 : 0);
+    my $branch = FCM1::CmBranch->new(URL => $url->url_peg());
+    if (!$branch->branch()) {
+        return _cm_err(FCM1::Cm::Exception->INVALID_BRANCH, $branch->url_peg());
+    }
+    if (!$branch->url_exists()) {
+        return _cm_err(FCM1::Cm::Exception->NOT_EXIST, $branch->url_peg());
+    }
+    $branch->url_peg($branch->branch_url_peg());
+    $option_ref->{'show-children'} ||= $option_ref->{'show-all'};
+    $option_ref->{'show-other'   } ||= $option_ref->{'show-all'};
+    $option_ref->{'show-siblings'} ||= $option_ref->{'show-all'};
+    $branch->display_info(
+        SHOW_CHILDREN => $option_ref->{'show-children'},
+        SHOW_OTHER    => $option_ref->{'show-other'   },
+        SHOW_SIBLINGS => $option_ref->{'show-siblings'},
+    );
+    $branch;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &FCM1::Cm::cm_commit ();
+#
+# DESCRIPTION
+#   This is a FCM wrapper to the "svn commit" command.
+# ------------------------------------------------------------------------------
+
+sub cm_commit {
+  my ($option_ref, $path) = @_;
+  $path ||= cwd();
+  if (!-e $path) {
+    return _cm_err(FCM1::Cm::Exception->NOT_EXIST, $path);
+  }
+
+  # Make sure we are in a working copy
+  if (!is_wc($path)) {
+    return _cm_err(FCM1::Cm::Exception->INVALID_WC, $path);
+  }
+
+  # Make sure we are at the top level of the working copy
+  # (otherwise we might miss any template commit message)
+  my $dir = $SVN->get_wc_root($path);
+
+  if ($dir ne cwd ()) {
+    chdir($dir) || return _cm_err(FCM1::Cm::Exception->CHDIR, $dir);
+    $CLI_MESSAGE->('CHDIR_WCT', $dir);
+  }
+
+  # Get update status of working copy
+  # Check working copy files are not in conflict, missing, or out of date
+  my @status = _svn_status_get([], 1);
+  if (!defined($option_ref->{'dry-run'})) {
+    my %st_lines_of = (CONFLICT => [], MISSING => [], OOD => []);
+
+    LINE:
+    for my $line (@status) {
+      for my $key (keys(%st_lines_of)) {
+        if ($line && $ST_MATCHER_FOR{$key}->($line)) {
+          push(@{$st_lines_of{$key}}, $line);
+          next LINE;
+        }
+      }
+      # 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)
+      my ($file) = $line =~ qr/\AA.{8}\s*\d+\s+(.*)/msx;
+      if (!$file || !-f $file) {
+        next LINE;
+      }
+      my ($command, @arguments)
+        = (-x $file && !-l $file) ? ('propset', '*') : ('propdel');
+      $SVN->call($command, qw{-q svn:executable}, @arguments, $file);
+    }
+
+    # Abort commit if files are in conflict, missing, or out of date
+    my @keys = grep {@{$st_lines_of{$_}}} keys(%st_lines_of);
+    if (@keys) {
+      for my $key (sort(@keys)) {
+        my @lines = map {"$_\n"} @{$st_lines_of{$key}};
+        $CLI_MESSAGE->('ST_' . $key, join(q{}, @lines));
+      }
+      return _cm_abort(FCM1::Cm::Abort->FAIL);
+    }
+  }
+
+  # Read in any existing message
+  my $commit_message_ctx = $COMMIT_MESSAGE_UTIL->load();
+
+  # Execute "svn status" for a list of changed items
+  @status = map {$_ . "\n"} grep {$_ =~ qr/\A[^\?]/msx} _svn_status_get();
+
+  # Abort if there is no change in the working copy
+  if (!@status) {
+    return _cm_abort(FCM1::Cm::Abort->NULL);
+  }
+
+  # Abort if attempt to add commit message file
+  my $ci_mesg_file_base = $COMMIT_MESSAGE_UTIL->path_base();
+  my @bad_status = grep {$_ =~ qr{^A.*?\s$ci_mesg_file_base\n}m} @status;
+  if (@bad_status) {
+    for my $bad_status (@bad_status) {
+      $CLI_MESSAGE->('ST_CI_MESG_FILE', $bad_status);
+    }
+    return _cm_abort(FCM1::Cm::Abort->FAIL);
+  }
+
+  # Get associated URL of current working copy
+  my $layout = $SVN->get_layout($SVN->get_info()->[0]->{url});
+
+  # Include URL, or project, branch and sub-directory info in @status
+  unshift @status, "\n";
+
+  if ($layout->get_branch()) {
+    unshift(@status,
+      map {sprintf("[%-7s: %s]\n", @{$_})} (
+        ['Root'   , $layout->get_root()    ],
+        ['Project', $layout->get_project() ],
+        ['Branch' , $layout->get_branch()  ],
+        ['Sub-dir', $layout->get_sub_tree()],
+      ),
+    );
+  }
+  else {
+    unshift(@status,
+      map {sprintf("[%s: %s]\n", @{$_})} (
+        ['Root', $layout->get_root()],
+        ['Path', $layout->get_path()],
+      ),
+    );
+  }
+
+  # Use a temporary file to store the final commit log message
+  $commit_message_ctx->set_info_part(join(q{}, @status));
+  $COMMIT_MESSAGE_UTIL->edit($commit_message_ctx);
+  $COMMIT_MESSAGE_UTIL->notify($commit_message_ctx);
+
+  # Check with the user to see if he/she wants to go ahead
+  my $reply = 'n';
+  if (!defined($option_ref->{'dry-run'})) {
+    $reply = $CLI_PROMPT->('commit', (
+        $layout->is_trunk()         ? ('CI_TRUNK')
+      : !$layout->get_branch_owner()? ('CI')
+      : $layout->is_owned_by_user() ? ('CI')
+      : $layout->is_shared()        ? ('CI_BRANCH_SHARED',
+                                       $layout->get_branch_owner())
+      :                               ('CI_BRANCH_USER')
+    ));
+  }
+
+  if ($reply eq 'y') {
+    # Commit the change if user replies "y" for "yes"
+    my $temp = $COMMIT_MESSAGE_UTIL->temp($commit_message_ctx);
+    eval {$SVN->call(
+      qw{commit -F}, "$temp",
+      ($option_ref->{'svn-non-interactive'} ? '--non-interactive' : ()),
+      (   defined($option_ref->{password})
+          ? ('--password', $option_ref->{password}) : ()
+      ),
+    )};
+    if ($@) {
+      $COMMIT_MESSAGE_UTIL->save($commit_message_ctx);
+      die($@);
+    }
+
+    # Remove commit message file
+    unlink($COMMIT_MESSAGE_UTIL->path());
+
+    # Update the working copy
+    _svn_update();
+
+  } else {
+    $COMMIT_MESSAGE_UTIL->save($commit_message_ctx);
+    if (!$option_ref->{'dry-run'}) {
+      return _cm_abort();
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &FCM1::Cm::cm_merge ();
+#
+# DESCRIPTION
+#   This is a wrapper to "svn merge".
+# ------------------------------------------------------------------------------
+
+sub cm_merge {
+  my ($option_ref, @args) = @_;
+  # Find out the URL of the working copy
+  if (!is_wc()) {
+    return _cm_err(FCM1::Cm::Exception->INVALID_WC, '.');
+  }
+  my $wct = $SVN->get_wc_root();
+  if ($wct ne cwd()) {
+    chdir($wct) || return _cm_err(FCM1::Cm::Exception->CHDIR, $wct);
+    $CLI_MESSAGE->('CHDIR_WCT', $wct);
+  }
+  my $target = FCM1::CmBranch->new(URL => get_url_of_wc($wct));
+  if (!$target->url_exists()) {
+    return _cm_err(FCM1::Cm::Exception->WC_URL_NOT_EXIST, '.');
+  }
+
+  # 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();
+  if ($subdir) {
+    $target->url_peg($target->branch_url_peg());
+  }
+
+  # Check for any local modifications
+  # ----------------------------------------------------------------------------
+  if (!$option_ref->{'dry-run'} && !$option_ref->{'non-interactive'}) {
+    _svn_status_checker('merge', 'MODIFIED', $CLI_HANDLER_OF{WC_STATUS})->();
+  }
+
+  # Determine the SOURCE URL
+  # ----------------------------------------------------------------------------
+  my $source;
+
+  if ($option_ref->{reverse}) {
+    # Reverse merge, the SOURCE is the working copy URL
+    $source = FCM1::CmBranch->new (URL => $target->url);
+
+  } else {
+    # Automatic/custom merge, argument 1 is the SOURCE of the merge
+    my $source_url = shift (@args);
+    if (!$source_url) {
+      _cli_err('CLI_MERGE_ARG1');
+    }
+
+    $source = _cm_get_source($source_url, $target);
+  }
+
+  # Parse the revision option
+  # ----------------------------------------------------------------------------
+  my @revs
+    = (grep {$option_ref->{$_}} qw{reverse custom}) && $option_ref->{revision}
+    ? split(qr{:}xms, $option_ref->{revision})
+    : ();
+
+  # Determine the merge delta and the commit log message
+  # ----------------------------------------------------------------------------
+  my (@delta, $mesg, @logs);
+  my $separator = '-' x 80 . "\n";
+
+  if ($option_ref->{reverse}) {
+    # Reverse merge
+    # --------------------------------------------------------------------------
+    if (@revs == 0) {
+      my $last_commit_rev = $source->svninfo('FLAG' => 'commit:revision');
+      @revs = ($last_commit_rev, $last_commit_rev - 1);
+    }
+    elsif (@revs == 1) {
+      $revs[1] = ($revs[0] - 1);
+    }
+    else {
+      @revs = sort {$b <=> $a} @revs;
+    }
+
+    # "Delta" of the "svn merge" command
+    @delta = ('-r' . $revs[0] . ':' . $revs[1], $source->url_peg);
+
+    # Template message
+    $mesg = 'Reversed r' . $revs[0];
+    if ($revs[1] < $revs[0] - 1) {
+      $mesg .= ':' . $revs[1];
+    }
+    if ($source->path()) {
+      $mesg .= ' of ' . $source->path();
+    }
+    $mesg .= "\n";
+
+  } elsif ($option_ref->{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->url_peg(
+        $source->branch_url() . '/' . $subdir . '@' . $source->pegrev(),
+      );
+      $target->url_peg(
+        $target->branch_url() . '/' . $subdir . '@' . $target->pegrev(),
+      );
+
+      # "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 (@args);
+      if (!$source2_url) {
+        _cli_err('CLI_MERGE_ARG2');
+      }
+
+      my $source2 = _cm_get_source($source2_url, $target);
+      for my $item ($source, $source2, $target) {
+        $item->url_peg($item->branch_url() . '/' . $subdir . '@' . $item->pegrev());
+      }
+
+      # "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
+    if (!$target->branch()) {
+      return _cm_err(FCM1::Cm::Exception->WC_INVALID_BRANCH, $wct);
+    }
+    if ($source->branch() eq $target->branch()) {
+      return _cm_err(FCM1::Cm::Exception->MERGE_SELF, $target->url_peg(), $wct);
+    }
+
+    # Only allow the merge if the source and target are "directly related"
+    # --------------------------------------------------------------------------
+    my $anc = $target->ancestor ($source);
+    return _cm_err(
+      FCM1::Cm::Exception->MERGE_UNRELATED, $target->url_peg(), $source->url_peg
+    ) 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) {
+      if ($option_ref->{verbose}) {
+        # Verbose mode, print log messages of available merges
+        $CLI_MESSAGE->('MERGE_REVS', $source->path_peg(), q{});
+        for (@revs) {
+          $CLI_MESSAGE->('SEPARATOR');
+          $CLI_MESSAGE->(q{}, $source->display_svnlog($_));
+        }
+        $CLI_MESSAGE->('SEPARATOR');
+      }
+      else {
+        # Normal mode, list revisions of available merges
+        $CLI_MESSAGE->('MERGE_REVS', $source->path_peg(), join(q{ }, @revs));
+      }
+
+    } else {
+      return _cm_abort(FCM1::Cm::Abort->NULL);
+    }
+
+    # If more than one merge available, prompt user to enter a revision number
+    # to merge from, default to $revs [0]
+    # --------------------------------------------------------------------------
+    if ($option_ref->{'non-interactive'} || @revs == 1) {
+      $source->url_peg($source->url() . '@' . $revs[0]);
+    }
+    else {
+      my $reply = $CLI_PROMPT->(
+        {type => q{}, default => $revs[0]}, 'merge', 'MERGE_REV',
+      );
+      if (!defined($reply)) {
+        return _cm_abort();
+      }
+      # Expand revision keyword if necessary
+      if ($reply) {
+        $reply = (FCM1::Keyword::expand($target->project_url(), $reply))[1];
+      }
+      # Check that the reply is a number in the available merges list
+      if (!grep {$_ eq $reply} @revs) {
+        return _cm_err(FCM1::Cm::Exception->MERGE_REV_INVALID, $reply)
+      }
+      $source->url_peg($source->url() . '@' . $reply);
+    }
+
+    # 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)) {
+      return _cm_err(FCM1::Cm::Exception->MERGE_UNSAFE, $source->url_peg());
+    }
+
+    # 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 $source_full = FCM1::CmBranch->new (URL => $source->url_peg);
+    my $base_full = FCM1::CmBranch->new (URL => $base->url_peg);
+
+    if ($subdir) {
+      $source_full->url_peg(
+        $source_full->branch_url() . '/' . $subdir . '@' . $source_full->pegrev()
+      );
+      $base_full->url_peg(
+        $base_full->branch_url() . '/' . $subdir . '@' . $base_full->pegrev()
+      );
+    }
+
+    # Diagnostic
+    $CLI_MESSAGE->('SEPARATOR'); 
+    $CLI_MESSAGE->('MERGE_COMPARE', $source->path_peg(), $base->path_peg()); 
+    # Delta of the "svn merge" command
+    @delta = ($base_full->url_peg, $source_full->url_peg);
+
+    # Template message
+    $mesg = sprintf(
+      "Merged into %s: %s cf. %s",
+      $target->path(), $source->path_peg(), $base->path_peg(),
+    );
+
+    if (exists($option_ref->{'auto-log'})) {
+      my $last_merge_from_source = ($target->last_merge_from($source))[1];
+      if (!defined($last_merge_from_source)) {
+        $last_merge_from_source = $target->ancestor($source);
+      }
+      my %log_entries = $source->svnlog(
+        REV => [$last_merge_from_source->pegrev() + 1, $source->pegrev()],
+      );
+      @logs = sort {$b->{'revision'} <=> $a->{'revision'}} values(%log_entries);
+    }
+  }
+
+  # Run "svn merge" in "--dry-run" mode to see the result
+  # ----------------------------------------------------------------------------
+  my $dry_run_output
+    = $SVN->stdout(qw{svn merge --dry-run --non-interactive}, @delta);
+
+  # Abort merge if it will result in no change
+  if (!$dry_run_output) {
+    return _cm_abort(FCM1::Cm::Abort->NULL);
+  }
+
+  # Report result of "svn merge --dry-run"
+  if ($option_ref->{'dry-run'} || !$option_ref->{'non-interactive'}) {
+    $CLI_MESSAGE->('MERGE_DRYRUN', $dry_run_output);
+  }
+
+  return if $option_ref->{'dry-run'};
+
+  # Prompt the user to see if (s)he would like to go ahead
+  # ----------------------------------------------------------------------------
+  # Go ahead with merge only if user replies "y"
+  if (
+    !$option_ref->{'non-interactive'} && $CLI_PROMPT->('merge', 'MERGE') ne 'y'
+  ) {
+    return _cm_abort();
+  }
+  $SVN->call('cleanup');
+  my $output = $SVN->stdout(qw{svn merge --non-interactive}, @delta);
+  $CLI_MESSAGE->('MERGE_OK');
+  if ($output ne $dry_run_output) {
+    $CLI_MESSAGE->('MERGE_ACTUAL', $output);
+  }
+
+  # Prepare the commit log
+  # ----------------------------------------------------------------------------
+  my $commit_message_ctx = $COMMIT_MESSAGE_UTIL->load();
+  my @auto_log = map {
+    my $log_entry = $_;
+    my @msg_list = (
+      map  {q{> } . $_}
+      grep {
+            $_
+        &&  $_ !~ qr{\AMerged\sinto\s\S+:\s(?:\S+)\scf\.\s(?:\S+)\z}msx
+        &&  $_ !~ qr{\A(?:\#\d+(?:,\#\d+)*:\s)?Created\s\S+\sfrom\s\S+\.\z}msx
+        &&  $_ !~ qr{\Ar\d+:\z}msx
+        &&  $_ !~ qr{\A>\s.+\z}msx
+      }
+      split("\n", $log_entry->{'msg'})
+    );
+    @msg_list ? ('----', 'r' . $log_entry->{'revision'} . ':', @msg_list) : ();
+  } @logs;
+  my @messages = (
+    $mesg,
+    (@auto_log ? (@auto_log, '----'): ()),
+    $commit_message_ctx->get_auto_part()
+  );
+  $commit_message_ctx->set_auto_part(join("\n", @messages));
+  $COMMIT_MESSAGE_UTIL->save($commit_message_ctx);
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &FCM1::Cm::cm_mkpatch ();
+#
+# DESCRIPTION
+#   This is a FCM command to create a patching script from particular revisions
+#   of a URL.
+# ------------------------------------------------------------------------------
+
+sub cm_mkpatch {
+  my ($option_ref, $u, $outdir) = @_;
+  # Process command line options and arguments
+  my @exclude = $option_ref->{exclude} ? @{$option_ref->{exclude}} : ();
+  my $organisation = $option_ref->{organisation};
+  my $revision = $option_ref->{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 ||= 'original';
+
+  # Make sure revision option is set correctly
+  my @revs = $revision ? split (/:/, $revision) : ();
+  @revs    = @revs [0, 1] if @revs > 2;
+
+  if (!$u) {
+    _cli_err('CLI_USAGE', 'URL');
+  }
+
+  my $url = FCM1::CmUrl->new (URL => $u);
+  if (!$url->is_url()) {
+    return _cm_err(FCM1::Cm::Exception->INVALID_URL, $u);
+  }
+  if (!$url->url_exists()) {
+    return _cm_err(FCM1::Cm::Exception->NOT_EXIST, $u);
+  }
+  if (!$url->branch()) {
+    $CLI_MESSAGE->('INVALID_BRANCH', $u);
+  }
+  elsif ($url->subdir()) {
+    $CLI_MESSAGE->('BRANCH_SUBDIR', $u);
+  }
+
+  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
+    if ($CLI_PROMPT->('mkpatch', 'MKPATCH_OVERWRITE', $outdir) ne 'y') {
+      return _cm_abort();
+    }
+
+    rmtree($outdir) || return _cm_err(FCM1::Cm::Exception->RMTREE, $outdir);
+  }
+
+  # (Re-)create output directory
+  mkpath($outdir) || return _cm_err(FCM1::Cm::Exception->MKPATH, $outdir);
+  $CLI_MESSAGE->('OUT_DIR', $outdir);
+
+  # Get and process log of URL
+  # ----------------------------------------------------------------------------
+  my @script   = (); # main output script
+  my %log      = $url->svnlog (REV => \@revs);
+  my $url_path = $url->path;
+
+  for my $rev (sort {$a <=> $b} keys %log) {
+    # Look at the changed paths for each revision
+    my $use_patch     = 1; # OK to use a patch file?
+    my $only_modified = 1; # Change only contains modifications?
+    my @paths;
+    PATH: for my $path (sort keys %{ $log{$rev}{paths} }) {
+      my $file = $path;
+
+      # Skip paths outside of the branch
+      next PATH unless $file =~ s#^$url_path/##;
+
+      # Skip excluded paths
+      for my $exclude (@exclude) {
+        if ($file =~ m#^$exclude(?:/|$)#) {
+          # Can't use a patch file if any files have been excluded
+          $use_patch = 0;
+          next PATH;
+        }
+      }
+
+      # Can't use a patch file if any files have been added or replaced
+      $use_patch = 0 if $log{$rev}{paths}{$path}{action} eq 'A' or
+                        $log{$rev}{paths}{$path}{action} eq 'R';
+
+      $only_modified = 0 unless $log{$rev}{paths}{$path}{action} eq 'M';
+
+      push @paths, $path;
+    }
+
+    # If the change only contains modifications, make sure they aren't
+    # just property changes
+    if ($only_modified) {
+      my @changedpaths;
+      for my $path (@paths) {
+        (my $file = $path) =~ s#^$url_path/*##;
+        my @diff = $SVN->stdout(
+          qw{svn diff --no-diff-deleted --summarize -c}, $rev,
+          sprintf("%s/%s@%s", $url->url(), $file, $rev),
+        );
+        next unless $diff[-1] =~ /^[A-Z]/;
+        push @changedpaths, $path;
+      }
+      @paths = @changedpaths;
+    }
+
+    next unless @paths;
+
+    # Create the patch using "svn diff"
+    my $patch = ();
+    if ($use_patch) {
+      $patch = $SVN->stdout(
+          qw{svn diff --no-diff-deleted -c}, $rev, $url->url(),
+      );
+      if ($patch) {
+        # Don't use the patch if it may contain subversion keywords or
+        # any changes to PDF files or any changes to symbolic links or
+        # any carriage returns in the middle of a line
+        for (split(qr{\n}msx, $patch)) {
+          if (/\$[a-zA-Z:]+ *\$/ or /^--- .+\.pdf\t/ or /^\+link / or /\r.+/) {
+            $use_patch = 0;
+            last;
+          }
+        }
+      } else {
+        $use_patch = 0;
+      }
+    }
+
+    # Create a directory for this revision in the output directory
+    my $outdir_rev = File::Spec->catfile ($outdir, $rev);
+    mkpath($outdir_rev)
+      || return _cm_err(FCM1::Cm::Exception->MKPATH, $outdir_rev);
+
+    # 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-message');
+    open FILE, '>', $f_revlog or die $f_revlog, ': cannot open (', $!, ')';
+    print FILE @msg;
+    close FILE or die $f_revlog, ': cannot close (', $!, ')';
+
+    # Handle each changed path
+    my $export_file   = 1;  # name for next exported file (gets incremented)
+    my $patch_needed  = 0;  # is a patch file required?
+    my @before_script = (); # patch script to run before patch applied
+    my @after_script  = (); # patch script to run after patch applied
+    my @copied_dirs   = (); # copied directories
+    CHANGED: for my $path (@paths) {
+      (my $file = $path) =~ s#^$url_path/*##;
+      my $url_file = $url->url . '/' . $file . '@' . $rev;
+
+      # Skip paths within copied directories
+      for my $copied_dir (@copied_dirs) {
+        next CHANGED if $file =~ m#^$copied_dir(?:/|$)#;
+      }
+
+      # Handle deleted files
+      if ($log{$rev}{paths}{$path}{action} eq 'D') {
+        push @after_script, 'svn delete "' . $file . '"';
+
+      } else {
+        # Skip property changes (if not done earlier)
+        if (not $only_modified and $log{$rev}{paths}{$path}{action} eq 'M') {
+          my @diff = $SVN->stdout(
+            qw{svn diff --no-diff-deleted --summarize -c}, $rev, $url_file,
+          );
+          next CHANGED unless $diff[-1] =~ /^[A-Z]/;
+        }
+
+        # Determine if the file is a directory
+        my $is_dir
+          =     $log{$rev}{paths}{$path}{action} ne 'M'
+            &&  $SVN->get_info($url_file)->[0]->{'kind'} eq 'dir';
+
+        # Decide how to treat added files
+        my $export_required = 0;
+        if ($log{$rev}{paths}{$path}{action} eq 'A') {
+          my $is_newfile = 0;
+          # Determine if the file is copied
+          if (exists $log{$rev}{paths}{$path}{'copyfrom-path'}) {
+            if ($is_dir) {
+              # A copied directory needs to be exported and added recursively
+              push @after_script, 'svn add "' . $file . '"';
+              $export_required = 1;
+              push @copied_dirs, $file;
+            } else {
+              # History exists for this file
+              my $copyfrom_path = $log{$rev}{paths}{$path}{'copyfrom-path'};
+              my $copyfrom_rev  = $log{$rev}{paths}{$path}{'copyfrom-rev'};
+              my $cp_url = FCM1::CmUrl->new (
+                URL => $url->root . $copyfrom_path . '@' . $copyfrom_rev,
+              );
+
+              if ($copyfrom_path =~ s#^$url_path/*##) {
+                # File is copied from a file under the specified URL
+                # Check source exists
+                $is_newfile = 1 unless $cp_url->url_exists ($rev - 1);
+              } else {
+                # File copied from outside of the specified URL
+                $is_newfile = 1;
+
+                # Check branches can be determined
+                if ($url->branch and $cp_url->branch) {
+
+                  # Follow its history, stop on copy
+                  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 = %{ $cp_log{$cp_rev}{paths}{$cp_url->path} }
+                    if $cp_log{$cp_rev}{paths}{$cp_url->path};
+
+                  # Check whether the "first" revision is copied from elsewhere.
+                  if (exists $attrib{'copyfrom-path'}) {
+                    # If source exists in the specified URL, set up the copy
+                    my $cp_cp_url = FCM1::CmUrl->new (
+                      URL => $url->root . $attrib{'copyfrom-path'} . '@' .
+                             $attrib{'copyfrom-rev'},
+                    );
+                    if ($cp_cp_url->subdir()) {
+                      $cp_cp_url->url_peg(
+                        $cp_cp_url->project_url()
+                        . '/' . $url->branch()
+                        . '/' . $cp_cp_url->subdir()
+                        . '@' . $cp_cp_url->pegrev(),
+                      );
+                      if ($cp_cp_url->url_exists ($rev - 1)) {
+                        ($copyfrom_path = $cp_cp_url->path) =~ s#^$url_path/*##;
+                        # Check path is defined - if not it probably means the
+                        # branch doesn't follow the FCM naming convention
+                        $is_newfile = 0 if $copyfrom_path;
+                      }
+                    }
+                  }
+
+                  # Note: The logic above does not cover all cases. However, it
+                  # should do the right thing for the most common case. Even
+                  # where it gets it wrong the file contents should always be
+                  # correct even if the file history is not.
+                }
+              }
+
+              # Check whether file is copied from an excluded or copied path
+              if (not $is_newfile) {
+                for my $path (@exclude,@copied_dirs) {
+                  if ($copyfrom_path =~ m#^$path(?:/|$)#) {
+                    $is_newfile = 1;
+                    last;
+                  }
+                }
+              }
+
+              # Check whether file is copied from a file which has been replaced
+              if (not $is_newfile) {
+                my $copyfrom_fullpath = $url->branch_path . "/" . $copyfrom_path;
+                $is_newfile = 1 if ($log{$rev}{paths}{$copyfrom_fullpath}{action} and
+                                    $log{$rev}{paths}{$copyfrom_fullpath}{action} eq 'R');
+              }
+
+              # Copy the file, if required
+              push @before_script, 'svn copy ' . $copyfrom_path .  ' "' . $file . '"'
+                if not $is_newfile;
+            }
+
+          } else {
+            # History does not exist, must be a new file
+            if ($is_dir) {
+              # If it's a directory then create it and add it immediately
+              # (in case it contains any copied files)
+              push @before_script, 'mkdir "' . $file. '"';
+              push @before_script, 'svn add "' . $file . '"';
+            } else {
+              $is_newfile = 1;
+	    }
+          }
+
+          # Add the file, if required
+          if ($is_newfile) {
+            push @after_script, 'svn add "' . $file . '"';
+          }
+        }
+
+        if ($is_dir and $log{$rev}{paths}{$path}{action} eq 'R') {
+          # Subversion does not appear to support replacing a directory in a
+          # single transaction from a working copy (other than as the result
+          # of a merge). Therefore the delete of the old directory must be
+          # done in advance as a separate commit.
+          push @script, 'svn delete -m "Delete directory in preparation for' .
+            ' replacing it (part of ' . $organisation . '_changeset:' . $rev .
+            ')" $target/' . $file;
+          push @script, 'svn update --non-interactive';
+          # The replaced directory needs to be exported and added recursively
+          push @after_script, 'svn add "' . $file . '"';
+          $export_required = 1;
+          push @copied_dirs, $file;
+        }
+
+        if (not $is_dir and $log{$rev}{paths}{$path}{action} ne 'A') {
+          my ($was_symlink) = $SVN->stdout(
+            qw{svn propget svn:special},
+            sprintf("%s/%s@%d", $url->url(), $file, ($rev - 1)),
+          );
+          my ($is_symlink) = $SVN->stdout(
+            qw{svn propget svn:special}, $url_file,
+          );
+          if ($was_symlink and not $is_symlink) {
+            # A symbolic link has been changed to a normal file
+            push @before_script, 'svn propdel -q svn:special "' . $file . '"';
+            push @before_script, 'rm "' . $file . '"';
+	  } elsif ($log{$rev}{paths}{$path}{action} eq 'R') {
+            # Delete the old file and then add the new file
+            push @before_script, 'svn delete "' . $file . '"';
+            push @after_script, 'svn add "' . $file . '"';
+          } elsif ($is_symlink and not $was_symlink) {
+            # A normal file has been changed to a symbolic link
+            push @after_script, 'svn propset -q svn:special \* "' . $file . '"';
+          } elsif ($is_symlink and $was_symlink) {
+            # If a symbolic link has been modified then remove the old
+            # copy first to allow the copy to work
+            push @before_script, 'rm "' . $file . '"';
+          }
+        }
+
+        # Decide whether the file needs to be exported
+        if (not $is_dir) {
+          if (not $use_patch) {
+            $export_required = 1;
+          } else {
+            # Export the file if it is binary
+            my @file_diff = $SVN->stdout(
+              qw{svn diff --no-diff-deleted -c}, $rev, $url_file,
+            );
+            for (@file_diff) {
+              $export_required = 1 if /Cannot display: file marked as a binary type./;
+            }
+            # Only create a patch file if necessary
+            $patch_needed = 1 if not $export_required;
+          }
+        }
+
+        if ($export_required) {
+          # Download the file using "svn export"
+          my $export = File::Spec->catfile ($outdir_rev, $export_file);
+          $SVN->call(qw{export -q -r}, $rev, $url_file, $export);
+
+          # Copy the exported file into the file
+          push @before_script,
+               'cp -r ${fcm_patch_dir}/' . $export_file . ' "' . $file . '"';
+          $export_file++;
+        }
+      }
+    }
+
+    # Write the patch file
+    if ($patch_needed) {
+      my $patchfile = File::Spec->catfile ($outdir_rev, 'patchfile');
+      open FILE, '>', $patchfile
+        or die $patchfile, ': cannot open (', $!, ')';
+      print FILE $patch;
+      close FILE or die $patchfile, ': cannot close (', $!, ')';
+    }
+
+    # Add line break to each line in @before_script and @after_script
+    @before_script = map {($_ ? $_ . ' || exit 1' . "\n" : "\n")}
+                     @before_script if (@before_script);
+    @after_script  = map {($_ ? $_ . ' || exit 1' . "\n" : "\n")}
+                     @after_script if (@after_script);
+
+    # Write patch script to output
+    my $out = File::Spec->catfile ($outdir_rev, 'apply-patch');
+    open FILE, '>', $out or die $out, ': cannot open (', $!, ')';
+
+    # Script header
+    print FILE <<EOF;
+#!/usr/bin/env bash
+# ------------------------------------------------------------------------------
+# NAME
+#   apply-patch
+#
+# DESCRIPTION
+#   This script is generated automatically by the "fcm mkpatch" command. It
+#   applies the patch to the current working directory which must be a working
+#   copy of a valid project tree that can accept the import of the patches.
+#
+#   Patch created from $organisation URL: $u
+#   Changeset: $rev
+# ------------------------------------------------------------------------------
+
+this=`basename \$0`
+echo "\$this: Applying patch for changeset $rev."
+
+# Location of the patch, base on the location of this script
+cd `dirname \$0` || exit 1
+fcm_patch_dir=\$PWD
+
+# Change directory back to the working copy
+cd \$OLDPWD || exit 1
+
+# Check working copy does not have local changes
+status=`svn status`
+if [[ -n \$status ]]; then
+  echo "\$this: working copy contains changes, abort." >&2
+  exit 1
+fi
+if [[ -a "#commit_message#" ]]; then
+  echo "\$this: existing commit message in "#commit_message#", abort." >&2
+  exit 1
+fi
+
+# Apply the changes
+patch_command=\${patch_command:-"patch --no-backup-if-mismatch -p0"}
+EOF
+
+    # Script content
+    print FILE @before_script if @before_script;
+    print FILE "\$patch_command <\${fcm_patch_dir}/patchfile || exit 1\n"
+      if $patch_needed;
+    print FILE @after_script  if @after_script;
+
+    # Script footer
+    print FILE <<EOF;
+
+# Copy in the commit message
+cp \${fcm_patch_dir}/log-message "#commit_message#"
+
+echo "\$this: finished normally."
+#EOF
+EOF
+
+    close FILE or die $out, ': cannot close (', $!, ')';
+
+    # Add executable permission
+    chmod 0755, $out;
+
+    # Script to commit the change
+    push @script, '${fcm_patches_dir}/' . $rev . '/apply-patch';
+    push @script, 'svn commit -F "#commit_message#"';
+    push @script, 'rm -f "#commit_message#"';
+    push @script, 'svn update --non-interactive';
+    push @script, '';
+
+    $CLI_MESSAGE->('PATCH_REV', $rev);
+  }
+
+  # Write the main output 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;
+#!/usr/bin/env bash
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm-import-patch
+#
+# SYNOPSIS
+#   fcm-import-patch TARGET
+#
+# DESCRIPTION
+#   This script is generated automatically by the "fcm mkpatch" command, as are
+#   the revision "patches" created in the same directory. 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:=/tmp}/\$this.XXXXXX`
+  fcm_working_copy=\$fcm_tmp_dir
+  svn checkout -q \$target \$fcm_working_copy || exit 1
+else
+  fcm_working_copy=\$target
+  target=`svn info \$fcm_working_copy | grep "^URL: " | sed 's/URL: //'` || exit 1
+fi
+
+# Location of the patches, base on the location of this script
+cd `dirname \$0` || exit 1
+fcm_patches_dir=\$PWD
+
+# Change directory to the working copy
+cd \$fcm_working_copy || exit 1
+
+# Set the language to avoid encoding problems
+if locale -a | grep -q en_GB\$; then
+  export LANG=en_GB
+fi
+
+# Commands to apply patches
+EOF
+
+    # Script content
+    print FILE @script;
+
+    # Script footer
+    print FILE <<EOF;
+# Check working copy does not have local changes
+status=`svn status`
+if [[ -n \$status ]]; then
+  echo "\$this: working copy still contains changes, abort." >&2
+  exit 1
+fi
+
+# 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
+    $CLI_MESSAGE->('PATCH_DONE', $outdir);
+
+  } else {
+    # Remove output directory
+    rmtree $outdir or die $outdir, ': cannot remove';
+
+    # Diagnostic
+    return _cm_abort(FCM1::Cm::Abort->NULL);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# CLI error.
+sub _cli_err {
+    my ($key, @args) = @_;
+    my $message = sprintf($CLI_MESSAGE_FOR_ERROR{$key}, @args);
+    die(FCM1::CLI::Exception->new({message => $message}));
+}
+
+# ------------------------------------------------------------------------------
+# The default handler of the "WC_STATUS" event.
+sub _cli_handler_of_wc_status {
+    my ($name, $target_list_ref, $status_list_ref) = @_;
+    $target_list_ref ||= [q{.}];
+    if (@{$status_list_ref}) {
+        $CLI_MESSAGE->(
+            'STATUS',
+            $name,
+            q{"} . join(q{", "}, @{$target_list_ref}) . q{"},
+            join("\n", @{$status_list_ref}),
+        );
+        if ($CLI_PROMPT->($name, 'CONTINUE', $name) ne 'y') {
+            return _cm_abort();
+        }
+    }
+    return @{$status_list_ref};
+}
+
+# ------------------------------------------------------------------------------
+# The default handler of the "WC_STATUS_PATH" event.
+sub _cli_handler_of_wc_status_path {
+    my ($name, $target_list_ref, $status_list_ref) = @_;
+    my $message
+        = @{$status_list_ref} ? (join("\n", @{$status_list_ref}) . "\n") : q{};
+    $CLI_MESSAGE->(q{}, $message);
+    my @paths = map {chomp(); ($_ =~ $PATTERN_OF{ST_PATH})} @{$status_list_ref};
+    my @paths_of_interest;
+    while (my $path = shift(@paths)) {
+        my %handler_of = (
+            a => sub {push(@paths_of_interest, $path, @paths); @paths = ()},
+            n => sub {},
+            y => sub {push(@paths_of_interest, $path)},
+        );
+        my $reply = $CLI_PROMPT->(
+            {type => 'yna'}, $name, 'RUN_SVN_COMMAND', "$name $path",
+        );
+        $handler_of{$reply}->();
+    }
+    return @paths_of_interest;
+}
+
+# ------------------------------------------------------------------------------
+# Expands location keywords in a list.
+sub _cli_keyword_expand_url {
+    my ($arg_list_ref) = @_;
+    ARG:
+    for my $arg (@{$arg_list_ref}) {
+        my ($label, $value) = ($arg =~ $PATTERN_OF{CLI_OPT});
+        if (!$label) {
+            ($label, $value) = (q{}, $arg);
+        }
+        if (!$value) {
+            next ARG;
+        }
+        eval {
+            $value = FCM1::Util::tidy_url(FCM1::Keyword::expand($value));
+        };
+        if ($@) {
+            if ($value ne 'fcm:revision') {
+                die($@);
+            }
+        }
+        $arg = $label . $value;
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Expands revision keywords in -r and --revision options in a list.
+sub _cli_keyword_expand_rev {
+    my ($arg_list_ref) = @_;
+    my @targets;
+    for my $arg (@{$arg_list_ref}) {
+        if (-e $arg && is_wc($arg) || is_url($arg)) {
+            push(@targets, $arg);
+        }
+    }
+    if (!@targets) {
+        push(@targets, get_url_of_wc());
+    }
+    if (!@targets) {
+        return;
+    }
+    my @old_arg_list = @{$arg_list_ref};
+    my @new_arg_list = ();
+    ARG:
+    while (defined(my $arg = shift(@old_arg_list))) {
+        my ($key, $value) = $arg =~ $PATTERN_OF{CLI_OPT_REV};
+        if (!$key) {
+            push(@new_arg_list, $arg);
+            next ARG;
+        }
+        push(@new_arg_list, '--revision');
+        if (!$value) {
+            $value = shift(@old_arg_list);
+        }
+        my @revs = grep {defined()} ($value =~ $PATTERN_OF{CLI_OPT_REV_RANGE});
+        my ($url, @url_list) = @targets;
+        for my $rev (@revs) {
+            if ($rev !~ $PATTERN_OF{SVN_REV}) {
+                $rev = (FCM1::Keyword::expand($url, $rev))[1];
+            }
+            if (@url_list) {
+                $url = shift(@url_list);
+            }
+        }
+        push(@new_arg_list, join(q{:}, @revs));
+    }
+    @{$arg_list_ref} = @new_arg_list;
+}
+
+# ------------------------------------------------------------------------------
+# Prints a message.
+sub _cli_message {
+    my ($key, @args) = @_;
+    for (
+        [\*STDOUT, \%CLI_MESSAGE_FOR        , q{}          ],
+        [\*STDERR, \%CLI_MESSAGE_FOR_WARNING, q{[WARNING] }],
+        [\*STDERR, \%CLI_MESSAGE_FOR_ABORT  , q{[ABORT] }  ],
+        [\*STDERR, \%CLI_MESSAGE_FOR_ERROR  , q{[ERROR] }  ],
+    ) {
+        my ($handle, $hash_ref, $prefix) = @{$_};
+        if (exists($hash_ref->{$key})) {
+            return printf({$handle} $prefix . $hash_ref->{$key}, @args);
+        }
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Wrapper for FCM1::Interactive::get_input.
+sub _cli_prompt {
+    my %option
+        = (type => 'yn', default => 'n', (ref($_[0]) ? %{shift(@_)} : ()));
+    my ($name, $key, @args) = @_;
+    return FCM1::Interactive::get_input(
+        title   => $CLI_PROMPT_PREFIX . $name,
+        message => sprintf($CLI_MESSAGE_FOR_PROMPT{$key}, @args),
+        %option,
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Check missing status and delete.
+sub cm_check_missing {
+    my %option = %{shift()};
+    my $checker
+        = _svn_status_checker('delete', 'MISSING', $option{st_check_handler});
+    my @paths = $checker->(\@_);
+    if (@paths) {
+        $SVN->call('delete', @paths);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Check unknown status and add.
+sub cm_check_unknown {
+    my %option = %{shift()};
+    my $checker
+        = _svn_status_checker('add', 'UNKNOWN', $option{st_check_handler});
+    my @paths = $checker->(\@_);
+    if (@paths) {
+        $SVN->call('add', @paths);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# FCM wrapper to SVN switch.
+sub cm_switch {
+    my %option = %{shift()};
+    my ($source, $path) = @_;
+    $path ||= cwd();
+    if (!$source) {
+        return _cm_err(FCM1::Cm::Exception->INVALID_TARGET, q{});
+    }
+    if (!-e $path) {
+        return _cm_err(FCM1::Cm::Exception->NOT_EXIST, $path);
+    }
+    if (!is_wc($path)) {
+        return _cm_err(FCM1::Cm::Exception->INVALID_WC, $path);
+    }
+
+    # Check for merge template in the commit log file in the working copy
+    my $path_of_wc = $SVN->get_wc_root($path);
+    my $commit_message_file = $COMMIT_MESSAGE_UTIL->path($path_of_wc);
+    my $commit_message_ctx = $COMMIT_MESSAGE_UTIL->load($commit_message_file);
+    if ($commit_message_ctx->get_auto_part()) {
+        return _cm_err(
+            FCM1::Cm::Exception->SWITCH_UNSAFE,
+            ($path eq $path_of_wc
+                ? File::Spec->abs2rel($commit_message_file)
+                : $commit_message_file
+            ),
+        );
+    }
+
+    # Check for any local modifications
+    if (defined($option{st_check_handler})) {
+        _svn_status_checker('switch', 'MODIFIED', $option{st_check_handler})->(
+            [$path_of_wc],
+        );
+    }
+
+    my @targets = $path_of_wc eq cwd() ? () : ($path_of_wc);
+    $SVN->call('cleanup', @targets);
+    $SVN->call(
+        'switch',
+        '--non-interactive',
+        ($option{revision} ? ('-r', $option{revision}) : ()),
+        ($option{quiet}    ? '--quiet'                 : ()),
+        _cm_get_source(
+            $source,
+            FCM1::CmBranch->new(URL => $path_of_wc),
+        )->url_peg(),
+        @targets,
+    );
+}
+
+# ------------------------------------------------------------------------------
+# FCM wrapper to SVN update.
+sub cm_update {
+    my %option = %{shift()};
+    my @targets = @_;
+    if (!@targets) {
+        @targets = (cwd());
+    }
+    for my $target (@targets) {
+        if (!-e $target) {
+            return _cm_err(FCM1::Cm::Exception->NOT_EXIST, $target);
+        }
+        if (!is_wc($target)) {
+            return _cm_err(FCM1::Cm::Exception->INVALID_WC, $target);
+        }
+        $target = $SVN->get_wc_root($target);
+        if ($target eq cwd()) {
+            $target = q{.};
+        }
+    }
+    if (defined($option{st_check_handler})) {
+        my ($matcher_keys_ref, $show_updates)
+            = defined($option{revision}) ? (['MODIFIED'       ], undef)
+            :                              (['MODIFIED', 'OOD'], 1    )
+            ;
+        my $matcher = sub {
+            for my $key (@{$matcher_keys_ref}) {
+                $ST_MATCHER_FOR{$key}->(@_) && return 1;
+            }
+        };
+        _svn_status_checker(
+            'update', $matcher, $option{st_check_handler}, $show_updates,
+        )->(\@targets);
+    }
+    if ($option{revision} && $option{revision} !~ $PATTERN_OF{SVN_REV}) {
+        $option{revision} = (
+            FCM1::Keyword::expand(get_url_of_wc($targets[0]), $option{revision})
+        )[1];
+    }
+    _svn_update(\@targets, \%option);
+}
+
+# ------------------------------------------------------------------------------
+# Raises an abort exception.
+sub _cm_abort {
+    my ($code) = @_;
+    $code ||= FCM1::Cm::Abort->USER;
+    die(bless({code => $code, message => 'abort'}, 'FCM1::Cm::Abort'));
+}
+
+# ------------------------------------------------------------------------------
+# Raises a failure.
+sub _cm_err {
+    my ($code, @targets) = @_;
+    die(bless(
+        {code => $code, message => "ERROR: $code", targets => \@targets},
+        'FCM1::Cm::Exception',
+    ));
+}
+
+# ------------------------------------------------------------------------------
+# Return a corresponding FCM1::CmBranch instance for $source_url w.r.t. $target.
+sub _cm_get_source {
+    my ($source_url, $target) = @_;
+    if (!$UTIL->uri_match($source_url)) {
+        # Source not full URL, construct source URL based on target URL
+        my ($path, $peg) = $source_url =~ qr{\A(.*?)(@[^@/]+)?\z}msx;
+        my $project = $target->project_path();
+        if (index($path, $project . '/') == 0) {
+            # $path contains the full path under the repository root
+            $path = substr($path, length($project));
+        }
+        my %layout_config = %{$target->layout()->get_config()};
+        if (!grep {!defined($layout_config{"dir-$_"})} qw{trunk branch tag}) {
+            # $path must be under the specified sub-directories for the trunk,
+            # branches and tags
+            my @dirs = map {$layout_config{"dir-$_"}} qw{trunk branch tag};
+            my @paths = split(qr{/+}msx, $path);
+            if (!@paths || !grep {$_ eq $paths[0]} @dirs) {
+                $path = $layout_config{'dir-branch'} . '/' . $path;
+            }
+        }
+        $peg ||= q{};
+        $source_url = join('/', $target->project_url(), $path) . $peg;
+    }
+    my $source = FCM1::CmBranch->new(URL => $source_url);
+    my $layout = eval {$source->layout()};
+    if ($@) {
+        $@ = undef;
+        return _cm_err(FCM1::Cm::Exception->INVALID_URL, $source_url);
+    }
+    if (!$layout->get_branch()) {
+        return _cm_err(FCM1::Cm::Exception->INVALID_BRANCH, $source_url);
+    }
+    $source->url_peg(
+        $source->branch_url()
+        . ($target->subdir() ? '/' . $target->subdir() : q{})
+        . ('@' . $source->pegrev())
+    );
+    # Ensure that the source and target URLs are in the same project
+    if ($source->project_url() ne $target->project_url()) {
+        return _cm_err(
+            FCM1::Cm::Exception->DIFF_PROJECTS,
+            $target->url_peg(),
+            $source->url_peg(),
+        );
+    }
+    return $source;
+}
+
+# ------------------------------------------------------------------------------
+# Returns the results of "svn status".
+sub _svn_status_get {
+    my ($targets_ref, $show_updates) = @_;
+    my @targets = (defined($targets_ref) ? @{$targets_ref} : ());
+    for my $target (@targets) {
+        if ($target eq cwd()) {
+            $target = q{.};
+        }
+    }
+    my @options = ($show_updates ? qw{--show-updates} : ());
+    $SVN->stdout(qw{svn status --ignore-externals}, @options, @targets);
+}
+
+# ------------------------------------------------------------------------------
+# Returns a "svn status" checker.
+sub _svn_status_checker {
+    my ($name, $matcher, $handler, $show_updates) = @_;
+    if (!ref($matcher)) {
+        $matcher = $ST_MATCHER_FOR{$matcher};
+    }
+    my $P = $PATTERN_OF{ST_PATH};
+    sub {
+        my ($targets_ref) = @_;
+        my @status = _svn_status_get($targets_ref, $show_updates);
+        if ($show_updates) {
+            @status = map {$_ =~ $PATTERN_OF{ST_AGAINST_REV} ? () : $_} @status;
+        }
+        my @status_of_interest = grep {$matcher->($_)} @status;
+        # Note: for future expansion...
+        #my @paths;
+        #if (!$show_updates) {
+        #    @paths = map {chomp(); $_} map {($_ =~ $P)} @status_of_interest;
+        #}
+        #defined($handler)
+        #? $handler->($name, $targets_ref, \@status_of_interest, \@paths)
+        #: @status_of_interest
+        #;
+        defined($handler)
+        ? $handler->($name, $targets_ref, \@status_of_interest)
+        : @status_of_interest
+        ;
+    };
+}
+
+# ------------------------------------------------------------------------------
+# Runs "svn update".
+sub _svn_update {
+    my ($targets_ref, $option_hash_ref) = @_;
+    my %option = (defined($option_hash_ref) ? %{$option_hash_ref} : ());
+    my @targets = defined($targets_ref) ? @{$targets_ref} : ();
+    $SVN->call('cleanup', @targets);
+    $SVN->call(
+        'update',
+        '--non-interactive',
+        ($option{revision} ? ('-r', $option{revision}) : ()),
+        ($option{quiet}    ? '--quiet'                 : ()),
+        @targets,
+    );
+}
+
+# ------------------------------------------------------------------------------
+# CLI exception.
+package FCM1::CLI::Exception;
+use base qw{FCM1::Exception};
+
+# ------------------------------------------------------------------------------
+# Abort exception.
+package FCM1::Cm::Abort;
+use base qw{FCM1::Exception};
+use constant {FAIL => 'FAIL', NULL => 'NULL', USER => 'USER'};
+
+sub get_code {
+    return $_[0]->{code};
+}
+
+# ------------------------------------------------------------------------------
+# Resource exception.
+package FCM1::Cm::Exception;
+our @ISA = qw{FCM1::Cm::Abort};
+use constant {
+    CHDIR             => 'CHDIR',
+    DIFF_PROJECTS     => 'DIFF_PROJECTS',
+    INVALID_BRANCH    => 'INVALID_BRANCH',
+    INVALID_PROJECT   => 'INVALID_PROJECT',
+    INVALID_TARGET    => 'INVALID_TARGET',
+    INVALID_URL       => 'INVALID_URL',
+    INVALID_WC        => 'INVALID_WC',
+    MERGE_REV_INVALID => 'MERGE_REV_INVALID',
+    MERGE_SELF        => 'MERGE_SELF',
+    MERGE_UNRELATED   => 'MERGE_UNRELATED',
+    MERGE_UNSAFE      => 'MERGE_UNSAFE',
+    MKPATH            => 'MKPATH',
+    NOT_EXIST         => 'NOT_EXIST',
+    PARENT_NOT_EXIST  => 'PARENT_NOT_EXIST',
+    RMTREE            => 'RMTREE',
+    SWITCH_UNSAFE     => 'SWITCH_UNSAFE',
+    WC_INVALID_BRANCH => 'WC_INVALID_BRANCH',
+    WC_URL_NOT_EXIST  => 'WC_URL_NOT_EXIST',
+};
+
+sub get_targets {
+    return @{$_[0]->{targets}};
+}
+
+1;
+__END__
+
+=pod
+
+=head1 NAME
+
+FCM1::Cm
+
+=head1 SYNOPSIS
+
+    use FCM1::Cm qw{cm_check_missing cm_check_unknown cm_switch cm_update};
+
+    # Checks status for "missing" items and "svn delete" them
+    $missing_st_handler = sub {
+        my ($name, $targets_ref, $status_list_ref) = @_;
+        # ...
+        return @paths_of_interest;
+    };
+    cm_check_missing({st_check_handler => $missing_st_handler}, @targets);
+
+    # Checks status for "unknown" items and "svn add" them
+    $unknown_st_handler = sub {
+        my ($name, $targets_ref, $status_list_ref) = @_;
+        # ...
+        return @paths_of_interest;
+    };
+    cm_check_unknown({st_check_handler => $unknown_st_handler}, @targets);
+
+    # Sets up a status checker
+    $st_check_handler = sub {
+        my ($name, $targets_ref, $status_list_ref) = @_;
+        # ...
+    };
+    # Switches a "working copy" at the "root" level to a new URL target
+    cm_switch(
+        {
+            'non-interactive'  => $non_interactive_flag,
+            'quiet'            => $quiet_flag,
+            'revision'         => $revision,
+            'st_check_handler' => $st_check_handler,
+        },
+        $target, $path_of_wc,
+    );
+    # Runs "svn update" on each working copy from their "root" level
+    cm_update(
+        {
+            'non-interactive'  => $non_interactive_flag,
+            'quiet'            => $quiet_flag,
+            'revision'         => $revision,
+            'st_check_handler' => $st_check_handler,
+        },
+        @targets,
+    );
+
+=head1 DESCRIPTION
+
+Wraps the Subversion client and implements other FCM code management
+functionalities.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item cm_check_missing(\%option,@targets)
+
+Use "svn status" to check for missing items in @targets. If @targets is an empty
+list, the function adds the current working directory to it. Expects
+$option{st_check_handler} to be a CODE reference. Calls
+$option{st_check_handler} with ($name, $targets_ref, $status_list_ref) where
+$name is "delete", $targets_ref is \@targets, and $status_list_ref is an
+ARRAY reference to a list of "svn status" output with the "missing" status.
+$option{st_check_handler} should return a list of interesting paths, which will
+be scheduled for removal using "svn delete".
+
+=item cm_check_unknown(\%option,@targets)
+
+Similar to cm_check_missing(\%option,@targets) but checks for "unknown" items,
+which will be scheduled for addition using "svn add".
+
+=item cm_switch(\%option,$target,$path_of_wc)
+
+Invokes "svn switch" at the root of a working copy specified by $path_of_wc (or
+the current working directory if $path_of_wc is not specified).
+$option{'non-interactive'}, $option{quiet}, $option{revision} determines the
+options (of the same name) that are passed to "svn switch". If
+$option{st_check_handler} is set, it should be a CODE reference, and will be
+called with ('switch', [$path_of_wc], $status_list_ref), where $status_list_ref
+is an ARRAY reference to the output returned by "svn status" on $path_of_wc.
+This can be used for the application to display the working copy status to the
+user before prompting him/her to continue. The return value of
+$option{st_check_handler} is ignored.
+
+=item cm_update(\%option,@targets)
+
+Invokes "svn update" at the root of each working copy specified by @targets. If
+@targets is an empty list, the function adds the current working directory to
+it. $option{'non-interactive'}, $option{quiet}, $option{revision} determines the
+options (of the same name) that are passed to "svn update". If
+$option{st_check_handler} is set, it should be a CODE reference, and will be
+called with ($name, $targets_ref, $status_list_ref), where $name is
+'update', $targets_ref is \@targets and $status_list_ref is an ARRAY
+reference to the output returned by "svn status -u" on the @targets. This can be
+used for the application to display the working copy update status to the user
+before prompting him/her to continue. The return value of
+$option{st_check_handler} is ignored.
+
+=back
+
+=head1 DIAGNOSTICS
+
+The following exceptions can be raised:
+
+=over 4
+
+=item FCM1::Cm::Abort
+
+This exception @ISA L<FCM1::Exception|FCM1::Exception>. It is raised if a command
+is aborted for some reason. The $e->get_code() method can be used to retrieve an
+error code, which can be one of the following:
+
+=over 4
+
+=item $e->FAIL
+
+The command aborts because of a failure.
+
+=item $e->NULL
+
+The command aborts because it will result in no change.
+
+=item $e->USER
+
+The command aborts because of an action by the user.
+
+=back
+
+=item FCM1::Cm::Exception
+
+This exception @ISA L<FCM1::Abort|FCM1::Abort>. It is raised if a command fails
+with a known reason. The $e->get_targets() method can be used to retrieve a list
+of targets/resources associated with this exception. The $e->get_code() method
+can be used to retrieve an error code, which can be one of the following:
+
+=over 4
+
+=item $e->CHDIR
+
+Fails to change directory to a target.
+
+=item $e->INVALID_BRANCH
+
+A target is not a valid branch URL in the standard FCM project layout.
+
+=item $e->INVALID_PROJECT
+
+A target is not a valid project URL in the standard FCM project layout.
+
+=item $e->INVALID_TARGET
+
+A target is not a valid Subversion URL or working copy.
+
+=item $e->INVALID_URL
+
+A target is not a valid Subversion URL.
+
+=item $e->INVALID_WC
+
+A target is not a valid Subversion working copy.
+
+=item $e->MERGE_REV_INVALID
+
+An invalid revision (target element 0) is specified for a merge.
+
+=item $e->MERGE_SELF
+
+Attempt to merge a URL (target element 0) to its own working copy (target
+element 1).
+
+=item $e->MERGE_UNRELATED
+
+The merge target (target element 0) is not directly related to the merge source
+(target element 1).
+
+=item $e->MERGE_UNSAFE
+
+A merge source (target element 0) contains changes outside the target
+sub-directory.
+
+=item $e->MKPATH
+
+Fail to create a directory (target element 0) recursively.
+
+=item $e->NOT_EXIST
+
+A target does not exist.
+
+=item $e->PARENT_NOT_EXIST
+
+The parent of the target no longer exists.
+
+=item $e->RMTREE
+
+Fail to remove a directory (target element 0) recursively.
+
+=item $e->SWITCH_UNSAFE
+
+A merge template exists in the commit message file (target element 0) in a
+working copy target.
+
+=item $e->WC_INVALID_BRANCH
+
+The URL of the target working copy is not a valid branch URL in the standard FCM
+project layout.
+
+=item $e->WC_URL_NOT_EXIST
+
+The URL of the target working copy no longer exists at the HEAD revision.
+
+=back
+
+=back
+
+=head1 TO DO
+
+Reintegrate with L<FCM1::CmUrl|FCM1::CmUrl> and L<FCM1::CmBranch|FCM1::CmBranch>,
+but separate this module into the CLI part and the CM part. Expose the remaining
+CM functions when this is done.
+
+Use L<SVN::Client|SVN::Client> to interface with Subversion.
+
+Move C<mkpatch> out of this module.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
+opy already exists.
+
+=item $e->WC_INVALID_BRANCH
+
+The URL of the target working copy is not a valid branch URL in the standard FCM
+project layout.
+
+=item $e->WC_URL_NOT_EXIST
+
+The URL of the target working copy no longer exists at the HEAD revision.
+
+=back
+
+=back
+
+=head1 TO DO
+
+Reintegrate with L<FCM1::CmUrl|FCM1::CmUrl> and L<FCM1::CmBranch|FCM1::CmBranch>,
+but separate this module into the CLI part and the CM part. Expose the remaining
+CM functions when this is done.
+
+Use L<SVN::Client|SVN::Client> to interface with Subversion.
+
+Move C<mkpatch> out of this module.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
+$e->CHDIR
+
+Fails to change directory to a target.
+
+=item $e->INVALID_BRANCH
+
+A target is not a valid branch URL in the standard FCM project layout.
+
+=item $e->INVALID_PROJECT
+
+A target is not a valid project URL in the standard FCM project layout.
+
+=item $e->INVALID_TARGET
+
+A target is not a valid Subversion URL or working copy.
+
+=item $e->INVALID_URL
+
+A target is not a valid Subversion URL.
+
+=item $e->INVALID_WC
+
+A target is not a valid Subversion working copy.
+
+=item $e->MERGE_REV_INVALID
+
+An invalid revision (target element 0) is specified for a merge.
+
+=item $e->MERGE_SELF
+
+Attempt to merge a URL (target element 0) to its own working copy (target
+element 1).
+
+=item $e->MERGE_UNRELATED
+
+The merge target (target element 0) is not directly related to the merge source
+(target element 1).
+
+=item $e->MERGE_UNSAFE
+
+A merge source (target element 0) contains changes outside the target
+sub-directory.
+
+=item $e->MKPATH
+
+Fail to create a directory (target element 0) recursively.
+
+=item $e->NOT_EXIST
+
+A target does not exist.
+
+=item $e->PARENT_NOT_EXIST
+
+The parent of the target no longer exists.
+
+=item $e->RMTREE
+
+Fail to remove a directory (target element 0) recursively.
+
+=item $e->SWITCH_UNSAFE
+
+A merge template exists in the commit message file (target element 0) in a
+working copy target.
+
+=item $e->WC_INVALID_BRANCH
+
+The URL of the target working copy is not a valid branch URL in the standard FCM
+project layout.
+
+=item $e->WC_URL_NOT_EXIST
+
+The URL of the target working copy no longer exists at the HEAD revision.
+
+=back
+
+=back
+
+=head1 TO DO
+
+Migrate to FCM::System hierarchy.
+
+Move C<mkpatch> out of this module.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CmBranch.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CmBranch.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CmBranch.pm	(revision 5095)
@@ -0,0 +1,1009 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::CmBranch
+#
+# DESCRIPTION
+#   This class contains methods for manipulating a branch. It is a sub-class of
+#   FCM1::CmUrl, and inherits all methods from that class.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::CmBranch;
+use base qw{FCM1::CmUrl};
+
+use strict;
+use warnings;
+
+use FCM1::Config;
+use FCM1::Interactive;
+use FCM1::Keyword;
+use FCM1::Util qw/e_report w_report 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 FCM1::CmBranch
+  'ANCESTOR',    # list of common ancestors with other branches
+                 # key = URL, value = ancestor FCM1::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
+);
+
+# Set the commit message utility provided by FCM::System::CM.
+our $COMMIT_MESSAGE_UTIL;
+sub set_commit_message_util {
+    $COMMIT_MESSAGE_UTIL = shift();
+}
+
+# Set the SVN utility provided by FCM::System::CM.
+our $SVN;
+sub set_svn_util {
+    $SVN = shift();
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch = FCM1::CmBranch->new (URL => $url,);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::CmBranch class.
+#
+# ARGUMENTS
+#   URL    - URL of a branch
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::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 FCM1::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} = FCM1::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 = FCM1::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) = @_;
+  !$self->is_trunk()
+    && $self->parent()->url() eq $branch->url()
+    && (!$branch->is_branch() || $self->create_rev() >= $branch->create_rev());
+}
+
+# ------------------------------------------------------------------------------
+# 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) = @_;
+
+  $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}};
+  }
+
+  my @url_peg_list = $self->branch_list();
+  URL:
+  for my $url_peg (@url_peg_list) {
+    my ($url, $peg) = $SVN->split_by_peg($url_peg);
+    # Ignore URL of current branch and its parent
+    if ( $url eq $self->url()
+         # Ignore parent
+      || $self->is_branch() && $url eq $self->parent()->url()
+         # Ignore the other type of relatives
+      || exists($other_list{$url})
+    ) {
+      next URL;
+    }
+
+    my $branch = FCM1::CmBranch->new(URL => $url_peg);
+
+    # Test whether $branch is a relative we are looking for
+    my $can_return = $relation eq 'CHILDREN'
+      ? $branch->is_child_of($self) : $branch->is_sibling_of($self);
+    if ($can_return) {
+      push(@{$self->{$relation}}, $branch);
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @children = $cm_branch->children;
+#
+# DESCRIPTION
+#   This method returns a list of children (FCM1::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 (FCM1::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 FCM1::CmBranch object) of a
+#   specified $branch and the current branch. The argument $branch must be a
+#   FCM1::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 FCM1::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 FCM1::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} = [
+          FCM1::CmBranch->new (URL => $cr->url . '@' . $rev), # target
+          FCM1::CmBranch->new (URL => $self->root . $u_path), # delta upper
+          FCM1::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 FCM1::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;
+
+  my $return = 1;
+  if ($branch->pegrev > $rev) {
+    # Use "svn diff --summarize" to work out what's changed between last
+    # merge/ancestor and current revision
+    my $range = $branch->pegrev . ':' . $rev;
+    my @out = $SVN->stdout(
+        qw{svn diff --summarize -r}, $range, $branch->url_peg(),
+    );
+
+    # Returns false if there are changes outside of $subdir
+    my $url = join ('/', $branch->url, $subdir);
+    for my $line (@out) {
+      chomp $line;
+      $line = substr ($line, 7); # file name begins at column 7
+      if ($line !~ m#^$url(?:/|$)#) {
+        $return = 0;
+        last;
+      }
+    }
+  }
+
+  return $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;
+
+  # Create a temporary file for the commit log message
+  my $temp_handle = $self->_commit_message(
+    sprintf("Deleted %s.\n", $self->branch_path()), 'D', $non_interactive,
+  );
+
+  # Check with the user to see if he/she wants to go ahead
+  # ----------------------------------------------------------------------------
+  if (!$non_interactive) {
+    my $mesg = '';
+    if ($self->branch_owner() && !$self->layout()->is_owned_by_user()) {
+      $mesg .= "\n";
+
+      if (exists $FCM1::CmUrl::owner_keywords{$self->branch_owner()}) {
+        my $type = $FCM1::CmUrl::owner_keywords{$self->branch_owner()};
+        $mesg .= '*** WARNING: YOU ARE DELETING A ' . uc ($type) .
+                 ' BRANCH.';
+
+      } else {
+        $mesg .= '*** WARNING: YOU ARE DELETING A BRANCH NOT OWNED BY YOU.';
+      }
+
+      $mesg .= "\n" .
+               '*** Please ensure that you have the owner\'s permission.' .
+               "\n\n";
+    }
+
+    $mesg   .= 'Would you like to go ahead and delete this branch?';
+
+    my $reply = FCM1::Interactive::get_input (
+      title   => 'fcm branch',
+      message => $mesg,
+      type    => 'yn',
+      default => 'n',
+    );
+
+    return unless $reply eq 'y';
+  }
+
+  # Delete branch if answer is "y" for "yes"
+  # ----------------------------------------------------------------------------
+  print 'Deleting branch ', $self->url, ' ...', "\n";
+  $SVN->call(
+    'delete',
+    '-F', $temp_handle->filename(),
+    (defined $password    ? ('--password', $password) : ()),
+    ($svn_non_interactive ? '--non-interactive'       : ()),
+    $self->url(),
+  );
+
+  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 (
+    ['URL',                 'url'            ],
+    ['Repository Root',     'repository:root'],
+    ['Revision',            'revision'       ],
+    ['Last Changed Author', 'commit:author'  ],
+    ['Last Changed Rev',    'commit:revision'],
+    ['Last Changed Date',   'commit:date'    ],
+  ) {
+    my ($key, $flag) = @{$_};
+    if ($self->svninfo(FLAG => $flag)) {
+      printf("%s: %s\n", $key, $self->svninfo(FLAG => $flag));
+    }
+  }
+
+  if ($self->config->verbose) {
+    # Verbose mode, print log message at last changed revision
+    my %log = $self->svnlog (REV => $self->svninfo(FLAG => 'commit:revision'));
+    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 = FCM1::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, 'Searching 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, 'Searching 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 (sort 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 (sort keys %avail) {
+      next unless @{ $avail{$key} };
+
+      $return .= $indent . $key . ' ' . join (' ', @{ $avail{$key} }) . "\n";
+    }
+  }
+
+  return $return;
+}
+
+# Returns a File::Temp object containing the commit log for create/del.
+sub _commit_message {
+    my ($self, $message, $action, $non_interactive) = @_;
+    my $commit_message_ctx = $COMMIT_MESSAGE_UTIL->ctx();
+    $commit_message_ctx->set_auto_part($message);
+    $commit_message_ctx->set_info_part(
+        sprintf("%s    %s\n", $action, $self->url())
+    );
+    if (!$non_interactive) {
+        $COMMIT_MESSAGE_UTIL->edit($commit_message_ctx);
+    }
+    $COMMIT_MESSAGE_UTIL->notify($commit_message_ctx);
+    $COMMIT_MESSAGE_UTIL->temp($commit_message_ctx);
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CmUrl.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CmUrl.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/CmUrl.pm	(revision 5095)
@@ -0,0 +1,645 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::CmUrl
+#
+# DESCRIPTION
+#   This class contains methods for manipulating a Subversion URL in a standard
+#   FCM project.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::CmUrl;
+use base qw{FCM1::Base};
+
+use strict;
+use warnings;
+
+use FCM::System::Exception;
+use FCM1::Keyword;
+use FCM1::Util qw/svn_date/;
+
+# Special branches
+our %owner_keywords = (Share => 'shared', Config => 'config', Rel => 'release');
+
+# Revision pattern
+my $rev_pattern = '\d+|HEAD|BASE|COMMITTED|PREV|\{.+\}';
+
+my $E = 'FCM::System::Exception';
+
+# "svn log --xml" handlers.
+# -> element node start tag handlers
+my %SVN_LOG_ELEMENT_0_HANDLER_FOR = (
+#   tag        => handler
+    'logentry' => \&_svn_log_handle_element_0_logentry,
+    'path'     => \&_svn_log_handle_element_0_path,
+);
+# -> text node (after a start tag) handlers
+my %SVN_LOG_TEXT_HANDLER_FOR = (
+#   tag    => handler
+    'date' => \&_svn_log_handle_text_date,
+    'path' => \&_svn_log_handle_text_path,
+);
+
+# Set the SVN utility provided by FCM::System::CM.
+our $SVN;
+sub set_svn_util {
+    $SVN = shift();
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_url = FCM1::CmUrl->new ([URL => $url,]);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::CmUrl class.
+#
+# ARGUMENTS
+#   URL - URL of a branch
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  $self->{URL} = (exists $args{URL} ? $args{URL} : '');
+
+  for (qw/LAYOUT BRANCH_LIST INFO LIST LOG LOG_RANGE RLIST/) {
+    $self->{$_} = undef;
+  }
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# 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/LAYOUT 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 =~ qr{^[\w\+\-]+://}msx);
+}
+
+# ------------------------------------------------------------------------------
+# 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 $url = eval {$self->svninfo(FLAG => 'url', REV => $rev)};
+  if ($@) {
+    $@ = undef;
+  }
+
+  defined($url);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $cm_url->svninfo([FLAG => $flag], [REV => $rev]);
+#
+# DESCRIPTION
+#   Returns the value of $flag, where $flag is a field returned by "svn info
+#   --xml". The original hierarchy below the entry element is delimited by a
+#   colon in the name. (If $flag is not set, default to "url".) If REV is
+#   specified, it will be used as the operative revision.
+# ------------------------------------------------------------------------------
+
+sub svninfo {
+  my ($self, %args) = @_;
+  if (!$self->is_url()) {
+    return;
+  }
+  my $flag = exists($args{FLAG}) ? $args{FLAG} : 'url';
+  my $rev  = exists($args{REV})  ? $args{REV}  : undef;
+  $rev ||= ($self->pegrev ? $self->pegrev : 'HEAD');
+  # Get "info" for the specified revision if necessary
+  if (!exists($self->{INFO}{$rev})) {
+    $self->{INFO}{$rev}
+      = $SVN->get_info({'revision' => $rev}, $self->url_peg())->[0];
+  }
+  exists($self->{INFO}{$rev}{$flag}) ? $self->{INFO}{$rev}{$flag} : undef;
+}
+
+# ------------------------------------------------------------------------------
+# 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} : undef;
+  my $rev_arg       = exists $args{REV}          ? $args{REV}          : 0;
+
+  my @revs;
+
+  # Get revision options
+  # ----------------------------------------------------------------------------
+  if ($rev_arg) {
+    if (ref ($rev_arg)) {
+      # Revision 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] && exists($self->{LOG}{$revs [0]}));
+  my @ranges = @revs;
+  if ($need_update && $self->{LOG_RANGE}) {
+    my %log_range = %{$self->{LOG_RANGE}};
+    $log_range{LOWER_SOC} ||= 0;
+    $log_range{LOWER} ||= 0;
+
+    if ($stop_on_copy && $ranges[1] >= $log_range{LOWER_SOC}) {
+      if ($ranges[1] >= $log_range{LOWER_SOC}) {
+        $ranges[1] = $log_range{UPPER};
+      }
+    }
+    else {
+      if ($ranges[1] >= $log_range{LOWER}) {
+        $ranges[1] = $log_range{UPPER};
+      }
+    }
+  }
+
+  $need_update = 0 if $ranges[0] < $ranges[1];
+
+  if ($need_update) {
+    my @entries = @{$SVN->get_log(
+      {'revision' => join(':', @ranges), 'stop-on-copy' => $stop_on_copy},
+      $self->url_peg(),
+    )};
+    for my $entry (@entries) {
+      $self->{LOG}{$entry->{revision}} = $entry;
+      $entry->{paths} = {map {($_->{path} => $_)} @{$entry->{paths}}};
+    }
+
+    # 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 = FCM1::Keyword::get_browser_url($self->url);
+
+    # Get list of tickets from log
+    my @tickets;
+    while ($log{msg} =~ /(?:(\w+):)?(?:#|ticket:)(\d+)/g) {
+      push @tickets, [$1, $2];
+    }
+    @tickets = sort {
+      if ($a->[0] and $b->[0]) {
+        $a->[0] cmp $b->[0] or $a->[1] <=> $b->[1];
+
+      } elsif ($a->[0]) {
+        1;
+
+      } else {
+        $a->[1] <=> $b->[1];
+      }
+    } @tickets;
+
+    if ($trac_url =~ qr{^$wiki(?:/*|$)}msx) {
+      # URL is in the specified $wiki, use Trac link
+      $return .= '[' . $rev . '] ||';
+
+      for my $ticket (@tickets) {
+        $return .= ' ';
+        $return .= $ticket->[0] . ':' if $ticket->[0];
+        $return .= '#' . $ticket->[1];
+      }
+
+      $return .= ' ||';
+
+    } else {
+      # URL is not in the specified $wiki, use full URL
+      my $rev_url = $trac_url;
+      $rev_url    =~ s{/intertrac/source:.*\z}{/intertrac/changeset:$rev}xms;
+      $return    .= '[' . $rev_url . ' ' . $rev . '] ||';
+
+      my $ticket_url = $trac_url;
+      $ticket_url    =~ s{/intertrac/source:.*\z}{/intertrac/}xms;
+
+      for my $ticket (@tickets) {
+        $return .= ' [' . $ticket_url;
+        $return .= $ticket->[0] . ':' if $ticket->[0];
+        $return .= 'ticket:' . $ticket->[1] . ' ' . $ticket->[1] . ']';
+      }
+
+      $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->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) = @_;
+  if (!defined($self->project())) {
+    return;
+  }
+  $rev = $self->svninfo(FLAG => 'revision', REV => $rev);
+  if (!exists($self->{BRANCH_LIST}{$rev})) {
+    my %layout_config = %{$self->layout()->get_config()};
+    my $url0 = $self->project_url();
+    my @d1_filters = ();
+    if ($layout_config{'dir-branch'}) {
+      $url0 .= '/' . $layout_config{'dir-branch'};
+    }
+    else {
+      for my $key (qw{trunk tag}) {
+        if ($layout_config{"dir-$key"}) {
+          push(@d1_filters, $layout_config{"dir-$key"});
+        }
+      }
+    }
+    $self->{BRANCH_LIST}{$rev} = [$SVN->get_list(
+      $url0 . '@' . $self->pegrev(),
+      sub {
+        my ($this_url, $this_name, $is_dir, $depth) = @_;
+        if ($depth == 1 && @d1_filters && grep {$this_name eq $_} @d1_filters) {
+          return (0, 0);
+        }
+        my $can_return = $depth >= $layout_config{'depth-branch'};
+        ($can_return, ($is_dir && !$can_return));
+      },
+    )];
+  }
+  @{$self->{BRANCH_LIST}{$rev}};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $layout = $self->layout();
+#
+# DESCRIPTION
+#   Wrap FCM::System::CM::SVN->get_layout($url).
+# ------------------------------------------------------------------------------
+
+sub layout {
+  my ($self) = @_;
+  if (defined($self->{LAYOUT})) {
+    return $self->{LAYOUT};
+  }
+  my $url = $self->url_peg();
+  my $layout = $SVN->get_layout($url);
+  $self->{URL} = $layout->get_url();
+  $self->{LAYOUT} = $layout;
+
+  $layout;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->url();
+#   $url = $cm_url->pegrev();
+#   $url = $cm_url->root();
+#   $url = $cm_url->path();
+#   $url = $cm_url->path_peg();
+#
+# DESCRIPTION
+#   Return the relevant part of the current URL. The url method returns the URL
+#   without the peg revision. The pegrev method returns the peg revision. The
+#   root method returns the repository root. The path method returns the path in
+#   URL under root. The path_peg method returns the path in URL with a peg
+#   revision.
+# ------------------------------------------------------------------------------
+
+sub url {
+  my $layout = $_[0]->layout();
+  $layout->get_root() . $layout->get_path();
+}
+
+sub pegrev {
+  $_[0]->layout()->get_peg_rev();
+}
+
+sub root {
+  $_[0]->layout()->get_root();
+}
+
+sub path {
+  $_[0]->layout()->get_path();
+}
+
+sub path_peg {
+  my $layout = $_[0]->layout();
+  $layout->get_path() . '@' . $layout->get_peg_rev();
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->project_url_peg();
+#   $url = $cm_url->project_url();
+#   $url = $cm_url->project_path();
+#   $url = $cm_url->project();
+#   $url = $cm_url->branch_url();
+#   $url = $cm_url->branch_url_peg();
+#   $url = $cm_url->branch_path();
+#   $url = $cm_url->branch();
+#   $url = $cm_url->subdir();
+#
+# DESCRIPTION
+#   Return the relevant part of the current URL. The "project_*" methods return
+#   the "project" part. The "branch_*" methods return the "branch" part.
+#   The "*_url_peg" methods return the URL@PEG, and the "*_url" methods return
+#   the URL without the peg revision. The "*_path" methods return the path in
+#   the URL under the root.
+# ------------------------------------------------------------------------------
+
+sub project_url_peg {
+  my $layout = $_[0]->layout();
+  if (!defined($layout->get_project())) {
+    return;
+  }
+  my $path = $layout->get_project() ? '/' . $layout->get_project() : q{};
+  $layout->get_root() . $path . '@' . $layout->get_peg_rev();
+}
+
+sub project_url {
+  my $layout = $_[0]->layout();
+  if (!defined($layout->get_project())) {
+    return;
+  }
+  my $path = $layout->get_project() ? '/' . $layout->get_project() : q{};
+  $layout->get_root() . $path;
+}
+
+sub project_path {
+  my $layout = $_[0]->layout();
+  if (!defined($layout->get_project())) {
+    return;
+  }
+  '/' . $layout->get_project();
+}
+
+sub project {
+  $_[0]->layout()->get_project();
+}
+
+sub branch_url_peg {
+  my $layout = $_[0]->layout();
+  if (!$layout->get_branch()) {
+    return;
+  }
+  $_[0]->project_url() . '/' . $layout->get_branch()
+    . '@' . $layout->get_peg_rev();
+}
+
+sub branch_url {
+  my $layout = $_[0]->layout();
+  if (!$layout->get_branch()) {
+    return;
+  }
+  $_[0]->project_url() . '/' . $layout->get_branch();
+}
+
+sub branch_path {
+  my $layout = $_[0]->layout();
+  if (!$layout->get_branch()) {
+    return;
+  }
+  ($_[0]->project() ? '/' . $_[0]->project() : q{}) . '/' . $layout->get_branch();
+}
+
+sub branch {
+  $_[0]->layout()->get_branch();
+}
+
+sub subdir {
+  $_[0]->layout()->get_sub_tree();
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $obj->branch_owner();
+#
+# DESCRIPTION
+#   This method returns the owner of the branch (based on the default layout).
+# ------------------------------------------------------------------------------
+
+sub branch_owner {
+  $_[0]->layout()->get_branch_owner();
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_trunk();
+#   $flag = $cm_url->is_branch();
+#   $flag = $cm_url->is_tag();
+#
+# DESCRIPTION
+#   Return true if the branch of current URL belongs to a given category (i.e.
+#   trunk, branch or tag).
+# ------------------------------------------------------------------------------
+
+sub is_trunk {
+  $_[0]->layout()->is_trunk();
+}
+
+sub is_branch {
+  $_[0]->layout()->is_branch();
+}
+
+sub is_tag {
+  $_[0]->layout()->is_tag();
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Config.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Config.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Config.pm	(revision 5095)
@@ -0,0 +1,898 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Config
+#
+# DESCRIPTION
+#   This is a class for reading and processing central and user configuration
+#   settings for FCM.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::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 FCM1::CfgFile;
+
+# Other declarations:
+sub _get_hash_value;
+
+# Delimiter for setting and for list
+our $DELIMITER         = '::';
+our $DELIMITER_PATTERN = qr{::|/};
+our $DELIMITER_LIST    = ',';
+
+my $INSTANCE;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = FCM1::Config->instance();
+#
+# DESCRIPTION
+#   Returns an instance of this class.
+# ------------------------------------------------------------------------------
+
+sub instance {
+    my ($class) = @_;
+    if (!defined($INSTANCE)) {
+        $INSTANCE = $class->new();
+        $INSTANCE->get_config();
+        $INSTANCE->is_initialising(0);
+    }
+    return $INSTANCE;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::Config->new (VERBOSE => $verbose);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::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';
+  }
+
+  my $self = {
+    initialising   => 1,
+    central_config => undef,
+    user_config    => undef,
+    user_id        => undef,
+    verbose        => exists $args{VERBOSE} ? $args{VERBOSE} : undef,
+    variable       => {},
+
+    # Primary settings
+    setting => {
+      # Fortran BLOCKDATA dependencies
+      BLD_BLOCKDATA => {},
+
+      # Copy dummy target
+      BLD_CPDUMMY => '$(FCM_DONEDIR)/FCM_CP.dummy',
+
+      # No dependency check
+      BLD_DEP_N => {},
+
+      # Additional (PP) dependencies
+      BLD_DEP => {},
+      BLD_DEP_PP => {},
+
+      # Excluded dependency
+      BLD_DEP_EXCL => {
+        '' => [
+          # Fortran intrinsic modules
+          'USE' . $DELIMITER . 'ISO_C_BINDING',
+          'USE' . $DELIMITER . 'IEEE_EXCEPTIONS',
+          'USE' . $DELIMITER . 'IEEE_ARITHMETIC',
+          'USE' . $DELIMITER . 'IEEE_FEATURES',
+
+          # Fortran intrinsic subroutines
+          'OBJ' . $DELIMITER . 'CPU_TIME',
+          'OBJ' . $DELIMITER . 'GET_COMMAND',
+          'OBJ' . $DELIMITER . 'GET_COMMAND_ARGUMENT',
+          'OBJ' . $DELIMITER . 'GET_ENVIRONMENT_VARIABLE',
+          'OBJ' . $DELIMITER . 'MOVE_ALLOC',
+          'OBJ' . $DELIMITER . 'MVBITS',
+          'OBJ' . $DELIMITER . 'RANDOM_NUMBER',
+          'OBJ' . $DELIMITER . 'RANDOM_SEED',
+          'OBJ' . $DELIMITER . 'SYSTEM_CLOCK',
+
+          # Dummy statements
+          'OBJ' . $DELIMITER . 'NONE',
+          'EXE' . $DELIMITER . 'NONE',
+        ],
+      },
+
+      # Extra executable dependencies
+      BLD_DEP_EXE => {},
+
+      # Dependency pattern for each type
+      BLD_DEP_PATTERN => {
+        H         => q/^#\s*include\s*['"](\S+)['"]/,
+        USE       => q/^\s*use\s+(\w+)/,
+        INTERFACE => q/^#?\s*include\s+['"](\S+##OUTFILE_EXT/ . $DELIMITER .
+                     q/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+)/,
+      },
+
+      # Rename main program targets
+      BLD_EXE_NAME => {},
+
+      # Rename library targets
+      BLD_LIB => {'' => 'fcm_default'},
+
+      # Name of Makefile and run environment shell script
+      BLD_MISC => {
+        'BLDMAKEFILE' => 'Makefile',
+        'BLDRUNENVSH' => 'fcm_env.sh',
+      },
+
+      # PP flags
+      BLD_PP => {},
+
+      # Custom source file type
+      BLD_TYPE => {},
+
+      # Types that always need to be built
+      BLD_TYPE_ALWAYS_BUILD =>                   'PVWAVE' .
+                               $DELIMITER_LIST . 'GENLIST' .
+                               $DELIMITER_LIST . 'SQL',
+
+      # Dependency scan types
+      BLD_TYPE_DEP => {
+        FORTRAN =>              'USE' .
+                   $DELIMITER . 'INTERFACE' .
+                   $DELIMITER . 'INC' .
+                   $DELIMITER . 'OBJ',
+        FPP     =>              'USE' .
+                   $DELIMITER . 'INTERFACE' .
+                   $DELIMITER . 'INC' .
+                   $DELIMITER . 'H' .
+                   $DELIMITER . 'OBJ',
+        CPP     =>              'H' .
+                   $DELIMITER . 'OBJ',
+        C       =>              'H' .
+                   $DELIMITER . 'OBJ',
+        SCRIPT  =>              'EXE',
+      },
+
+      # Dependency scan types for pre-processing
+      BLD_TYPE_DEP_PP => {
+        FPP => 'H',
+        CPP => 'H',
+        C   => 'H',
+      },
+
+      # Types that cannot have duplicated targets
+      BLD_TYPE_NO_DUPLICATED_TARGET => '',
+
+      # BLD_VPATH, each value must be a comma separate list
+      # ''     translates to %
+      # 'FLAG' translates to {OUTFILE_EXT}{FLAG}
+      BLD_VPATH   => {
+        BIN   => q{},
+        ETC   => 'ETC',
+        DONE  => join($DELIMITER_LIST, qw{DONE IDONE}),
+        FLAGS => 'FLAGS',
+        INC   => q{},
+        LIB   => 'LIB',
+        OBJ   => 'OBJ',
+      },
+
+      # Cache basename
+      CACHE          => '.config',
+      CACHE_DEP      => '.config_dep',
+      CACHE_DEP_PP   => '.config_dep_pp',
+      CACHE_FILE_SRC => '.config_file_src',
+
+      # Types of "inc" statements expandable CFG files
+      CFG_EXP_INC =>                   'BLD' .
+                     $DELIMITER_LIST . 'EXT' .
+                     $DELIMITER_LIST . 'FCM',
+
+      # Configuration file labels that can be declared more than once
+      CFG_KEYWORD =>                   'USE' .
+                     $DELIMITER_LIST . 'INC' .
+                     $DELIMITER_LIST . 'TARGET' .
+                     $DELIMITER_LIST . 'BLD_DEP_EXCL',
+
+      # Labels for all types of FCM configuration files
+      CFG_LABEL => {
+        CFGFILE => 'CFG', # config file information
+        INC     => 'INC', # "include" from an configuration file
+
+        # Labels for central/user internal config setting
+        SETTING => 'SET',
+
+        # Labels for systems that allow inheritance
+        DEST => 'DEST', # destination
+        USE  => 'USE',  # use (inherit) a previous configuration
+
+        # Labels for bld and pck cfg
+        TARGET => 'TARGET', # BLD: declare targets, PCK: target of source file
+
+        # Labels for bld cfg
+        BLD_BLOCKDATA => 'BLOCKDATA',   # declare Fortran BLOCKDATA dependencies
+        BLD_DEP       => 'DEP',         # additional dependencies
+        BLD_DEP_N     => 'NO_DEP',      # no dependency check
+        BLD_DEP_EXCL  => 'EXCL_DEP',    # exclude automatic dependencies
+        BLD_DEP_EXE   => 'EXE_DEP',     # declare dependencies for program
+        BLD_EXE_NAME  => 'EXE_NAME',    # rename a main program
+        BLD_LIB       => 'LIB',         # rename library
+        BLD_PP        => 'PP',          # sub-package needs pre-process?
+        BLD_TYPE      => 'SRC_TYPE',    # custom source file type
+        DIR           => 'DIR',         # DEPRECATED, same as DEST
+        INFILE_EXT    => 'INFILE_EXT',  # change input file name extension type
+        INHERIT       => 'INHERIT',     # inheritance flag
+        NAME          => 'NAME',        # name the build
+        OUTFILE_EXT   => 'OUTFILE_EXT', # change output file type extension
+        FILE          => 'SRC',         # declare a sub-package
+        SEARCH_SRC    => 'SEARCH_SRC',  # search src/ sub-directory?
+        TOOL          => 'TOOL',        # declare a tool
+
+        # Labels for ext cfg
+        BDECLARE   => 'BLD',      # build declaration
+        CONFLICT   => 'CONFLICT', # set conflict mode
+        DIRS       => 'SRC',      # declare source directory
+        EXPDIRS    => 'EXPSRC',   # declare expandable source directory
+        MIRROR     => 'MIRROR',   # DEPRECATED, same as RDEST::MIRROR_CMD
+        OVERRIDE   => 'OVERRIDE', # DEPRECATED, replaced by CONFLICT
+        RDEST      => 'RDEST',    # declare remote destionation
+        REVISION   => 'REVISION', # declare branch revision in a project
+        REVMATCH   => 'REVMATCH', # branch revision must match changed revision
+        REPOS      => 'REPOS',    # declare branch in a project
+        VERSION    => 'VERSION',  # DEPRECATED, same as REVISION
+      },
+
+      # Default names of known FCM configuration files
+      CFG_NAME => {
+        BLD        => 'bld.cfg',      # build configuration file
+        EXT        => 'ext.cfg',      # extract configuration file
+        PARSED     => 'parsed_',      # as-parsed configuration file prefix
+      },
+
+      # Latest version of known FCM configuration files
+      CFG_VERSION => {
+        BLD        => '1.0', # bld cfg
+        EXT        => '1.0', # ext cfg
+      },
+
+      # 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
+      },
+
+      # A flag to indicate whether the revision of a given branch for extract
+      # must match with the revision of a changed revision of the branch
+      EXT_REVMATCH => 0, # default is false (allow any revision)
+
+      # Input file name extension and type
+      # (may overlap with output (below) and vpath (above))
+      INFILE_EXT => {
+        # General extensions
+        'f'    =>              'FORTRAN' .
+                  $DELIMITER . 'SOURCE',
+        'for'  =>              'FORTRAN' .
+                  $DELIMITER . 'SOURCE',
+        'ftn'  =>              'FORTRAN' .
+                  $DELIMITER . 'SOURCE',
+        'f77'  =>              'FORTRAN' .
+                  $DELIMITER . 'SOURCE',
+        'f90'  =>              'FORTRAN' .
+                  $DELIMITER . 'FORTRAN9X' .
+                  $DELIMITER . 'SOURCE',
+        'f95'  =>              'FORTRAN' .
+                  $DELIMITER . 'FORTRAN9X' .
+                  $DELIMITER . 'SOURCE',
+        'F'    =>              'FPP' .
+                  $DELIMITER . 'SOURCE',
+        'FOR'  =>              'FPP' .
+                  $DELIMITER . 'SOURCE',
+        'FTN'  =>              'FPP' .
+                  $DELIMITER . 'SOURCE',
+        'F77'  =>              'FPP' .
+                  $DELIMITER . 'SOURCE',
+        'F90'  =>              'FPP' .
+                  $DELIMITER . 'FPP9X' .
+                  $DELIMITER . 'SOURCE',
+        'F95'  =>              'FPP' .
+                  $DELIMITER . 'FPP9X' .
+                  $DELIMITER . 'SOURCE',
+        'c'    =>              'C' .
+                  $DELIMITER . 'SOURCE',
+        'cpp'  =>              'C' .
+                  $DELIMITER . 'C++' .
+                  $DELIMITER . 'SOURCE',
+        'h'    =>              'CPP' .
+                  $DELIMITER . 'INCLUDE',
+        'o'    =>              'BINARY' .
+                  $DELIMITER . 'OBJ',
+        'obj'  =>              'BINARY' .
+                  $DELIMITER . 'OBJ',
+        'exe'  =>              'BINARY' .
+                  $DELIMITER . 'EXE',
+        'a'    =>              'BINARY' .
+                  $DELIMITER . 'LIB',
+        'sh'   =>              'SCRIPT' .
+                  $DELIMITER . 'SHELL',
+        'ksh'  =>              'SCRIPT' .
+                  $DELIMITER . 'SHELL',
+        'bash' =>              'SCRIPT' .
+                  $DELIMITER . 'SHELL',
+        'csh'  =>              'SCRIPT' .
+                  $DELIMITER . 'SHELL',
+        'pl'   =>              'SCRIPT' .
+                  $DELIMITER . 'PERL',
+        'pm'   =>              'SCRIPT' .
+                  $DELIMITER . 'PERL',
+        'py'   =>              'SCRIPT' .
+                  $DELIMITER . 'PYTHON',
+        'tcl'  =>              'SCRIPT' .
+                  $DELIMITER . 'TCL',
+        'pro'  =>              'SCRIPT' .
+                  $DELIMITER . 'PVWAVE',
+
+        # Local extensions
+        'cfg'       =>              'CFGFILE',
+        'h90'       =>              'CPP' .
+                       $DELIMITER . 'INCLUDE',
+        'inc'       =>              'FORTRAN' .
+                       $DELIMITER . 'FORTRAN9X' .
+                       $DELIMITER . 'INCLUDE',
+        'interface' =>              'FORTRAN' .
+                       $DELIMITER . 'FORTRAN9X' .
+                       $DELIMITER . 'INCLUDE' .
+                       $DELIMITER . 'INTERFACE',
+      },
+
+      # Ignore input files matching the following names (comma-separated list)
+      INFILE_IGNORE =>                   'fcm_env.ksh' .
+                       $DELIMITER_LIST . 'fcm_env.sh',
+
+      # Input file name pattern and type
+      INFILE_PAT => {
+        '\w+Scr_\w+'              =>              'SCRIPT' .
+                                     $DELIMITER . 'SHELL',
+        '\w+Comp_\w+'             =>              'SCRIPT' .
+                                     $DELIMITER . 'SHELL' .
+                                     $DELIMITER . 'GENTASK',
+        '\w+(?:IF|Interface)_\w+' =>              'SCRIPT' .
+                                     $DELIMITER . 'SHELL' .
+                                     $DELIMITER . 'GENIF',
+        '\w+Suite_\w+'            =>              'SCRIPT' .
+                                     $DELIMITER . 'SHELL' .
+                                     $DELIMITER . 'GENSUITE',
+        '\w+List_\w+'             =>              'SCRIPT' .
+                                     $DELIMITER . 'SHELL' .
+                                     $DELIMITER . 'GENLIST',
+        '\w+Sql_\w+'              =>              'SCRIPT' .
+                                     $DELIMITER . 'SQL',
+      },
+
+      # Input text file pattern and type
+      INFILE_TXT => {
+        '(?:[ck]|ba)?sh'  =>              'SCRIPT' .
+                             $DELIMITER . 'SHELL',
+        'perl'            =>              'SCRIPT' .
+                             $DELIMITER . 'PERL',
+        'python'          =>              'SCRIPT' .
+                             $DELIMITER . 'PYTHON',
+        'tcl(?:sh)?|wish' =>              'SCRIPT' .
+                             $DELIMITER . 'TCL',
+      },
+
+      # Lock file
+      LOCK => {
+        BLDLOCK => 'fcm.bld.lock', # build lock file
+        EXTLOCK => 'fcm.ext.lock', # extract lock file
+      },
+
+      # Output file type and extension
+      # (may overlap with input and vpath (above))
+      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
+        MOD       => '.mod',       # compiled Fortran module information files
+        OBJ       => '.o',         # compiled object files
+        PDONE     => '.pdone',     # "done" files for pre-processed files
+        TAR       => '.tar',       # TAR archive
+      },
+
+      # Build commands and options (i.e. tools)
+      TOOL => {
+        SHELL        => '/bin/sh',         # 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_MODSEARCH => '',                # FC flag, specify "module" path
+        FC_DEFINE    => '-D',              # FC flag, define macro
+
+        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_FILE    => '-f',              # make flag, path to Makefile
+        MAKE_SILENT  => '-s',              # make flag, silent diagnostic
+        MAKE_JOB     => '-j',              # make flag, number of jobs
+
+        INTERFACE    => 'file',            # name interface after file/program
+        GENINTERFACE => '',                # Fortran 9x interface generator
+
+        DIFF3        => 'diff3',           # extract diff3 merge
+        DIFF3FLAGS   => '-E -m',           # DIFF3 flags
+        GRAPHIC_DIFF => 'xxdiff',          # graphical diff tool
+        GRAPHIC_MERGE=> 'xxdiff',          # graphical merge tool
+      },
+
+      # List of tools that are local to FCM, (will not be exported to a Makefile)
+      TOOL_LOCAL =>                   'CPP' .
+                    $DELIMITER_LIST . 'CPPFLAGS' .
+                    $DELIMITER_LIST . 'CPP_INCLUDE' .
+                    $DELIMITER_LIST . 'CPP_DEFINE' .
+                    $DELIMITER_LIST . 'DIFF3' .
+                    $DELIMITER_LIST . 'DIFF3_FLAGS' .
+                    $DELIMITER_LIST . 'FPP' .
+                    $DELIMITER_LIST . 'FPPFLAGS' .
+                    $DELIMITER_LIST . 'FPP_INCLUDE' .
+                    $DELIMITER_LIST . 'FPP_DEFINE' .
+                    $DELIMITER_LIST . 'GRAPHIC_DIFF' .
+                    $DELIMITER_LIST . 'GRAPHIC_MERGE' .
+                    $DELIMITER_LIST . 'MAKE' .
+                    $DELIMITER_LIST . 'MAKEFLAGS' .
+                    $DELIMITER_LIST . 'MAKE_FILE' .
+                    $DELIMITER_LIST . 'MAKE_SILENT' .
+                    $DELIMITER_LIST . 'MAKE_JOB' .
+                    $DELIMITER_LIST . 'INTERFACE' .
+                    $DELIMITER_LIST . 'GENINTERFACE' .
+                    $DELIMITER_LIST . 'MIRROR' .
+                    $DELIMITER_LIST . 'REMOTE_SHELL',
+
+      # List of tools that allow sub-package declarations
+      TOOL_PACKAGE =>                   'CPPFLAGS' .
+                      $DELIMITER_LIST . 'CPPKEYS' .
+                      $DELIMITER_LIST . 'CFLAGS' .
+                      $DELIMITER_LIST . 'FPPFLAGS' .
+                      $DELIMITER_LIST . 'FPPKEYS' .
+                      $DELIMITER_LIST . 'FFLAGS' .
+                      $DELIMITER_LIST . 'LD' .
+                      $DELIMITER_LIST . 'LDFLAGS' .
+                      $DELIMITER_LIST . 'INTERFACE' .
+                      $DELIMITER_LIST . 'GENINTERFACE',
+
+      # Supported tools for compilable source
+      TOOL_SRC_PP => {
+        FPP     => {
+          COMMAND => 'FPP',
+          FLAGS   => 'FPPFLAGS',
+          PPKEYS  => 'FPPKEYS',
+          INCLUDE => 'FPP_INCLUDE',
+          DEFINE  => 'FPP_DEFINE',
+        },
+
+        C       => {
+          COMMAND => 'CPP',
+          FLAGS   => 'CPPFLAGS',
+          PPKEYS  => 'CPPKEYS',
+          INCLUDE => 'CPP_INCLUDE',
+          DEFINE  => 'CPP_DEFINE',
+        },
+      },
+
+      # Supported tools for compilable source
+      TOOL_SRC => {
+        FORTRAN => {
+          COMMAND => 'FC',
+          FLAGS   => 'FFLAGS',
+          OUTPUT  => 'FC_OUTPUT',
+          INCLUDE => 'FC_INCLUDE',
+        },
+
+        FPP     => {
+          COMMAND => 'FC',
+          FLAGS   => 'FFLAGS',
+          PPKEYS  => 'FPPKEYS',
+          OUTPUT  => 'FC_OUTPUT',
+          INCLUDE => 'FC_INCLUDE',
+          DEFINE  => 'FC_DEFINE',
+        },
+
+        C       => {
+          COMMAND => 'CC',
+          FLAGS   => 'CFLAGS',
+          PPKEYS  => 'CPPKEYS',
+          OUTPUT  => 'CC_OUTPUT',
+          INCLUDE => 'CC_INCLUDE',
+          DEFINE  => 'CC_DEFINE',
+        },
+      },
+
+      # FCM URL keyword and prefix, FCM revision keyword, and FCM Trac URL
+      URL          => {},
+      URL_REVISION => {},
+
+      URL_BROWSER_MAPPING => {},
+      URL_BROWSER_MAPPING_DEFAULT => {
+        LOCATION_COMPONENT_PATTERN
+        => qr{\A // ([^/]+) /+ ([^/]+)_svn /+(.*) \z}xms,
+        BROWSER_URL_TEMPLATE
+        => 'http://{1}/projects/{2}/intertrac/source:{3}{4}',
+        BROWSER_REV_TEMPLATE => '@{1}',
+      },
+
+      # Default web browser
+      WEB_BROWSER   => 'firefox',
+    },
+  };
+
+  # Backward compatibility: the REPOS setting is equivalent to the URL setting
+  $self->{setting}{REPOS} = $self->{setting}{URL};
+
+  # Alias the REVISION and TRAC setting to URL_REVISION and URL_TRAC
+  $self->{setting}{REVISION} = $self->{setting}{URL_REVISION};
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in the "new" method.
+# ------------------------------------------------------------------------------
+
+for my $name (qw/central_config user_config user_id verbose/) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'central_config') {
+        # Central configuration file
+        if (-f catfile (dirname ($FindBin::Bin), 'etc', 'fcm.cfg')) {
+          $self->{$name} = catfile (
+            dirname ($FindBin::Bin), 'etc', 'fcm.cfg'
+          );
+
+        } elsif (-f catfile ($FindBin::Bin, 'fcm.cfg')) {
+          $self->{$name} = catfile ($FindBin::Bin, 'fcm.cfg');
+        }
+
+      } elsif ($name eq 'user_config') {
+        # User configuration file
+        my $home = (getpwuid ($<))[7];
+        $home = $ENV{HOME} if not defined $home;
+        $self->{$name} = catfile ($home, '.fcm')
+          if defined ($home) and -f catfile ($home, '.fcm');
+
+      } elsif ($name eq 'user_id') {
+        # User ID of current process
+        my $user = (getpwuid ($<))[0];
+        $user = $ENV{LOGNAME} if not defined $user;
+        $user = $ENV{USER} if not defined $user;
+        $self->{$name} = $user;
+
+      } elsif ($name eq 'verbose') {
+        # Verbose mode
+        $self->{$name} = exists $ENV{FCM_VERBOSE} ? $ENV{FCM_VERBOSE} : 1;
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $obj->is_initialising();
+#
+# DESCRIPTION
+#   Returns true if this object is initialising.
+# ------------------------------------------------------------------------------
+sub is_initialising {
+  my ($self, $value) = @_;
+  if (defined($value)) {
+    $self->{initialising} = $value;
+  }
+  return $self->{initialising};
+}
+
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %hash = %{ $obj->X () };
+#   $obj->X (\%hash);
+#
+#   $value = $obj->X ($index);
+#   $obj->X ($index, $value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in the "new" method.
+#
+#   If no argument is set, this method returns a hash containing a list of
+#   objects. If an argument is set and it is a reference to a hash, the objects
+#   are replaced by the specified hash.
+#
+#   If a scalar argument is specified, this method returns a reference to an
+#   object, if the indexed object exists or undef if the indexed object does
+#   not exist. If a second argument is set, the $index element of the hash will
+#   be set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+for my $name (qw/variable/) {
+  no strict 'refs';
+
+  *$name = sub {
+    my ($self, $arg1, $arg2) = @_;
+
+    # Ensure property is defined as a reference to a hash
+    $self->{$name} = {} if not defined ($self->{$name});
+
+    # Argument 1 can be a reference to a hash or a scalar index
+    my ($index, %hash);
+
+    if (defined $arg1) {
+      if (ref ($arg1) eq 'HASH') {
+        %hash = %$arg1;
+
+      } else {
+        $index = $arg1;
+      }
+    }
+
+    if (defined $index) {
+      # A scalar index is defined, set and/or return the value of an element
+      $self->{$name}{$index} = $arg2 if defined $arg2;
+
+      return (
+        exists $self->{$name}{$index} ? $self->{$name}{$index} : undef
+      );
+
+    } else {
+      # A scalar index is not defined, set and/or return the hash
+      $self->{$name} = \%hash if defined $arg1;
+      return $self->{$name};
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $setting = $obj->setting (@labels);
+#   $obj->setting (\@labels, $setting);
+#
+# DESCRIPTION
+#   This method returns/sets an item under the setting hash table. The depth
+#   within the hash table is given by the list of arguments @labels, which
+#   should match with the keys in the multi-dimension setting hash table.
+# ------------------------------------------------------------------------------
+
+sub setting {
+  my $self = shift;
+
+  if (@_) {
+    my $arg1 = shift;
+    my $s    = $self->{setting};
+
+    if (ref ($arg1) eq 'ARRAY') {
+      # Assign setting
+      # ------------------------------------------------------------------------
+      my $value = shift;
+
+      while (defined (my $label = shift @$arg1)) {
+        if (exists $s->{$label}) {
+          if (ref $s->{$label} eq 'HASH') {
+            $s = $s->{$label};
+
+          } else {
+            $s->{$label} = $value;
+            last;
+          }
+
+        } else {
+          if (@$arg1) {
+            $s->{$label} = {};
+            $s           = $s->{$label};
+
+          } else {
+            $s->{$label} = $value;
+          }
+        }
+      }
+
+    } else {
+      # Get setting
+      # ------------------------------------------------------------------------
+      return _get_hash_value ($s->{$arg1}, @_) if exists $s->{$arg1};
+    }
+  }
+
+  return undef;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj->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->central_config);  
+  $self->_read_config_file ($self->user_config);
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj->_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];
+
+  if (!$config_file || !-f $config_file) {
+    return;
+  }
+
+  my $cfgfile = FCM1::CfgFile->new (SRC => $config_file, TYPE => 'FCM');
+  $cfgfile->read_cfg ();
+
+  LINE: for my $line (@{ $cfgfile->lines }) {
+    next unless $line->label;
+
+    # "Environment variables" start with $
+    if ($line->label =~ /^\$([A-Za-z_]\w*)$/) {
+      $ENV{$1} = $line->value;
+      next LINE;
+    }
+
+    # "Settings variables" start with "set"
+    if ($line->label_starts_with_cfg ('SETTING')) {
+      my @tags = $line->label_fields;
+      shift @tags;
+      @tags = map {uc} @tags;
+      $self->setting (\@tags, $line->value);
+      next LINE;
+    }
+
+    # Not a standard setting variable, put in internal variable list
+    (my $label = $line->label) =~ s/^\%//;
+    $self->variable ($label, $line->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: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ConfigSystem.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ConfigSystem.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ConfigSystem.pm	(revision 5095)
@@ -0,0 +1,754 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::ConfigSystem
+#
+# DESCRIPTION
+#   This is the base class for FCM systems that are based on inherited
+#   configuration files, e.g. the extract and the build systems.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::ConfigSystem;
+use base qw{FCM1::Base};
+
+use strict;
+use warnings;
+
+use FCM1::CfgFile;
+use FCM1::CfgLine;
+use FCM1::Dest;
+use FCM1::Util     qw{expand_tilde e_report w_report};
+use Sys::Hostname qw{hostname};
+
+# List of property methods for this class
+my @scalar_properties = (
+ 'cfg',         # configuration file
+ 'cfg_methods', # list of sub-methods for parse_cfg
+ 'cfg_prefix',  # optional prefix in configuration declaration
+ 'dest',        # destination for output
+ 'inherit',     # list of inherited configurations
+ 'inherited',   # list of inheritance hierarchy
+ 'type',        # system type
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::ConfigSystem->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::ConfigSystem class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  $self->{$_} = undef for (@scalar_properties);
+
+  bless $self, $class;
+
+  # List of sub-methods for parse_cfg
+  $self->cfg_methods ([qw/header inherit dest/]);
+
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'cfg') {
+        # New configuration file
+        $self->{$name} = FCM1::CfgFile->new (TYPE => $self->type);
+
+      } elsif ($name =~ /^(?:cfg_methods|inherit|inherited)$/) {
+        # Reference to an array
+        $self->{$name} = [];
+
+      } elsif ($name eq 'cfg_prefix' or $name eq 'type') {
+        # Reference to an array
+        $self->{$name} = '';
+
+      } elsif ($name eq 'dest') {
+        # New destination
+        $self->{$name} = FCM1::Dest->new (TYPE => $self->type);
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, $out_of_date) = $obj->check_cache ();
+#
+# DESCRIPTION
+#   This method returns $rc = 1 on success or undef on failure. It returns
+#   $out_of_date = 1 if current cache file is out of date relative to those in
+#   inherited runs or 0 otherwise.
+# ------------------------------------------------------------------------------
+
+sub check_cache {
+  my $self = shift;
+
+  my $rc = 1;
+  my $out_of_date = 0;
+
+  if (@{ $self->inherit } and -f $self->dest->cache) {
+    # Get modification time of current cache file
+    my $cur_mtime = (stat ($self->dest->cache))[9];
+
+    # Compare with modification times of inherited cache files
+    for my $use (@{ $self->inherit }) {
+      next unless -f $use->dest->cache;
+      my $use_mtime = (stat ($use->dest->cache))[9];
+      $out_of_date = 1 if $use_mtime > $cur_mtime;
+    }
+  }
+
+  return ($rc, $out_of_date);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->check_lock ();
+#
+# DESCRIPTION
+#   This method returns true if no lock is found in the destination or if the
+#   locks found are allowed. 
+# ------------------------------------------------------------------------------
+
+sub check_lock {
+  my $self = shift;
+
+  # Check all types of locks
+  for my $method (@FCM1::Dest::lockfiles) {
+    my $lock = $self->dest->$method;
+
+    # Check whether lock exists
+    next unless -e $lock;
+
+    # Check whether this lock is allowed
+    next if $self->check_lock_is_allowed ($lock);
+
+    # Throw error if a lock exists
+    w_report 'ERROR: ', $lock, ': lock file exists,';
+    w_report '       ', $self->dest->rootdir, ': destination is busy.';
+    return;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->check_lock_is_allowed ($lock);
+#
+# DESCRIPTION
+#   This method returns true if it is OK for $lock to exist in the destination.
+# ------------------------------------------------------------------------------
+
+sub check_lock_is_allowed {
+  my ($self, $lock) = @_;
+
+  # Disallow all types of locks by default
+  return 0;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->compare_setting (
+#     METHOD_LIST  => \@method_list,
+#     [METHOD_ARGS => \@method_args,]
+#     [CACHEBASE   => $cachebase,]
+#   );
+#
+# DESCRIPTION
+#   This method gets settings from the previous cache and updates the current.
+#
+# METHOD
+#   The method returns true on success. @method_list must be a list of method
+#   names for processing the cached lines in the previous run. If an existing
+#   cache exists, its content is read into $old_lines, which is a list of
+#   FCM1::CfgLine objects. Otherwise, $old_lines is set to undef. If $cachebase
+#   is set, it is used for as the cache basename. Otherwise, the default for
+#   the current system is used. It calls each method in the @method_list using
+#   $self->$method ($old_lines, @method_args), which should return a
+#   two-element list. The first element should be a return code (1 for out of
+#   date, 0 for up to date and undef for failure). The second element should be
+#   a reference to a list of FCM1::CfgLine objects for the output.
+# ------------------------------------------------------------------------------
+
+sub compare_setting {
+  my ($self, %args) = @_;
+
+  my @method_list = exists ($args{METHOD_LIST}) ? @{ $args{METHOD_LIST} } : ();
+  my @method_args = exists ($args{METHOD_ARGS}) ? @{ $args{METHOD_ARGS} } : ();
+  my $cachebase   = exists ($args{CACHEBASE}) ? $args{CACHEBASE} : undef;
+
+  my $rc = 1;
+
+  # Read cache if the file exists
+  # ----------------------------------------------------------------------------
+  my $cache = $cachebase
+              ? File::Spec->catfile ($self->dest->cachedir, $cachebase)
+              : $self->dest->cache;
+  my @in_caches = ();
+  if (-f $cache) {
+    push @in_caches, $cache;
+
+  } else {
+    for my $use (@{ $self->inherit }) {
+      my $use_cache = $cachebase
+                      ? File::Spec->catfile ($use->dest->cachedir, $cachebase)
+                      : $use->dest->cache;
+      push @in_caches, $use_cache if -f $use_cache;
+    }
+  }
+
+  my $old_lines = undef;
+  for my $in_cache (@in_caches) {
+    next unless -f $in_cache;
+    my $cfg = FCM1::CfgFile->new (SRC => $in_cache);
+
+    if ($cfg->read_cfg) {
+      $old_lines = [] if not defined $old_lines;
+      push @$old_lines, @{ $cfg->lines };
+    }
+  }
+
+  # Call methods in @method_list to see if cache is out of date
+  # ----------------------------------------------------------------------------
+  my @new_lines = ();
+  my $out_of_date = 0;
+  for my $method (@method_list) {
+    my ($return, $lines);
+    ($return, $lines) = $self->$method ($old_lines, @method_args) if $rc;
+
+    if (defined $return) {
+      # Method succeeded
+      push @new_lines, @$lines;
+      $out_of_date = 1 if $return;
+
+    } else {
+      # Method failed
+      $rc = $return;
+      last;
+    }
+  }
+
+  # Update the cache in the current run
+  # ----------------------------------------------------------------------------
+  if ($rc) {
+    if (@{ $self->inherited } and $out_of_date) {
+      # If this is an inherited configuration, the cache must not be changed
+      w_report 'ERROR: ', $self->cfg->src,
+               ': inherited configuration does not match with its cache.';
+      $rc = undef;
+
+    } elsif ((not -f $cache) or $out_of_date) {
+      my $cfg = FCM1::CfgFile->new;
+      $cfg->lines ([sort {$a->label cmp $b->label} @new_lines]);
+      $rc = $cfg->print_cfg ($cache, 1);
+    }
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($changed_hash_ref, $new_lines_array_ref) =
+#     $self->compare_setting_in_config($prefix, \@old_lines);
+#
+# DESCRIPTION
+#   This method compares old and current settings for a specified item.
+#
+# METHOD
+#   This method does two things.
+#
+#   It uses the current configuration for the $prefix item to generate a list of
+#   new FCM1::CfgLine objects (which is returned as a reference in the second
+#   element of the returned list).
+#
+#   The values of the old lines are then compared with those of the new lines.
+#   Any settings that are changed are stored in a hash, which is returned as a
+#   reference in the first element of the returned list. The key of the hash is
+#   the name of the changed setting, and the value is the value of the new
+#   setting or undef if the setting no longer exists.
+#
+# ARGUMENTS
+#   $prefix    - the name of an item in FCM1::Config to be compared
+#   @old_lines - a list of FCM1::CfgLine objects containing the old settings
+# ------------------------------------------------------------------------------
+
+sub compare_setting_in_config {
+  my ($self, $prefix, $old_lines_ref) = @_;
+  
+  my %changed = %{$self->setting($prefix)};
+  my (@new_lines, %new_val_of);
+  while (my ($key, $val) = each(%changed)) {
+    $new_val_of{$key} = (ref($val) eq 'ARRAY' ? join(q{ }, sort(@{$val})) : $val);
+    push(@new_lines, FCM1::CfgLine->new(
+      LABEL => $prefix . $FCM1::Config::DELIMITER . $key,
+      VALUE => $new_val_of{$key},
+    ));
+  }
+
+  if (defined($old_lines_ref)) {
+    my %old_val_of
+      = map {($_->label_from_field(1), $_->value())} # converts into a hash
+        grep {$_->label_starts_with($prefix)}        # gets relevant lines
+        @{$old_lines_ref};
+
+    while (my ($key, $val) = each(%old_val_of)) {
+      if (exists($changed{$key})) {
+        if ($val eq $new_val_of{$key}) { # no change from old to new
+          delete($changed{$key});
+        }
+      }
+      else { # exists in old but not in new
+        $changed{$key} = undef;
+      }
+    }
+  }
+
+  return (\%changed, \@new_lines);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->invoke ([CLEAN => 1, ]%args);
+#
+# DESCRIPTION
+#   This method invokes the system. If CLEAN is set to true, it will only parse
+#   the configuration and set up the destination, but will not invoke the
+#   system. See the invoke_setup_dest and the invoke_system methods for list of
+#   other arguments in %args.
+# ------------------------------------------------------------------------------
+
+sub invoke {
+  my $self = shift;
+  my %args = @_;
+
+  # Print diagnostic at beginning of run
+  # ----------------------------------------------------------------------------
+  # Name of the system
+  (my $name = ref ($self)) =~ s/^FCM1:://;
+
+  # Print start time on system run, if verbose is true
+  my $date = localtime;
+  print $name, ' command started on ', $date, '.', "\n"
+    if $self->verbose;
+
+  # Start time (seconds since epoch)
+  my $otime = time;
+
+  # Parse the configuration file
+  my $rc = $self->invoke_stage ('Parse configuration', 'parse_cfg');
+
+  # Set up the destination
+  $rc = $self->invoke_stage ('Setup destination', 'invoke_setup_dest', %args)
+    if $rc;
+
+  # Invoke the system
+  # ----------------------------------------------------------------------------
+  $rc = $self->invoke_system (%args) if $rc and not $args{CLEAN};
+
+  # Remove empty directories
+  $rc = $self->dest->clean (MODE => 'EMPTY') if $rc;
+
+  # Print diagnostic at end of run
+  # ----------------------------------------------------------------------------
+  # Print lapse time at the end, if verbose is true
+  if ($self->verbose) {
+    my $total = time - $otime;
+    my $s_str = $total > 1 ? 'seconds' : 'second';
+    print '->TOTAL: ', $total, ' ', $s_str, "\n";
+  }
+
+  # Report end of system run
+  $date = localtime;
+  if ($rc) {
+    # Success
+    print $name, ' command finished on ', $date, '.', "\n"
+      if $self->verbose;
+
+  } else {
+    # Failure
+    e_report $name, ' failed on ', $date, '.';
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->invoke_setup_dest ([CLEAN|FULL => 1], [IGNORE_LOCK => 1]);
+#
+# DESCRIPTION
+#   This method sets up the destination and returns true on success.
+#
+# ARGUMENTS
+#   CLEAN|FULL   - If set to "true", set up the system in "clean|full" mode.
+#                  Sub-directories and files in the root directory created by
+#                  the previous invocation of the system will be removed. If
+#                  not set, the default is to run in "incremental" mode.
+#   IGNORE_LOCK  - If set to "true", it ignores any lock files that may exist in
+#                  the destination root directory. 
+# ------------------------------------------------------------------------------
+
+sub invoke_setup_dest {
+  my $self = shift;
+  my %args = @_;
+
+  # Set up destination
+  # ----------------------------------------------------------------------------
+  # Print destination in verbose mode
+  if ($self->verbose()) {
+    printf(
+      "Destination: %s@%s:%s\n",
+      scalar(getpwuid($<)),
+      hostname(),
+      $self->dest()->rootdir(),
+    );
+  }
+
+  my $rc = 1;
+  my $out_of_date = 0;
+
+  # Check whether lock exists in the destination root
+  $rc = $self->check_lock if $rc and not $args{IGNORE_LOCK};
+
+  # Check whether current cache is out of date relative to the inherited ones
+  ($rc, $out_of_date) = $self->check_cache if $rc;
+
+  # Remove sub-directories and files in destination in "full" mode
+  $rc = $self->dest->clean (MODE => 'ALL')
+    if $rc and ($args{FULL} or $args{CLEAN} or $out_of_date);
+
+  # Create build root directory if necessary
+  $rc = $self->dest->create if $rc;
+
+  # Set a lock in the destination root
+  $rc = $self->dest->set_lock if $rc;
+
+  # Generate an as-parsed configuration file
+  $self->cfg->print_cfg ($self->dest->parsedcfg);
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_stage ($name, $method, @args);
+#
+# DESCRIPTION
+#   This method invokes a named stage of the system, where $name is the name of
+#   the stage, $method is the name of the method for invoking the stage and
+#   @args are the arguments to the &method.
+# ------------------------------------------------------------------------------
+
+sub invoke_stage {
+  my ($self, $name, $method, @args) = @_;
+
+  # Print diagnostic at beginning of a stage
+  print '->', $name, ': start', "\n" if $self->verbose;
+  my $stime = time;
+
+  # Invoke the stage
+  my $rc = $self->$method (@args);
+
+  # Print diagnostic at end of a stage
+  my $total = time - $stime;
+  my $s_str = $total > 1 ? 'seconds' : 'second';
+  print '->', $name, ': ', $total, ' ', $s_str, "\n";
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_system (%args);
+#
+# DESCRIPTION
+#   This is a prototype method for invoking the system.
+# ------------------------------------------------------------------------------
+
+sub invoke_system {
+  my $self = shift;
+  my %args = @_;
+
+  print "Dummy code.\n";
+
+  return 0;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->parse_cfg($is_for_inheritance);
+#
+# DESCRIPTION
+#   This method calls other methods to parse the configuration file.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg {
+  my ($self, $is_for_inheritance) = @_;
+
+  # Read config file
+  # ----------------------------------------------------------------------------
+  if (!$self->cfg()->src() || !$self->cfg()->read_cfg($is_for_inheritance)) {
+    return;
+  }
+
+  if ($self->cfg->type ne $self->type) {
+    w_report 'ERROR: ', $self->cfg->src, ': not a ', $self->type,
+             ' config file.';
+    return;
+  }
+
+  # Strip out optional prefix from all labels
+  # ----------------------------------------------------------------------------
+  if ($self->cfg_prefix) {
+    for my $line (@{ $self->cfg->lines }) {
+      $line->prefix ($self->cfg_prefix);
+    }
+  }
+
+  # Filter lines from the configuration file
+  # ----------------------------------------------------------------------------
+  my @cfg_lines = grep {
+    $_->slabel                   and       # ignore empty/comment lines
+    index ($_->slabel, '%') != 0 and       # ignore user variable
+    not $_->slabel_starts_with_cfg ('INC') # ignore INC line
+  } @{ $self->cfg->lines };
+
+  # Parse the lines to read in the various settings, by calling the methods:
+  # $self->parse_cfg_XXX, where XXX is: header, inherit, dest, and the values
+  # in the list @{ $self->cfg_methods }.
+  # ----------------------------------------------------------------------------
+  my $rc = 1;
+  for my $name (@{ $self->cfg_methods }) {
+    my $method = 'parse_cfg_' . $name;
+    $self->$method (\@cfg_lines) or $rc = 0;
+  }
+
+  # Report warnings/errors
+  # ----------------------------------------------------------------------------
+  for my $line (@cfg_lines) {
+    $rc = 0 if not $line->parsed;
+    my $mesg = $line->format_error;
+    w_report $mesg if $mesg;
+  }
+
+  return ($rc);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_dest (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the destination settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_dest {
+  my ($self, $cfg_lines) = @_;
+
+  my $rc = 1;
+
+  # DEST/DIR declarations
+  # ----------------------------------------------------------------------------
+  my @lines  = grep {
+    $_->slabel_starts_with_cfg ('DEST') or $_->slabel_starts_with_cfg ('DIR')
+  } @$cfg_lines;
+
+  # Only ROOTDIR declarations are accepted
+  for my $line (@lines) {
+    my ($d, $method) = $line->slabel_fields;
+    $d = lc $d;
+    if ($method) {
+      $method = lc($method);
+    }
+
+    # Backward compatibility
+    $d = 'dest' if $d eq 'dir';
+
+    # Default to "rootdir"
+    $method = 'rootdir' if (not $method) or $method eq 'root';
+
+    # Only "rootdir" can be set
+    next unless $method eq 'rootdir';
+
+    $self->$d->$method (&expand_tilde ($line->value));
+    $line->parsed (1);
+  }
+
+  # Make sure root directory is set
+  # ----------------------------------------------------------------------------
+  if (not $self->dest->rootdir) {
+    w_report 'ERROR: ', $self->cfg->actual_src,
+             ': destination root directory not set.';
+    $rc = 0;
+  }
+
+  # Inherit destinations
+  # ----------------------------------------------------------------------------
+  @{$self->dest()->inherit()} = ();
+  my @nodes = @{$self->inherit()};
+  while (my $node = pop(@nodes)) {
+      push(@nodes, @{$node->inherit()});
+      push(@{$self->dest()->inherit()}, $node->dest());
+  }
+  @{$self->dest()->inherit()} = reverse(@{$self->dest()->inherit()});
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_header (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the header setting in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_header {
+  my ($self, $cfg_lines) = @_;
+
+  # Set header lines as "parsed"
+  map {$_->parsed (1)} grep {$_->slabel_starts_with_cfg ('CFGFILE')} @$cfg_lines;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_inherit (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the inherit setting in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_inherit {
+  my ($self, $cfg_lines) = @_;
+
+  # USE declaration
+  # ----------------------------------------------------------------------------
+  my @lines = grep {$_->slabel_starts_with_cfg ('USE')} @$cfg_lines;
+
+  # Check for cyclic dependency
+  if (@lines and grep {$_ eq $self->cfg->actual_src} @{ $self->inherited }) {
+    # Error if current configuration file is in its own inheritance hierarchy
+    w_report 'ERROR: ', $self->cfg->actual_src, ': attempt to inherit itself.';
+    $_->error ($_->label . ': ignored due to cyclic dependency.') for (@lines);
+    return 0;
+  }
+
+  my $rc = 1;
+
+  for my $line (@lines) {
+    # Invoke new instance of the current class
+    my $use = ref ($self)->new;
+
+    # Set configuration file, inheritance hierarchy
+    # and attempt to parse the configuration
+    $use->cfg->src  (&expand_tilde ($line->value));
+    $use->inherited ([$self->cfg->actual_src, @{ $self->inherited }]);
+    $use->parse_cfg(1); # 1 = is for inheritance
+
+    # Add to list of inherit configurations
+    push @{ $self->inherit }, $use;
+
+    $line->parsed (1);
+  }
+
+  # Check locks in inherited destination
+  # ----------------------------------------------------------------------------
+  for my $use (@{ $self->inherit }) {
+    $rc = 0 unless $use->check_lock;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @cfglines = $obj->to_cfglines ();
+#
+# DESCRIPTION
+#   This method returns the configuration lines of this object.
+# ------------------------------------------------------------------------------
+
+sub to_cfglines {
+  my ($self) = @_;
+
+  my @inherited_dests = map {
+    FCM1::CfgLine->new (
+      label => $self->cfglabel ('USE'), value => $_->dest->rootdir
+    );
+  } @{ $self->inherit };
+
+  return (
+    FCM1::CfgLine::comment_block ('File header'),
+    FCM1::CfgLine->new (
+      label => $self->cfglabel ('CFGFILE') . $FCM1::Config::DELIMITER . 'TYPE',
+      value => $self->type,
+    ),
+    FCM1::CfgLine->new (
+      label => $self->cfglabel ('CFGFILE') . $FCM1::Config::DELIMITER . 'VERSION',
+      value => '1.0',
+    ),
+    FCM1::CfgLine->new (),
+
+    @inherited_dests,
+
+    FCM1::CfgLine::comment_block ('Destination'),
+    ($self->dest->to_cfglines()),
+  );
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Dest.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Dest.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Dest.pm	(revision 5095)
@@ -0,0 +1,899 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Dest
+#
+# DESCRIPTION
+#   This class contains methods to set up a destination location of an FCM
+#   extract/build.
+#
+# ------------------------------------------------------------------------------
+use warnings;
+use strict;
+
+package FCM1::Dest;
+use base qw{FCM1::Base};
+
+use Carp             qw{croak}                          ;
+use Cwd              qw{cwd}                            ;
+use FCM1::CfgLine                                       ;
+use FCM1::Timer      qw{timestamp_command}              ;
+use FCM1::Util       qw{run_command touch_file w_report};
+use File::Basename   qw{basename dirname}               ;
+use File::Find       qw{find}                           ;
+use File::Path       qw{mkpath rmtree}                  ;
+use File::Spec                                          ;
+use Sys::Hostname    qw{hostname}                       ;
+use Text::ParseWords qw{shellwords}                     ;
+
+# Useful variables
+# ------------------------------------------------------------------------------
+# List of configuration files
+our @cfgfiles = (
+  'bldcfg',     # default location of the build configuration file
+  'extcfg',     # default location of the extract configuration file
+);
+
+# List of cache and configuration files, according to the dest type
+our @cfgfiles_type = (
+  'cache',     # default location of the cache file
+  'cfg',       # default location of the configuration file
+  'parsedcfg', # default location of the as-parsed configuration file
+);
+
+# List of lock files
+our @lockfiles = (
+  'bldlock',    # the build lock file
+  'extlock',    # the extract lock file
+);
+
+# List of misc files
+our @miscfiles_bld = (
+  'bldrunenvsh', # the build run environment shell script
+  'bldmakefile', # the build Makefile
+);
+
+# List of sub-directories created by extract
+our @subdirs_ext = (
+  'cfgdir',     # sub-directory for configuration files
+  'srcdir',     # sub-directory for source tree
+);
+
+# List of sub-directories that can be archived by "tar" at end of build
+our @subdirs_tar = (
+  'donedir',    # sub-directory for "done" files
+  'flagsdir',   # sub-directory for "flags" files
+  'incdir',     # sub-directory for include files
+  'ppsrcdir',   # sub-directory for pre-process source tree
+  'objdir',     # sub-directory for object files
+);
+
+# List of sub-directories created by build
+our @subdirs_bld = (
+  'bindir',     # sub-directory for executables
+  'etcdir',     # sub-directory for miscellaneous files
+  'libdir',     # sub-directory for object libraries
+  'tmpdir',     # sub-directory for temporary build files
+  @subdirs_tar, # -see above-
+);
+
+# List of sub-directories under rootdir
+our @subdirs = (
+  'cachedir',   # sub-directory for caches
+  @subdirs_ext, # -see above-
+  @subdirs_bld, # -see above-
+);
+
+# List of inherited search paths
+# "rootdir" + all @subdirs, with "XXXdir" replaced with "XXXpath"
+our @paths = (
+    'rootpath',
+    (map {my $key = $_; $key =~ s{dir\z}{path}msx; $key} @subdirs),
+);
+
+# List of properties and their default values.
+my %PROP_OF = (
+  # the original destination (if current destination is a mirror)
+  'dest0'                => undef,
+  # list of inherited FCM1::Dest objects
+  'inherit'              => [],
+  # remote login name
+  'logname'              => scalar(getpwuid($<)),
+  # lock file
+  'lockfile'             => undef,
+  # remote machine
+  'machine'              => hostname(),
+  # mirror command to use
+  'mirror_cmd'           => 'rsync',
+  # (for rsync) remote mkdir, the remote shell command
+  'rsh_mkdir_rsh'        => 'ssh',
+  # (for rsync) remote mkdir, the remote shell command flags
+  'rsh_mkdir_rshflags'   => '-n -oBatchMode=yes',
+  # (for rsync) remote mkdir, the remote shell command
+  'rsh_mkdir_mkdir'      => 'mkdir',
+  # (for rsync) remote mkdir, the remote shell command flags
+  'rsh_mkdir_mkdirflags' => '-p',
+  # (for rsync) remote mkdir, the remote shell command
+  'rsync'                => 'rsync',
+  # (for rsync) remote mkdir, the remote shell command flags
+  'rsyncflags'           => q{-a --exclude='.*' --delete-excluded}
+                            . q{ --timeout=900 --rsh='ssh -oBatchMode=yes'},
+  # destination root directory
+  'rootdir'              => undef,
+  # destination type, "bld" (default) or "ext"
+  'type'                 => 'bld',
+);
+# Hook for property setter
+my %PROP_HOOK_OF = (
+  'inherit' => \&_reset_inherit,
+  'rootdir' => \&_reset_rootdir,
+);
+
+# Mirror implementations
+my %MIRROR_IMPL_OF = (
+  rdist => \&_mirror_with_rdist,
+  rsync => \&_mirror_with_rsync,
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::Dest->new(%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::Dest class. See above for
+#   allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my ($class, %args) = @_;
+  my $self = bless(FCM1::Base->new(%args), $class);
+  while (my ($key, $value) = each(%args)) {
+    $key = lc($key);
+    if (exists($PROP_OF{$key})) {
+        $self->{$key} = $value;
+    }
+  }
+  for my $key (@subdirs, @paths, @lockfiles, @cfgfiles) {
+    $self->{$key} = undef;
+  }
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->DESTROY;
+#
+# DESCRIPTION
+#   This method is called automatically when the FCM1::Dest object is
+#   destroyed.
+# ------------------------------------------------------------------------------
+
+sub DESTROY {
+  my $self = shift;
+
+  # Remove the lockfile if it is set
+  unlink $self->lockfile if $self->lockfile and -f $self->lockfile;
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in %PROP_OF.
+# ------------------------------------------------------------------------------
+
+while (my ($key, $default) = each(%PROP_OF)) {
+  no strict 'refs';
+  *{$key} = sub {
+    my $self = shift();
+    # Set property to specified value
+    if (@_) {
+      $self->{$key} = $_[0];
+      if (exists($PROP_HOOK_OF{$key})) {
+        $PROP_HOOK_OF{$key}->($self, $key);
+      }
+    }
+    # Sets default where possible
+    if (!defined($self->{$key})) {
+      $self->{$key} = $default;
+    }
+    return $self->{$key};
+  };
+}
+
+# Remote shell property: deprecated.
+sub remote_shell {
+  my $self = shift();
+  $self->rsh_mkdir_rsh(@_);
+}
+
+# Resets properties associated with root directory.
+sub _reset_rootdir {
+  my $self = shift();
+  for my $key (@cfgfiles, @lockfiles, @miscfiles_bld, @subdirs) {
+    $self->{$key} = undef;
+  }
+}
+
+# Reset properties associated with inherited paths.
+sub _reset_inherit {
+  my $self = shift();
+  for my $key (@paths) {
+    $self->{$key} = undef;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#
+# DESCRIPTION
+#   This method returns X, where X is a location derived from rootdir, and can
+#   be one of:
+#     bindir, bldcfg, blddir, bldlock, bldrunenv, cache, cachedir, cfg, cfgdir,
+#     donedir, etcdir, extcfg, extlock, flagsdir, incdir, libdir, parsedcfg,
+#     ppsrcdir, objdir, or tmpdir.
+#
+#   Details of these properties are explained earlier.
+# ------------------------------------------------------------------------------
+
+for my $name (@cfgfiles, @cfgfiles_type, @lockfiles, @miscfiles_bld, @subdirs) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # If variable not set, derive it from rootdir
+    if ($self->rootdir and not defined $self->{$name}) {
+      if ($name eq 'cache') {
+        # Cache file under root/.cache
+        $self->{$name} =  File::Spec->catfile (
+          $self->cachedir, $self->setting ('CACHE'),
+        );
+
+      } elsif ($name eq 'cfg') {
+        # Configuration file of current type
+        my $method = $self->type . 'cfg';
+        $self->{$name} = $self->$method;
+
+      } elsif (grep {$name eq $_} @cfgfiles) {
+        # Configuration files under the root/cfg
+        (my $label = uc ($name)) =~ s/CFG//;
+        $self->{$name} = File::Spec->catfile (
+          $self->cfgdir, $self->setting ('CFG_NAME', $label),
+        );
+
+      } elsif (grep {$name eq $_} @lockfiles) {
+        # Lock file
+        $self->{$name} = File::Spec->catfile (
+          $self->rootdir, $self->setting ('LOCK', uc ($name)),
+        );
+
+      } elsif (grep {$name eq $_} @miscfiles_bld) {
+        # Misc file
+        $self->{$name} = File::Spec->catfile (
+          $self->rootdir, $self->setting ('BLD_MISC', uc ($name)),
+        );
+
+      } elsif ($name eq 'parsedcfg') {
+        # As-parsed configuration file of current type
+        $self->{$name} = File::Spec->catfile (
+          dirname ($self->cfg),
+          $self->setting (qw/CFG_NAME PARSED/) . basename ($self->cfg),
+        )
+
+      } elsif (grep {$name eq $_} @subdirs) {
+        # Sub-directories under the root
+        (my $label = uc ($name)) =~ s/DIR//;
+        $self->{$name} = File::Spec->catfile (
+          $self->rootdir,
+          $self->setting ('DIR', $label),
+          ($name eq 'cachedir' ? '.' . $self->type : ()),
+        );
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#
+# DESCRIPTION
+#   This method returns X, an array containing the search path of a destination
+#   directory, which can be one of:
+#     binpath, bldpath, cachepath, cfgpath, donepath, etcpath, flagspath,
+#     incpath, libpath, ppsrcpath, objpath, rootpath, srcpath, or tmppath,
+#
+#   Details of these properties are explained earlier.
+# ------------------------------------------------------------------------------
+
+for my $name (@paths) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    (my $dir = $name) =~ s/path/dir/;
+
+    if ($self->$dir and not defined $self->{$name}) {
+      my @path = ();
+
+      # Recursively inherit the search path
+      for my $d (@{ $self->inherit }) {
+        unshift @path, $d->$dir;
+      }
+
+      # Place the path of the current build in the front
+      unshift @path, $self->$dir;
+
+      $self->{$name} = \@path;
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->archive ();
+#
+# DESCRIPTION
+#   This method creates TAR archives for selected sub-directories.
+# ------------------------------------------------------------------------------
+
+sub archive {
+  my $self = shift;
+
+  # Save current directory
+  my $cwd = cwd ();
+
+  my $tar      = $self->setting (qw/OUTFILE_EXT TAR/);
+  my $verbose  = $self->verbose;
+
+  for my $name (@subdirs_tar) {
+    my $dir = $self->$name;
+
+    # Ignore unless sub-directory exists
+    next unless -d $dir;
+
+    # Change to container directory
+    my $base = basename ($dir);
+    print 'cd ', dirname ($dir), "\n" if $verbose > 2;
+    chdir dirname ($dir);
+
+    # Run "tar" command
+    my $rc = &run_command (
+      [qw/tar -czf/, $base . $tar, $base],
+      PRINT => $verbose > 1, ERROR => 'warn',
+    );
+
+    # Remove sub-directory
+    &run_command ([qw/rm -rf/, $base], PRINT => $verbose > 1) if not $rc;
+  }
+
+  # Change back to "current" directory
+  print 'cd ', $cwd, "\n" if $verbose > 2;
+  chdir $cwd;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $authority = $obj->authority();
+#
+# DESCRIPTION
+#   Returns LOGNAME@MACHINE for this destination if LOGNAME is defined and not
+#   the same as the user ID of the current process. Returns MACHINE if LOGNAME
+#   is the same as the user ID of the current process, but MACHINE is not the
+#   same as the current hostname. Returns an empty string if LOGNAME and
+#   MACHINE are not defined or are the same as in the current process.
+# ------------------------------------------------------------------------------
+
+sub authority {
+  my $self = shift;
+  my $return = '';
+
+  if ($self->logname ne $self->config->user_id) {
+    $return = $self->logname . '@' . $self->machine;
+
+  } elsif ($self->machine ne &hostname()) {
+    $return = $self->machine;
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->clean([ITEM => <list>,] [MODE => 'ALL|CONTENT|EMPTY',]);
+#
+# DESCRIPTION
+#   This method removes files/directories from the destination. If ITEM is set,
+#   it must be a reference to a list of method names for files/directories to
+#   be removed. Otherwise, the list is determined by the destination type. If
+#   MODE is ALL, all directories/files created by the extract/build are
+#   removed. If MODE is CONTENT, only contents within sub-directories are
+#   removed. If MODE is EMPTY (default), only empty sub-directories are
+#   removed.
+# ------------------------------------------------------------------------------
+
+sub clean {
+  my ($self, %args) = @_;
+  my $mode = exists $args{MODE} ? $args{MODE} : 'EMPTY';
+  my $rc = 1;
+  my @names
+    = $args{ITEM}            ? @{$args{ITEM}}
+    : $self->type() eq 'ext' ? ('cachedir', @subdirs_ext)
+    :                          ('cachedir', @subdirs_bld, @miscfiles_bld)
+    ;
+  my @items;
+  if ($mode eq 'CONTENT') {
+    for my $name (@names) {
+      my $item = $self->$name();
+      push(@items, _directory_contents($item));
+    }
+  }
+  else {
+    for my $name (@names) {
+      my $item = $self->$name();
+      if ($mode eq 'ALL' || -d $item && !_directory_contents($item)) {
+        push(@items, $item);
+      }
+    }
+  }
+  for my $item (@items) {
+    if ($self->verbose() >= 2) {
+      printf("%s: remove\n", $item);
+    }
+    eval {rmtree($item)};
+    if ($@) {
+      w_report($@);
+      $rc = 0;
+    }
+  }
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->create ([DIR => <dir-list>,]);
+#
+# DESCRIPTION
+#   This method creates the directories of a destination. If DIR is set, it
+#   must be a reference to a list of sub-directories to be created.  Otherwise,
+#   the sub-directory list is determined by the destination type. It returns
+#   true if the destination is created or if it exists and is writable.
+# ------------------------------------------------------------------------------
+
+sub create {
+  my ($self, %args) = @_;
+
+  my $rc = 1;
+
+  my @dirs;
+  if (exists $args{DIR} and $args{DIR}) {
+    # Create only selected sub-directories
+    @dirs = @{ $args{DIR} };
+
+  } else {
+    # Create rootdir, cachedir and read-write sub-directories for extract/build
+    @dirs = (
+      qw/rootdir cachedir/,
+      ($self->type eq 'ext' ? @subdirs_ext : @subdirs_bld),
+    );
+  }
+
+  for my $name (@dirs) {
+    my $dir = $self->$name;
+
+    # Create directory if it does not already exist
+    if (not -d $dir) {
+      print 'Make directory: ', $dir, "\n" if $self->verbose > 1;
+      mkpath $dir;
+    }
+
+    # Check whether directory exists and is writable
+    if (!-d $dir) {
+      w_report 'ERROR: ', $dir, ': cannot create destination.';
+      $rc = 0;
+    }
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->create_bldrunenvsh ();
+#
+# DESCRIPTION
+#   This method creates the runtime environment script for the build.
+# ------------------------------------------------------------------------------
+
+sub create_bldrunenvsh {
+  my $self = shift;
+
+  # Path to executable files and directory for misc files
+  my @bin_paths = grep {_directory_contents($_)} @{$self->binpath()};
+  my $bin_dir = -d $self->bindir() ? $self->bindir() : undef;
+  my $etc_dir = _directory_contents($self->etcdir()) ? $self->etcdir() : undef;
+
+  # Create a runtime environment script if necessary
+  if (@bin_paths || $etc_dir) {
+    my $path = $self->bldrunenvsh();
+    open(my $handle, '>', $path) || croak("$path: cannot open ($!)\n");
+    printf($handle "#!%s\n", $self->setting(qw/TOOL SHELL/));
+    if (@bin_paths) {
+      printf($handle "PATH=%s:\$PATH\n", join(':', @bin_paths));
+      print($handle "export PATH\n");
+    }
+    if ($etc_dir) {
+      printf($handle "FCM_ETCDIR=%s\n", $etc_dir);
+      print($handle "export FCM_ETCDIR\n");
+    }
+    close($handle) || croak("$path: cannot close ($!)\n");
+
+    # Create symbolic links fcm_env.ksh and bin/fcm_env.ksh for backward
+    # compatibility
+    my $FCM_ENV_KSH = 'fcm_env.ksh';
+    for my $link (
+      File::Spec->catfile($self->rootdir, $FCM_ENV_KSH),
+      ($bin_dir ? File::Spec->catfile($bin_dir, $FCM_ENV_KSH) : ()),
+    ) {
+      if (-l $link && readlink($link) ne $path || -e $link) {
+        unlink($link);
+      }
+      if (!-l $link) {
+        symlink($path, $link) || croak("$link: cannot create symbolic link\n");
+      }
+    }
+  }
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->dearchive ();
+#
+# DESCRIPTION
+#   This method extracts from TAR archives for selected sub-directories.
+# ------------------------------------------------------------------------------
+
+sub dearchive {
+  my $self = shift;
+
+  my $tar     = $self->setting (qw/OUTFILE_EXT TAR/);
+  my $verbose = $self->verbose;
+
+  # Extract archives if necessary
+  for my $name (@subdirs_tar) {
+    my $tar_file = $self->$name . $tar;
+
+    # Check whether tar archive exists for the named sub-directory
+    next unless -f $tar_file;
+
+    # If so, extract the archive and remove it afterwards
+    &run_command ([qw/tar -xzf/, $tar_file], PRINT => $verbose > 1);
+    &run_command ([qw/rm -f/, $tar_file], PRINT => $verbose > 1);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $name = $obj->get_pkgname_of_path ($path);
+#
+# DESCRIPTION
+#   This method returns the package name of $path if $path is in (a relative
+#   path of) $self->srcdir, or undef otherwise.
+# ------------------------------------------------------------------------------
+
+sub get_pkgname_of_path {
+  my ($self, $path) = @_;
+
+  my $relpath = File::Spec->abs2rel ($path, $self->srcdir);
+  my $name = $relpath ? [File::Spec->splitdir ($relpath)] : undef;
+
+  return $name;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %src = $obj->get_source_files ();
+#
+# DESCRIPTION
+#   This method returns a hash (keys = package names, values = file names)
+#   under $self->srcdir.
+# ------------------------------------------------------------------------------
+
+sub get_source_files {
+  my $self = shift;
+
+  my %src;
+  if ($self->srcdir and -d $self->srcdir) {
+    &find (sub {
+      return if /^\./;                    # ignore system/hidden file
+      return if -d $File::Find::name;     # ignore directory
+
+      my $name = join (
+        '__', @{ $self->get_pkgname_of_path ($File::Find::name) },
+      );
+      $src{$name} = $File::Find::name;
+    }, $self->srcdir);
+  }
+
+  return \%src;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->mirror (\@items);
+#
+# DESCRIPTION
+#   This method mirrors @items (list of method names for directories or files)
+#   from $dest0 (which must be an instance of FCM1::Dest for a local
+#   destination) to this destination.
+# ------------------------------------------------------------------------------
+
+sub mirror {
+  my ($self, $items_ref) = @_;
+  if ($self->authority() || $self->dest0()->rootdir() ne $self->rootdir()) {
+    # Diagnostic
+    if ($self->verbose()) {
+      printf(
+        "Destination: %s\n",
+        ($self->authority() ? $self->authority() . q{:} : q{}) . $self->rootdir()
+      );
+    }
+    if ($MIRROR_IMPL_OF{$self->mirror_cmd()}) {
+      $MIRROR_IMPL_OF{$self->mirror_cmd()}->($self, $self->dest0(), $items_ref);
+    }
+    else {
+      # Unknown mirroring tool
+      w_report($self->mirror_cmd, ': unknown mirroring tool, abort.');
+      return 0;
+    }
+  }
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->_mirror_with_rdist ($dest0, \@items);
+#
+# DESCRIPTION
+#   This internal method implements $self->mirror with "rdist".
+# ------------------------------------------------------------------------------
+
+sub _mirror_with_rdist {
+  my ($self, $dest0, $items) = @_;
+
+  my $rhost = $self->authority ? $self->authority : &hostname();
+
+  # Print distfile content to temporary file
+  my @distfile = ();
+  for my $label (@$items) {
+    push @distfile, '( ' . $dest0->$label . ' ) -> ' . $rhost . "\n";
+    push @distfile, '  install ' . $self->$label . ';' . "\n";
+  }
+
+  # Set up mirroring command (use "rdist" at the moment)
+  my $command = 'rdist -R';
+  $command   .= ' -q' unless $self->verbose > 1;
+  $command   .= ' -f - 1>/dev/null';
+
+  # Diagnostic
+  my $croak = 'Cannot execute "' . $command . '"';
+  if ($self->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 $self->verbose > 2;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->_mirror_with_rsync($dest0, \@items);
+#
+# DESCRIPTION
+#   This internal method implements $self->mirror() with "rsync".
+# ------------------------------------------------------------------------------
+
+sub _mirror_with_rsync {
+  my ($self, $dest0, $items_ref) = @_;
+  my @rsh_mkdir;
+  if ($self->authority()) {
+    @rsh_mkdir = (
+        $self->rsh_mkdir_rsh(),
+        shellwords($self->rsh_mkdir_rshflags()),
+        $self->authority(),
+        $self->rsh_mkdir_mkdir(),
+        shellwords($self->rsh_mkdir_mkdirflags()),
+    );
+  }
+  my @rsync = ($self->rsync(), shellwords($self->rsyncflags()));
+  my @rsync_verbose = ($self->verbose() > 2 ? '-v' : ());
+  my $auth = $self->authority() ? $self->authority() . q{:} : q{};
+  for my $item (@{$items_ref}) {
+    # Create container directory, as rsync does not do it automatically
+    my $dir = dirname($self->$item());
+    if (@rsh_mkdir) {
+      run_command([@rsh_mkdir, $dir], TIME => $self->verbose() > 2);
+    }
+    else {
+      mkpath($dir);
+    }
+    run_command(
+      [@rsync, @rsync_verbose, $dest0->$item(), $auth . $dir],
+      TIME => $self->verbose > 2,
+    );
+  }
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->set_lock ();
+#
+# DESCRIPTION
+#   This method sets a lock in the current destination.
+# ------------------------------------------------------------------------------
+
+sub set_lock {
+  my $self = shift;
+
+  $self->lockfile ();
+
+  if ($self->type eq 'ext' and not $self->dest0) {
+    # Only set an extract lock for the local destination
+    $self->lockfile ($self->extlock);
+
+  } elsif ($self->type eq 'bld') {
+    # Set a build lock
+    $self->lockfile ($self->bldlock);
+  }
+
+  return &touch_file ($self->lockfile) if $self->lockfile;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @cfglines = $obj->to_cfglines ([$index]);
+#
+# DESCRIPTION
+#   This method returns a list of configuration lines for the current
+#   destination. If it is set, $index is the index number of the current
+#   destination.
+# ------------------------------------------------------------------------------
+
+sub to_cfglines {
+  my ($self, $index) = @_;
+
+  my $PREFIX = $self->cfglabel($self->dest0() ? 'RDEST' : 'DEST');
+  my $SUFFIX = ($index ? $FCM1::Config::DELIMITER . $index : q{});
+
+  my @return = (
+    FCM1::CfgLine->new(label => $PREFIX . $SUFFIX, value => $self->rootdir()),
+  );
+  if ($self->dest0()) {
+    for my $name (qw{
+      logname
+      machine
+      mirror_cmd
+      rsh_mkdir_rsh
+      rsh_mkdir_rshflags
+      rsh_mkdir_mkdir
+      rsh_mkdir_mkdirflags
+      rsync
+      rsyncflags
+    }) {
+      if ($self->{$name} && $self->{$name} ne $PROP_OF{$name}) { # not default
+        push(
+          @return,
+          FCM1::CfgLine->new(
+            label => $PREFIX . $FCM1::Config::DELIMITER . uc($name) . $SUFFIX,
+            value => $self->{$name},
+          ),
+        );
+      }
+    }
+  }
+
+  return @return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $obj->write_rules ();
+#
+# DESCRIPTION
+#   This method returns a string containing Makefile variable declarations for
+#   directories and search paths in this destination.
+# ------------------------------------------------------------------------------
+
+sub write_rules {
+  my $self   = shift;
+  my $return = '';
+
+  # FCM_*DIR*
+  for my $i (0 .. @{ $self->inherit }) {
+    for my $name (@paths) {
+      (my $label = $name) =~ s/path$/dir/;
+      my $dir = $name eq 'rootpath' ? $self->$name->[$i] : File::Spec->catfile (
+        '$(FCM_ROOTDIR' . ($i ? $i : '') . ')',
+        File::Spec->abs2rel ($self->$name->[$i], $self->rootpath->[$i]),
+      );
+
+      $return .= ($i ? '' : 'export ') . 'FCM_' . uc ($label) . ($i ? $i : '') .
+                 ' := ' . $dir . "\n";
+    }
+  }
+
+  # FCM_*PATH
+  for my $name (@paths) {
+    (my $label = $name) =~ s/path$/dir/;
+
+    $return .= 'export FCM_' . uc ($name) . ' := ';
+    for my $i (0 .. @{ $self->$name } - 1) {
+      $return .= ($i ? ':' : '') . '$(FCM_' . uc ($label) . ($i ? $i : '') . ')';
+    }
+    $return .= "\n";
+  }
+
+  $return .= "\n";
+
+  return $return;
+}
+
+# Returns contents in directory.
+sub _directory_contents {
+  my $path = shift();
+  if (!-d $path) {
+    return;
+  }
+  opendir(my $handle, $path) || croak("$path: cannot open directory ($!)\n");
+  my @items = grep {$_ ne q{.} && $_ ne q{..}} readdir($handle);
+  closedir($handle);
+  map {File::Spec->catfile($path . $_)} @items;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Exception.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Exception.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Exception.pm	(revision 5095)
@@ -0,0 +1,108 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Exception;
+use overload (q{""} => \&as_string);
+
+use Scalar::Util qw{blessed};
+
+# ------------------------------------------------------------------------------
+# Returns true if $e is a blessed instance of this class.
+sub caught {
+    my ($class, $e) = @_;
+    return (blessed($e) && $e->isa($class));
+}
+
+# ------------------------------------------------------------------------------
+# Constructor
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless(
+        {message => q{unknown problem}, ($args_ref ? %{$args_ref} : ())},
+        $class,
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns a string representation of this exception
+sub as_string {
+    my ($self) = @_;
+    return sprintf("%s: %s\n", blessed($self), $self->get_message());
+}
+
+# ------------------------------------------------------------------------------
+# Returns the message of this exception
+sub get_message {
+    my ($self) = @_;
+    return $self->{message};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::Exception
+
+=head1 SYNOPSIS
+
+    use FCM1::Exception;
+    eval {
+        croak(FCM1::Exception->new({message => $message}));
+    };
+    if ($@) {
+        if (FCM1::Exception->caught($@)) {
+            print({STDERR} $@);
+        }
+    }
+
+=head1 DESCRIPTION
+
+This exception is raised when there is a generic problem in FCM.
+
+=head1 METHODS
+
+=over 4
+
+=item $class->caught($e)
+
+Returns true if $e is a blessed instance of this class.
+
+=item $class->new({message=E<gt>$message})
+
+Returns a new instance of this exception. Its first argument must be a
+reference to a hash containing the detailed I<message> of the exception.
+
+=item $e->as_string()
+
+Returns a string representation of this exception.
+
+=item $e->get_message()
+
+Returns the detailed message of this exception.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Extract.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Extract.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Extract.pm	(revision 5095)
@@ -0,0 +1,1132 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Extract
+#
+# DESCRIPTION
+#   This is the top level class for the FCM extract system.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::Extract;
+@ISA = qw(FCM1::ConfigSystem);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use File::Path;
+use File::Spec;
+
+# FCM component modules
+use FCM1::CfgFile;
+use FCM1::CfgLine;
+use FCM1::Config;
+use FCM1::ConfigSystem;
+use FCM1::Dest;
+use FCM1::ExtractFile;
+use FCM1::ExtractSrc;
+use FCM1::Keyword;
+use FCM1::ReposBranch;
+use FCM1::SrcDirLayer;
+use FCM1::Util;
+
+# List of scalar property methods for this class
+my @scalar_properties = (
+ 'bdeclare', # list of build declarations
+ 'branches', # list of repository branches
+ 'conflict', # conflict mode
+ 'rdest'   , # remote destination information
+);
+
+# List of hash property methods for this class
+my @hash_properties = (
+ 'srcdirs' , # list of source directory extract info
+ 'files',    # list of files processed key=pkgname, value=FCM1::ExtractFile
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::Extract->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::Extract class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::ConfigSystem->new (%args);
+
+  $self->{$_} = undef for (@scalar_properties);
+
+  $self->{$_} = {} for (@hash_properties);
+
+  bless $self, $class;
+
+  # List of sub-methods for parse_cfg
+  push @{ $self->cfg_methods }, (qw/rdest bld conflict project/);
+
+  # System type
+  $self->type ('ext');
+
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'bdeclare' or $name eq 'branches') {
+        # Reference to an array
+        $self->{$name} = [];
+
+      } elsif ($name eq 'rdest') {
+        # New extract destination local/remote
+        $self->{$name} = FCM1::Dest->new (DEST0 => $self->dest(), TYPE => 'ext');
+
+      } elsif ($name eq 'conflict') {
+        # Conflict mode, default to "merge"
+        $self->{$name} = 'merge';
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %hash = %{ $obj->X () };
+#   $obj->X (\%hash);
+#
+#   $value = $obj->X ($index);
+#   $obj->X ($index, $value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @hash_properties.
+#
+#   If no argument is set, this method returns a hash containing a list of
+#   objects. If an argument is set and it is a reference to a hash, the objects
+#   are replaced by the specified hash.
+#
+#   If a scalar argument is specified, this method returns a reference to an
+#   object, if the indexed object exists or undef if the indexed object does
+#   not exist. If a second argument is set, the $index element of the hash will
+#   be set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+for my $name (@hash_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my ($self, $arg1, $arg2) = @_;
+
+    # Ensure property is defined as a reference to a hash
+    $self->{$name} = {} if not defined ($self->{$name});
+
+    # Argument 1 can be a reference to a hash or a scalar index
+    my ($index, %hash);
+
+    if (defined $arg1) {
+      if (ref ($arg1) eq 'HASH') {
+        %hash = %$arg1;
+
+      } else {
+        $index = $arg1;
+      }
+    }
+
+    if (defined $index) {
+      # A scalar index is defined, set and/or return the value of an element
+      $self->{$name}{$index} = $arg2 if defined $arg2;
+
+      return (
+        exists $self->{$name}{$index} ? $self->{$name}{$index} : undef
+      );
+
+    } else {
+      # A scalar index is not defined, set and/or return the hash
+      $self->{$name} = \%hash if defined $arg1;
+      return $self->{$name};
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->check_lock_is_allowed ($lock);
+#
+# DESCRIPTION
+#   This method returns true if it is OK for $lock to exist in the destination.
+# ------------------------------------------------------------------------------
+
+sub check_lock_is_allowed {
+  my ($self, $lock) = @_;
+
+  # Allow existence of build lock in inherited extract
+  return ($lock eq $self->dest->bldlock and @{ $self->inherited });
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_extract ();
+#
+# DESCRIPTION
+#   This method invokes the extract stage of the extract system. It returns
+#   true on success.
+# ------------------------------------------------------------------------------
+
+sub invoke_extract {
+  my $self = shift;
+
+  my $rc = 1;
+
+  my @methods = (
+    'expand_cfg',       # expand URL, revision keywords, relative path, etc
+    'create_dir_stack', # analyse the branches to create an extract sequence
+    'extract_src',      # use the sequence to extract source to destination
+    'write_cfg',        # generate final configuration file
+    'write_cfg_bld',    # generate build configuration file
+  );
+
+  for my $method (@methods) {
+    $rc = $self->$method if $rc;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_mirror ();
+#
+# DESCRIPTION
+#   This method invokes the mirror stage of the extract system. It returns
+#   true on success.
+# ------------------------------------------------------------------------------
+
+sub invoke_mirror {
+  my $self = shift;
+  return $self->rdest->mirror ([qw/bldcfg extcfg srcdir/]);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->invoke_system ();
+#
+# DESCRIPTION
+#   This method invokes the extract system. It returns true on success.
+# ------------------------------------------------------------------------------
+
+sub invoke_system {
+  my $self = shift;
+
+  my $rc = 1;
+  
+  $rc = $self->invoke_stage ('Extract', 'invoke_extract');
+  $rc = $self->invoke_stage ('Mirror', 'invoke_mirror')
+    if $rc and $self->rdest->rootdir;
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_rdest(\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the remote destination settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_rdest {
+  my ($self, $cfg_lines_ref) = @_;
+
+  # RDEST declarations
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg('RDEST')} @{$cfg_lines_ref}) {
+    my ($d, $method) = map {lc($_)} $line->slabel_fields();
+    $method ||= 'rootdir';
+    if ($self->rdest()->can($method)) {
+      $self->rdest()->$method(expand_tilde($line->value()));
+      $line->parsed(1);
+    }
+  }
+
+  # MIRROR declaration, deprecated = RDEST::MIRROR_CMD
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg('MIRROR')} @{$cfg_lines_ref}) {
+    $self->rdest()->mirror_cmd($line->value());
+    $line->parsed(1);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_bld (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the build configurations in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_bld {
+  my ($self, $cfg_lines) = @_;
+
+  # BLD declarations
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg ('BDECLARE')} @$cfg_lines) {
+    # Remove BLD from label
+    my @words = $line->slabel_fields;
+
+    # Check that a declaration follows BLD
+    next if @words <= 1;
+
+    push @{ $self->bdeclare }, FCM1::CfgLine->new (
+      LABEL  => join ($FCM1::Config::DELIMITER, @words),
+      PREFIX => $self->cfglabel ('BDECLARE'),
+      VALUE  => $line->value,
+    );
+    $line->parsed (1);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_conflict (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the conflict settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_conflict {
+  my ($self, $cfg_lines) = @_;
+
+  # Deprecated: Override mode setting
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg ('OVERRIDE')} @$cfg_lines) {
+    next if ($line->slabel_fields) > 1;
+    $self->conflict ($line->bvalue ? 'override' : 'fail');
+    $line->parsed (1);
+    $line->warning($line->slabel . ' is deprecated. Use ' .
+                   $line->cfglabel('CONFLICT') . ' override|merge|fail.');
+  }
+
+  # Conflict mode setting
+  # ----------------------------------------------------------------------------
+  my @conflict_modes = qw/fail merge override/;
+  my $conflict_modes_pattern = join ('|', @conflict_modes);
+  for my $line (grep {$_->slabel_starts_with_cfg ('CONFLICT')} @$cfg_lines) {
+    if ($line->value =~ /$conflict_modes_pattern/i) {
+      $self->conflict (lc ($line->value));
+      $line->parsed (1);
+
+    } elsif ($line->value =~ /^[012]$/) {
+      $self->conflict ($conflict_modes[$line->value]);
+      $line->parsed (1);
+
+    } else {
+      $line->error ($line->value, ': invalid value');
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->parse_cfg_project (\@cfg_lines);
+#
+# DESCRIPTION
+#   This method parses the project settings in the @cfg_lines.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg_project {
+  my ($self, $cfg_lines) = @_;
+
+  # Flag to indicate that a declared branch revision must match with its changed
+  # revision
+  # ----------------------------------------------------------------------------
+  for my $line (grep {$_->slabel_starts_with_cfg ('REVMATCH')} @$cfg_lines) {
+    next if ($line->slabel_fields) > 1;
+    $self->setting ([qw/EXT_REVMATCH/], $line->bvalue);
+    $line->parsed (1);
+  }
+
+  # Repository, revision and source directories
+  # ----------------------------------------------------------------------------
+  for my $name (qw/repos revision dirs expdirs/) {
+    my @lines = grep {
+      $_->slabel_starts_with_cfg (uc ($name)) or
+      $name eq 'revision' and $_->slabel_starts_with_cfg ('VERSION');
+    } @$cfg_lines;
+    for my $line (@lines) {
+      my @names = $line->slabel_fields;
+      shift @names;
+
+      # Detemine package and tag
+      my $tag     = pop @names;
+      my $pckroot = $names[0];
+      my $pck     = join ($FCM1::Config::DELIMITER, @names);
+
+      # Check that $tag and $pckroot are defined
+      next unless $tag and $pckroot;
+
+      # Check if branch already exists.
+      # If so, set $branch to point to existing branch
+      my $branch = undef;
+      for (@{ $self->branches }) {
+        next unless $_->package eq $pckroot and $_->tag eq $tag;
+
+        $branch = $_;
+        last;
+      }
+
+      # Otherwise, create a new branch
+      if (not $branch) {
+        $branch = FCM1::ReposBranch->new (PACKAGE => $pckroot, TAG => $tag,);
+
+        push @{ $self->branches }, $branch;
+      }
+
+      if ($name eq 'repos' or $name eq 'revision') {
+        # Branch location or revision
+        $branch->$name ($line->value);
+
+      } else { # $name eq 'dirs' or $name eq 'expdirs'
+        # Source directory or expandable source directory
+        if ($pck eq $pckroot and $line->value !~ m#^/#) {
+          # Sub-package name not set and source directory quoted as a relative
+          # path, determine package name from path name
+          $pck = join (
+            $FCM1::Config::DELIMITER,
+            ($pckroot, File::Spec->splitdir ($line->value)),
+          );
+        }
+
+        # A "/" is equivalent to the top (empty) package
+        my $value = ($line->value =~ m#^/+$#) ? '' : $line->value;
+        $branch->$name ($pck, $value);
+      }
+
+      $line->parsed (1);
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->expand_cfg ();
+#
+# DESCRIPTION
+#   This method expands the settings of the extract configuration.
+# ------------------------------------------------------------------------------
+
+sub expand_cfg {
+  my $self = shift;
+
+  my $rc = 1;
+  for my $use (@{ $self->inherit }) {
+    $rc = $use->expand_cfg if $rc;
+  }
+
+  return $rc unless $rc;
+
+  # Establish a set of source directories from the "base repository"
+  my %base_branches = ();
+
+  # Inherit "base" set of source directories from re-used extracts
+  for my $use (@{ $self->inherit }) {
+    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 = FCM1::Util::tidy_url(FCM1::Keyword::expand($branch->repos()));
+      $branch->repos ($repos) if $repos ne $branch->repos;
+    }
+
+    # Check that repository type and revision are set
+    if ($branch->repos and &is_url ($branch->repos)) {
+      $branch->type ('svn') unless $branch->type;
+      $branch->revision ('head') unless $branch->revision;
+
+    } else {
+      $branch->type ('user') unless $branch->type;
+      $branch->revision ('user') unless $branch->revision;
+    }
+
+    $rc = $branch->expand_revision if $rc; # Get revision number from keywords
+    $rc = $branch->expand_path     if $rc; # Expand relative path to full path
+    $rc = $branch->expand_all      if $rc; # Search sub-directories
+    last unless $rc;
+
+    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 $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->create_dir_stack ();
+#
+# DESCRIPTION
+#   This method creates a hash of source directories to be processed. If the
+#   flag INHERITED is set to true, the source directories are assumed processed
+#   and extracted.
+# ------------------------------------------------------------------------------
+
+sub create_dir_stack {
+  my $self = shift;
+  my %args = @_;
+
+  # Inherit from USE ext cfg
+  for my $use (@{ $self->inherit }) {
+    $use->create_dir_stack () or return 0;
+    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 (/$FCM1::Config::DELIMITER/, $key);
+      $self->srcdirs ($key)->{DEST} = File::Spec->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 $self->srcdirs ($dir)) { # if not, create it
+        $self->srcdirs ($dir, {
+          DEST  => File::Spec->catfile (
+            $self->dest->srcdir, split (/$FCM1::Config::DELIMITER/, $dir)
+          ),
+          STACK => [],
+          FILES => {},
+        });
+      }
+
+      my $stack = $self->srcdirs ($dir)->{STACK}; # copy reference
+
+      # Create a new layer in the input stack
+      my $layer = FCM1::SrcDirLayer->new (
+        NAME      => $dir,
+        PACKAGE   => $branch->package,
+        TAG       => $branch->tag,
+        LOCATION  => $branch->dirs ($dir),
+        REPOSROOT => $branch->repos,
+        REVISION  => $branch->revision,
+        TYPE      => $branch->type,
+        EXTRACTED => @{ $self->inherited }
+                     ? $self->srcdirs ($dir)->{DEST} : undef,
+      );
+
+      # Check whether layer is already in the stack
+      my $exist = grep {
+        $_->location eq $layer->location and $_->revision eq $layer->revision;
+      } @{ $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;
+
+      }
+    }
+  }
+
+  # Use the cache to sort the source directory layer hash
+  return $self->compare_setting (METHOD_LIST => ['sort_dir_stack']);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, \@new_lines) = $self->sort_dir_stack ($old_lines);
+#
+# DESCRIPTION
+#   This method sorts thesource directories hash to be processed.
+# ------------------------------------------------------------------------------
+
+sub sort_dir_stack {
+  my ($self, $old_lines) = @_;
+
+  my $rc = 0;
+
+  my %old = ();
+  if ($old_lines) {
+    for my $line (@$old_lines) {
+      $old{$line->label} = $line->value;
+    }
+  }
+
+  my %new;
+
+  # Compare each layer to base layer, discard unnecessary layers
+  DIR: for my $srcdir (keys %{ $self->srcdirs }) {
+    my @stack = ();
+
+    while (my $layer = shift @{ $self->srcdirs ($srcdir)->{STACK} }) {
+      if ($layer->user) {
+        # Local file system branch, check that the declared location exists
+        if (-d $layer->location) {
+          # Local file system branch always takes precedence
+          push @stack, $layer;
+
+        } else {
+          w_report 'ERROR: ', $layer->location, ': declared source directory ',
+                   'does not exists ';
+          $rc = undef;
+          last DIR;
+        }
+
+      } else {
+        my $key = join ($FCM1::Config::DELIMITER, (
+          $srcdir, $layer->location, $layer->revision
+        ));
+
+        unless ($layer->extracted and $layer->commit) {
+          # See if commit revision information is cached
+          if (keys %old and exists $old{$key}) {
+            $layer->commit ($old{$key});
+
+          } else {
+            $layer->get_commit;
+            $rc = 1;
+          }
+
+          # Check source directory for commit revision, if necessary
+          if (not $layer->commit) {
+            w_report 'Error: cannot determine the last changed revision of ',
+                     $layer->location;
+            $rc = undef;
+            last DIR;
+          }
+
+          # Set cache directory for layer
+          my $tag_ver = $layer->tag . '__' . $layer->commit;
+          $layer->cachedir (File::Spec->catfile (
+            $self->dest->cachedir,
+            split (/$FCM1::Config::DELIMITER/, $srcdir),
+            $tag_ver,
+          ));
+        }
+
+        # New line in cache config file
+        $new{$key} = $layer->commit;
+
+        # Push this layer in the stack:
+        # 1. it has a different revision 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
+  my @new_lines;
+  if (defined ($rc)) {
+    for my $key (sort keys %new) {
+      push @new_lines, FCM1::CfgLine->new (label => $key, value => $new{$key});
+    }
+  }
+
+  return ($rc, \@new_lines);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $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 $rc = 1;
+
+  # Ensure destinations exist and are directories
+  for my $srcdir (values %{ $self->srcdirs }) {
+    last if not $rc;
+    if (-f $srcdir->{DEST}) {
+      w_report $srcdir->{DEST},
+               ': destination exists and is not a directory, abort.';
+      $rc = 0;
+    }
+  }
+
+  # Retrieve previous/record current extract configuration for each file.
+  $rc = $self->compare_setting (
+    CACHEBASE => $self->setting ('CACHE_FILE_SRC'),
+    METHOD_LIST => ['compare_setting_srcfiles'],
+  ) if $rc;
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, \@new_lines) = $self->compare_setting_srcfiles ($old_lines);
+#
+# DESCRIPTION
+#   For each file to be extracted, this method creates an instance of an
+#   FCM1::ExtractFile object. It then compares its file's sources to determine
+#   if they have changed. If so, it will allow the FCM1::ExtractFile to
+#   "re-extract" the file to the destination. Otherwise, it will set
+#   FCM1::ExtractFile->dest_status to a null string to denote an "unchanged"
+#   dest_status.
+#
+# SEE ALSO
+#   FCM1::ConfigSystem->compare_setting.
+# ------------------------------------------------------------------------------
+
+sub compare_setting_srcfiles {
+  my ($self, $old_lines) = @_;
+  my $rc = 1;
+
+  # Retrieve previous extract configuration for each file
+  # ----------------------------------------------------------------------------
+  my %old = ();
+  if ($old_lines) {
+    for my $line (@$old_lines) {
+      $old{$line->label} = $line->value;
+    }
+  }
+
+  # Build up a sequence using a FCM1::ExtractFile object for each file
+  # ----------------------------------------------------------------------------
+  for my $srcdir (values %{ $self->srcdirs }) {
+    my %pkgnames0; # (to be) list of package names in the base layer
+    for my $i (0 .. @{ $srcdir->{STACK} } - 1) {
+      my $layer = $srcdir->{STACK}->[$i];
+      # Update the cache for each layer of the stack if necessary
+      $layer->update_cache unless $layer->extracted or -d $layer->localdir;
+
+      # Get list of files in the cache or local directory
+      my %pkgnames;
+      for my $file (($layer->get_files)) {
+        my $pkgname = join (
+          '/', split (/$FCM1::Config::DELIMITER/, $layer->name), $file
+        );
+        $pkgnames0{$pkgname} = 1 if $i == 0; # store package name in base layer
+        $pkgnames{$pkgname} = 1; # store package name in the current layer
+        if (not $self->files ($pkgname)) {
+          $self->files ($pkgname, FCM1::ExtractFile->new (
+            conflict => $self->conflict,
+            dest     => $self->dest->srcpath,
+            pkgname  => $pkgname,
+          ));
+
+          # Base is empty
+          $self->files ($pkgname)->src->[0] = FCM1::ExtractSrc->new (
+            id      => $layer->tag,
+            pkgname => $pkgname,
+          ) if $i > 0;
+        }
+        my $cache = File::Spec->catfile ($layer->localdir, $file);
+        push @{ $self->files ($pkgname)->src }, FCM1::ExtractSrc->new (
+          cache   => $cache,
+          id      => $layer->tag,
+          pkgname => $pkgname,
+          rev     => ($layer->user ? (stat ($cache))[9] : $layer->commit),
+          uri     => join ('/', $layer->location, $file),
+        );
+      }
+
+      # List of removed files in this layer (relative to base layer)
+      if ($i > 0) {
+        for my $pkgname (keys %pkgnames0) {
+          push @{ $self->files ($pkgname)->src }, FCM1::ExtractSrc->new (
+            id      => $layer->tag,
+            pkgname => $pkgname,
+          ) if not exists $pkgnames{$pkgname}
+        }
+      }
+    }
+  }
+
+  # Compare with old settings
+  # ----------------------------------------------------------------------------
+  my %new = ();
+  for my $key (sort keys %{ $self->files }) {
+    # Set up value for cache
+    my @sources = ();
+    for my $src (@{ $self->files ($key)->src }) {
+      push @sources, (defined ($src->uri) ? ($src->uri . '@' . $src->rev) : '');
+    }
+
+    my $value = join ($FCM1::Config::DELIMITER, @sources);
+
+    # Set FCM1::ExtractFile->dest_status to "unchanged" if value is unchanged
+    if (exists($old{$key}) && $old{$key} eq $value && !grep {!$_} @sources) {
+      $self->files($key)->dest_status('');
+    }
+
+    # Write current settings
+    $new{$key} = $value;
+  }
+
+  # Delete those that exist in previous extract but not in current
+  # ----------------------------------------------------------------------------
+  for my $key (sort keys %old) {
+    next if exists $new{$key};
+    $self->files ($key, FCM1::ExtractFile->new (
+      dest    => $self->dest->srcpath,
+      pkgname => $key,
+    ));
+  }
+
+  # Extract each file, if necessary
+  # ----------------------------------------------------------------------------
+  for my $key (sort keys %{ $self->files }) {
+    $rc = $self->files ($key)->run if defined ($rc);
+    last if not defined ($rc);
+  }
+
+  # Report status
+  # ----------------------------------------------------------------------------
+  if (defined ($rc) and $self->verbose) {
+    my %src_status_count = ();
+    my %dest_status_count = ();
+    for my $key (sort keys %{ $self->files }) {
+      # Report changes in destination in verbose 1 or above
+      my $dest_status = $self->files ($key)->dest_status;
+      my $src_status = $self->files ($key)->src_status;
+      next unless $self->verbose and $dest_status;
+
+      if ($dest_status and $dest_status ne '?') {
+        if (exists $dest_status_count{$dest_status}) {
+          $dest_status_count{$dest_status}++;
+
+        } else {
+          $dest_status_count{$dest_status} = 1;
+        }
+      }
+
+      if ($src_status and $src_status ne '?') {
+        if (exists $src_status_count{$src_status}) {
+          $src_status_count{$src_status}++;
+
+        } else {
+          $src_status_count{$src_status} = 1;
+        }
+      }
+
+      # Destination status in column 1, source status in column 2
+      if ($self->verbose > 1) {
+        my @srcs = @{$self->files ($key)->src_actual};
+        print ($dest_status ? $dest_status : ' ');
+        print ($src_status ? $src_status : ' ');
+        print ' ' x 5, $key;
+        print ' (', join (', ', map {$_->id} @srcs), ')' if @srcs;
+        print "\n";
+      }
+    }
+
+    # Report number of files in each dest_status category
+    if (%dest_status_count) {
+      print 'Column 1: ' if $self->verbose > 1;
+      print 'Destination status summary:', "\n";
+      for my $key (sort keys %FCM1::ExtractFile::DEST_STATUS_CODE) {
+        next unless $key;
+        next if not exists ($dest_status_count{$key});
+        print '  No of files ';
+        print '[', $key, '] ' if $self->verbose > 1;
+        print $FCM1::ExtractFile::DEST_STATUS_CODE{$key}, ': ',
+              $dest_status_count{$key}, "\n";
+      }
+    }
+
+    # Report number of files in each dest_status category
+    if (%src_status_count) {
+      print 'Column 2: ' if $self->verbose > 1;
+      print 'Source status summary:', "\n";
+      for my $key (sort keys %FCM1::ExtractFile::SRC_STATUS_CODE) {
+        next unless $key;
+        next if not exists ($src_status_count{$key});
+        print '  No of files ';
+        print '[', $key, '] ' if $self->verbose > 1;
+        print $FCM1::ExtractFile::SRC_STATUS_CODE{$key}, ': ',
+              $src_status_count{$key}, "\n";
+      }
+    }
+  }
+
+  # Record configuration of current extract for each file
+  # ----------------------------------------------------------------------------
+  my @new_lines;
+  if (defined ($rc)) {
+    for my $key (sort keys %new) {
+      push @new_lines, FCM1::CfgLine->new (label => $key, value => $new{$key});
+    }
+  }
+
+  return ($rc, \@new_lines);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @array = $self->sort_bdeclare ();
+#
+# DESCRIPTION
+#   This method returns sorted build declarations, filtering out repeated
+#   entries, where possible.
+# ------------------------------------------------------------------------------
+
+sub sort_bdeclare {
+  my $self = shift;
+
+  # Get list of build configuration labels that can be declared multiple times
+  my %cfg_keyword = map {
+    ($self->cfglabel ($_), 1)
+  } split (/$FCM1::Config::DELIMITER_LIST/, $self->setting ('CFG_KEYWORD'));
+
+  my @bdeclares = ();
+  for my $d (reverse @{ $self->bdeclare }) {
+    # Reconstruct array from bottom up
+    # * always add declarations that can be declared multiple times
+    # * otherwise add only if it is declared below
+    unshift @bdeclares, $d
+      if exists $cfg_keyword{uc (($d->slabel_fields)[0])} or
+         not grep {$_->slabel eq $d->slabel} @bdeclares;
+  }
+
+  return (sort {$a->slabel cmp $b->slabel} @bdeclares);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @cfglines = $obj->to_cfglines ();
+#
+# DESCRIPTION
+#   See description of FCM1::ConfigSystem->to_cfglines for further information.
+# ------------------------------------------------------------------------------
+
+sub to_cfglines {
+  my ($self) = @_;
+
+  return (
+    FCM1::ConfigSystem::to_cfglines($self),
+
+    $self->rdest->to_cfglines (),
+    FCM1::CfgLine->new (),
+
+    @{ $self->bdeclare } ? (
+      FCM1::CfgLine::comment_block ('Build declarations'),
+      map {
+        FCM1::CfgLine->new (label => $_->label, value => $_->value)
+      } ($self->sort_bdeclare),
+      FCM1::CfgLine->new (),
+    ) : (),
+
+    FCM1::CfgLine::comment_block ('Project and branches'),
+    (map {($_->to_cfglines ())} @{ $self->branches }),
+
+    ($self->conflict ne 'merge') ? (
+      FCM1::CfgLine->new (
+        label => $self->cfglabel ('CONFLICT'), value => $self->conflict,
+      ),
+      FCM1::CfgLine->new (),
+    ) : (),
+  );
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @cfglines = $obj->to_cfglines_bld ();
+#
+# DESCRIPTION
+#   Returns a list of configuration lines of the current extract suitable for
+#   feeding into the build system.
+# ------------------------------------------------------------------------------
+
+sub to_cfglines_bld {
+  my ($self) = @_;
+
+  my $dest = $self->rdest->rootdir ? 'rdest' : 'dest';
+  my $root = File::Spec->catfile ('$HERE', '..');
+
+  my @inherits;
+  my @no_inherits;
+  if (@{ $self->inherit }) {
+    # List of inherited builds
+    for (@{ $self->inherit }) {
+      push @inherits, FCM1::CfgLine->new (
+        label => $self->cfglabel ('USE'), value => $_->$dest->rootdir
+      );
+    }
+
+    # List of files that should not be inherited
+    for my $key (sort keys %{ $self->files }) {
+      next unless $self->files ($key)->dest_status eq 'd';
+      my $label = join ('::', (
+        $self->cfglabel ('INHERIT'),
+        $self->cfglabel ('FILE'),
+        split (m#/#, $self->files ($key)->pkgname),
+      ));
+      push @no_inherits, FCM1::CfgLine->new (label => $label, value => 'false');
+    }
+  }
+
+  return (
+    FCM1::CfgLine::comment_block ('File header'),
+    (map
+      {my ($lbl, $val) = @{$_}; FCM1::CfgLine->new(label => $lbl, value => $val)}
+      (
+        [$self->cfglabel('CFGFILE') . $FCM1::Config::DELIMITER . 'TYPE'   , 'bld'],
+        [$self->cfglabel('CFGFILE') . $FCM1::Config::DELIMITER . 'VERSION', '1.0'],
+        [],
+      )
+    ),
+
+    @{ $self->inherit } ? (
+      @inherits,
+      @no_inherits,
+      FCM1::CfgLine->new (),
+    ) : (),
+
+    FCM1::CfgLine::comment_block ('Destination'),
+    FCM1::CfgLine->new (label => $self->cfglabel ('DEST'), value => $root),
+    FCM1::CfgLine->new (),
+
+    @{ $self->bdeclare } ? (
+      FCM1::CfgLine::comment_block ('Build declarations'),
+      map {
+        FCM1::CfgLine->new (label => $_->slabel, value => $_->value)
+      } ($self->sort_bdeclare),
+      FCM1::CfgLine->new (),
+    ) : (),
+  );
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->write_cfg ();
+#
+# DESCRIPTION
+#   This method writes the configuration file at the end of the run. It calls
+#   $self->write_cfg_system ($cfg) to write any system specific settings.
+# ------------------------------------------------------------------------------
+
+sub write_cfg {
+  my $self = shift;
+
+  my $cfg = FCM1::CfgFile->new (TYPE => $self->type);
+  $cfg->lines ([$self->to_cfglines()]);
+  $cfg->print_cfg ($self->dest->extcfg);
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->write_cfg_bld ();
+#
+# DESCRIPTION
+#   This internal method writes the build configuration file.
+# ------------------------------------------------------------------------------
+
+sub write_cfg_bld {
+  my $self = shift;
+
+  my $cfg = FCM1::CfgFile->new (TYPE => 'bld');
+  $cfg->lines ([$self->to_cfglines_bld()]);
+  $cfg->print_cfg ($self->dest->bldcfg);
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractConfigComparator.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractConfigComparator.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractConfigComparator.pm	(revision 5095)
@@ -0,0 +1,371 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+################################################################################
+# A generic reporter of the comparator's result
+{
+    package Reporter;
+
+    ############################################################################
+    # Class method: Constructor
+    sub new {
+        my ($class) = @_;
+        return bless(\do{my $annon_scalar}, $class);
+    }
+
+    ############################################################################
+    # Class method: Factory for Reporter object
+    sub get_reporter {
+        my ($self, $comparator) = @_;
+        my $class = defined($comparator->get_wiki()) ? 'WikiReporter'
+                  :                                    'TextReporter'
+                  ;
+        return $class->new();
+    }
+
+    ############################################################################
+    # Reports the results
+    sub report {
+        my ($self, $comparator) = @_;
+        if (keys(%{$comparator->get_log_of()})) {
+            print("Revisions at which extract declarations are modified:\n\n");
+        }
+        $self->report_impl($comparator);
+    }
+
+    ############################################################################
+    # Does the actual reporting
+    sub report_impl {
+        my ($self, $comparator) = @_;
+    }
+}
+
+################################################################################
+# Reports the comparator's result in Trac wiki format
+{
+    package WikiReporter;
+    our @ISA = qw{Reporter};
+
+    use FCM1::CmUrl;
+    use FCM1::Keyword;
+    use FCM1::Util qw{tidy_url};
+
+    ############################################################################
+    # Reports the comparator's result
+    sub report_impl {
+        my ($self, $comparator) = @_;
+        # Output in wiki format
+        my $wiki_url = FCM1::CmUrl->new(
+            URL => tidy_url(FCM1::Keyword::expand($comparator->get_wiki()))
+        );
+        my $base_trac
+            = $comparator->get_wiki()
+            ? FCM1::Keyword::get_browser_url($wiki_url->project_url())
+            : $wiki_url;
+        if (!$base_trac) {
+            $base_trac = $wiki_url;
+        }
+
+        for my $key (sort keys(%{$comparator->get_log_of()})) {
+            my $branch_trac = FCM1::Keyword::get_browser_url($key);
+            $branch_trac =~ s{\A $base_trac (?:/*|\z)}{source:}xms;
+            print("[$branch_trac]:\n");
+            my %branch_of = %{$comparator->get_log_of()->{$key}};
+            for my $rev (sort {$b <=> $a} keys(%branch_of)) {
+                print(
+                    $branch_of{$rev}->display_svnlog($rev, $base_trac), "\n",
+                );
+            }
+            print("\n");
+        }
+    }
+}
+
+################################################################################
+# Reports the comparator's result in simple text format
+{
+    package TextReporter;
+    our @ISA = qw{Reporter};
+
+    use FCM1::Config;
+
+    my $SEPARATOR = q{-} x 80 . "\n";
+
+    ############################################################################
+    # Reports the comparator's result
+    sub report_impl {
+        my ($self, $comparator) = @_;
+        for my $key (sort keys(%{$comparator->get_log_of()})) {
+            # Output in plain text format
+            print $key, ':', "\n";
+            my %branch_of = %{$comparator->get_log_of()->{$key}};
+            if (FCM1::Config->instance()->verbose() > 1) {
+                for my $rev (sort {$b <=> $a} keys(%branch_of)) {
+                    print(
+                        $SEPARATOR, $branch_of{$rev}->display_svnlog($rev), "\n"
+                    );
+                }
+            }
+            else {
+                print(join(q{ }, sort {$b <=> $a} keys(%branch_of)), "\n");
+            }
+            print $SEPARATOR, "\n";
+        }
+    }
+}
+
+package FCM1::ExtractConfigComparator;
+
+use FCM1::CmUrl;
+use FCM1::Extract;
+
+################################################################################
+# Class method: Constructor
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Returns an array containing the 2 configuration files to compare
+sub get_files {
+    my ($self) = @_;
+    return (wantarray() ? @{$self->{files}} : $self->{files});
+}
+
+################################################################################
+# Returns the wiki link on wiki mode
+sub get_wiki {
+    my ($self) = @_;
+    return $self->{wiki};
+}
+
+################################################################################
+# Returns the result log
+sub get_log_of {
+    my ($self) = @_;
+    return (wantarray() ? %{$self->{log_of}} : $self->{log_of});
+}
+
+################################################################################
+# Invokes the comparator
+sub invoke {
+    my ($self) = @_;
+
+    # Reads the extract configurations
+    my (@cfg, $rc);
+    for my $i (0 .. 1) {
+        $cfg[$i] = FCM1::Extract->new();
+        $cfg[$i]->cfg()->src($self->get_files()->[$i]);
+        $cfg[$i]->parse_cfg();
+        $rc = $cfg[$i]->expand_cfg();
+        if (!$rc) {
+            e_report();
+        }
+    }
+
+    # Get list of URLs
+    # --------------------------------------------------------------------------
+    my @urls = ();
+    for my $i (0 .. 1) {
+        # List of branches in each extract configuration file
+        my @branches = @{$cfg[$i]->branches()};
+        BRANCH:
+        for my $branch (@branches) {
+            # Ignore declarations of local directories
+            if ($branch->type() eq 'user') {
+                next BRANCH;
+            }
+
+            # List of SRC declarations in each branch
+            my %dirs = %{$branch->dirs()};
+
+            for my $dir (values(%dirs)) {
+                # Set up a new instance of FCM1::CmUrl object for each SRC
+                my $cm_url = FCM1::CmUrl->new (
+                    URL => $dir . (
+                        $branch->revision() ? '@' . $branch->revision() : q{}
+                    ),
+                );
+
+                $urls[$i]{$cm_url->branch_url()}{$dir} = $cm_url;
+            }
+        }
+    }
+
+    # Compare
+    # --------------------------------------------------------------------------
+    $self->{log_of} = {};
+    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
+                DIR:
+                for my $dir (sort keys(%{$urls[$i]{$branch}})) {
+                    if (exists($urls[$j]{$branch}{$dir})) {
+                        if ($i == 1) {
+                            next DIR;
+                        }
+
+                        my $this_url = $urls[$i]{$branch}{$dir};
+                        my $that_url = $urls[$j]{$branch}{$dir};
+
+                        # Compare their last changed revisions
+                        my $this_rev
+                            = $this_url->svninfo(FLAG => 'commit:revision');
+                        my $that_rev
+                            = $that_url->svninfo(FLAG => 'commit:revision');
+
+                        # Make sure last changed revisions differ
+                        if ($this_rev eq $that_rev) {
+                            next DIR;
+                        }
+
+                        # 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]);
+
+                        REV:
+                        for my $rev (keys %revs) {
+                            # Check if revision is already in the list
+                            if (
+                                   exists($self->{log_of}{$branch}{$rev})
+                                || $rev == $min_rev
+                            ) {
+                                next REV;
+                            }
+
+                            # Get list of changed paths. Accept this revision
+                            # only if it contains changes in the current branch
+                            my %paths  = %{$revs{$rev}{paths}};
+
+                            PATH:
+                            for my $path (keys(%paths)) {
+                                my $change_url
+                                    = FCM1::CmUrl->new(URL => $u->root() . $path);
+
+                                if ($change_url->branch() eq $u->branch()) {
+                                    $self->{log_of}{$branch}{$rev} = $u;
+                                    last PATH;
+                                }
+                            }
+                        }
+                    }
+                    else {
+                        $self->_report_added(
+                            $urls[$i]{$branch}{$dir}->url_peg(), $i, $j);
+                    }
+                }
+            }
+            else {
+                $self->_report_added($branch, $i, $j);
+            }
+        }
+    }
+
+    my $reporter = Reporter->get_reporter($self);
+    $reporter->report($self);
+    return $rc;
+}
+
+################################################################################
+# Reports added/deleted declaration
+sub _report_added {
+    my ($self, $branch, $i, $j) = @_;
+    printf(
+        "%s:\n  in    : %s\n  not in: %s\n\n",
+        $branch, $self->get_files()->[$i], $self->get_files()->[$j],
+    );
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::ExtractConfigComparator
+
+=head1 SYNOPSIS
+
+    use FCM1::ExtractConfigComparator;
+    my $comparator = FCM1::ExtractConfigComparator->new({files => \@files});
+    $comparator->invoke();
+
+=head1 DESCRIPTION
+
+An object of this class represents a comparator of FCM extract configuration.
+It is used to compare the VC branch declarations in 2 FCM extract configuration
+files.
+
+=head1 METHODS
+
+=over 4
+
+=item C<new({files =E<gt> \@files, wiki =E<gt> $wiki})>
+
+Constructor.
+
+=item get_files()
+
+Returns an array containing the 2 configuration files to compare.
+
+=item get_wiki()
+
+Returns the wiki link on wiki mode.
+
+=item invoke()
+
+Invokes the comparator.
+
+=back
+
+=head1 TO DO
+
+More documentation.
+
+Improve the parser for extract configuration.
+
+Separate the comparator with the reporters.
+
+Add reporter to display HTML.
+
+More unit tests.
+
+=head1 SEE ALSO
+
+L<FCM1::Extract|FCM1::Extract>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractFile.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractFile.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractFile.pm	(revision 5095)
@@ -0,0 +1,423 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::ExtractFile
+#
+# DESCRIPTION
+#   Select/combine a file in different branches and extract it to destination.
+#
+# ------------------------------------------------------------------------------
+
+use warnings;
+use strict;
+
+package FCM1::ExtractFile;
+use base qw{FCM1::Base};
+
+use FCM1::Util      qw{run_command w_report};
+use File::Basename qw{dirname};
+use File::Compare  qw{compare};
+use File::Copy     qw{copy};
+use File::Path     qw{mkpath};
+use File::Spec;
+use File::Temp     qw(tempfile);
+
+# List of property methods for this class
+my @scalar_properties = (
+  'conflict',    # conflict mode
+  'dest',        # search path to destination file
+  'dest_status', # destination status, see below
+  'pkgname',     # package name of this file
+  'src',         # list of FCM1::ExtractSrc, specified for this file
+  'src_actual',  # list of FCM1::ExtractSrc, actually used by this file
+  'src_status',  # source status, see below
+);
+
+# Status code definition for $self->dest_status
+our %DEST_STATUS_CODE = (
+  ''  => 'unchanged',
+  'M' => 'modified',
+  'A' => 'added',
+  'a' => 'added, overridding inherited',
+  'D' => 'deleted',
+  'd' => 'deleted, overridding inherited',
+  '?' => 'irrelevant',
+);
+
+# Status code definition for $self->src_status
+our %SRC_STATUS_CODE = (
+  'A' => 'added by a branch',
+  'B' => 'from the base',
+  'D' => 'deleted by a branch',
+  'M' => 'modified by a branch',
+  'G' => 'merged from 2+ branches',
+  'O' => 'overridden by a branch',
+  '?' => 'irrelevant',
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::ExtractFile->new ();
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::ExtractFile class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  for (@scalar_properties) {
+    $self->{$_} = exists $args{$_} ? $args{$_} : undef;
+  }
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'conflict') {
+        $self->{$name} = 'merge'; # default to "merge" mode
+
+      } elsif ($name eq 'dest' or $name eq 'src' or $name eq 'src_actual') {
+        $self->{$name} = [];      # default to an empty list
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->run();
+#
+# DESCRIPTION
+#   This method runs only if $self->dest_status is not defined. It updates the
+#   destination according to the source in the list and the conflict mode
+#   setting. It updates the file in $self->dest as appropriate and sets
+#   $self->dest_status. (See above.) This method returns true on success.
+# ------------------------------------------------------------------------------
+
+sub run {
+  my ($self) = @_;
+  my $rc = 1;
+
+  if (not defined ($self->dest_status)) {
+    # Assume file unchanged
+    $self->dest_status ('');
+
+    if (@{ $self->src }) {
+      my $used;
+      # Determine or set up a file for comparing with the destination
+      ($rc, $used) = $self->run_get_used();
+
+      # Attempt to compare the destination with $used. Update on change.
+      if ($rc) {
+        $rc = defined ($used) ? $self->run_update($used) : $self->run_delete();
+      }
+
+    } else {
+      # No source, delete file in destination
+      $self->src_status ('?');
+      $rc = $self->run_delete();
+    }
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->run_delete();
+#
+# DESCRIPTION
+#   This method is part of run(). It detects this file in the destination path.
+#   If this file is in the current destination, it attempts to delete it and
+#   sets the dest_status to "D". If this file is in an inherited destination,
+#   it sets the dest_status to "d".
+# ------------------------------------------------------------------------------
+
+sub run_delete {
+  my ($self) = @_;
+
+  my $rc = 1;
+
+  $self->dest_status ('?');
+  for my $i (0 .. @{ $self->dest } - 1) {
+    my $dest = File::Spec->catfile ($self->dest->[$i], $self->pkgname);
+    next unless -f $dest;
+    if ($i == 0) {
+      $rc = unlink $dest;
+      $self->dest_status ('D');
+
+    } else {
+      $self->dest_status ('d');
+      last;
+    }
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, $used) = $obj->run_get_used();
+#
+# DESCRIPTION
+#   This method is part of run(). It attempts to work out or set up the $used
+#   file. ($used is undef if it is not defined in a branch for this file.)
+# ------------------------------------------------------------------------------
+
+sub run_get_used {
+  my ($self) = @_;
+  my $rc = 1;
+  my $used;
+
+  my @sources = ($self->src->[0]);
+  my $src_status = 'B';
+  if (defined ($self->src->[0]->cache)) {
+    # File exists in base branch
+    for my $i (1 .. @{ $self->src } - 1) {
+      if (defined ($self->src->[$i]->cache)) {
+        # Detect changes in this file between base branch and branch $i
+        push @sources, $self->src->[$i]
+          if &compare ($self->src->[0]->cache, $self->src->[$i]->cache);
+
+      } else {
+        # File deleted in branch $i
+        @sources = ($self->src->[$i]);
+        last unless $self->conflict eq 'override';
+      }
+    }
+
+    if ($rc) {
+      if (@sources > 2) {
+        if ($self->conflict eq 'fail') {
+          # On conflict, fail in fail mode
+          w_report 'ERROR: ', $self->pkgname,
+                   ': modified in 2+ branches in fail conflict mode.';
+          $rc = undef;
+
+        } elsif ($self->conflict eq 'override') {
+          $used = $sources[-1]->cache;
+          $src_status = 'O';
+
+        } else {
+          # On conflict, attempt to merge in merge mode
+          ($rc, $used) = $self->run_get_used_by_merge (@sources);
+          $src_status = 'G' if $rc;
+        }
+
+      } else {
+        # 0 or 1 change, use last source
+        if (defined $sources[-1]->cache) {
+          $used = $sources[-1]->cache;
+          $src_status = 'M' if @sources > 1;
+
+        } else {
+          $src_status = 'D';
+        }
+      }
+    }
+
+  } else {
+    # File does not exist in base branch
+    @sources = ($self->src->[-1]);
+    $used = $self->src->[1]->cache;
+    $src_status = (defined ($used) ? 'A' : 'D');
+    if ($self->conflict ne 'override' and defined ($used)) {
+      for my $i (1 - @{ $self->src } .. -2) {
+        # Allow this only if files are the same in all branches
+        my $file = $self->src->[$i]->cache;
+        if ((not defined ($file)) or &compare ($used, $file)) {
+          w_report 'ERROR: ', $self->pkgname, ': cannot merge:',
+                   ' not found in base branch,',
+                   ' but differs in subsequent branches.';
+          $rc = undef;
+          last;
+
+        } else {
+          unshift @sources, $self->src->[$i];
+        }
+      }
+    }
+  }
+
+  $self->src_status ($src_status);
+  $self->src_actual (\@sources);
+
+  return ($rc, $used);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($rc, $used) = $obj->run_get_used_by_merge(@soruces);
+#
+# DESCRIPTION
+#   This method is part of run_get_used(). It attempts to merge the files in
+#   @sources and return a temporary file $used. @sources should be an array of
+#   FCM1::ExtractSrc objects. On success, $rc will be set to true.
+# ------------------------------------------------------------------------------
+
+sub run_get_used_by_merge {
+  my ($self, @sources) = @_;
+  my $rc = 1;
+
+  # Get temporary file
+  my ($fh, $used) = &tempfile ('fcm.ext.merge.XXXXXX', UNLINK => 1);
+  close $fh or die $used, ': cannot close';
+
+  for my $i (2 .. @sources - 1) {
+    # Invoke the diff3 command to merge
+    my $mine = ($i == 2 ? $sources[1]->cache : $used);
+    my $older = $sources[0]->cache;
+    my $yours = $sources[$i]->cache;
+    my @command = (
+      $self->setting (qw/TOOL DIFF3/),
+      split (/\s+/, $self->setting (qw/TOOL DIFF3FLAGS/)),
+      $mine, $older, $yours,
+    );
+    my $code;
+    my @out = &run_command (
+      \@command,
+      METHOD => 'qx',
+      ERROR  => 'ignore',
+      PRINT  => $self->verbose > 1,
+      RC     => \$code,
+      TIME   => $self->verbose > 2,
+    );
+
+    if ($code) {
+      # Failure, report and return
+      my $m = ($code == 1)
+              ? 'cannot resolve conflicts:'
+              : $self->setting (qw/TOOL DIFF3/) . 'command failed';
+      w_report 'ERROR: ', $self->pkgname, ': merge - ', $m;
+      if ($code == 1 and $self->verbose) {
+        for (0 .. $i) {
+          my $src = $sources[$_]->uri eq $sources[$_]->cache
+                    ? $sources[$_]->cache
+                    : ($sources[$_]->uri . '@' . $sources[$_]->rev);
+          w_report '  source[', $_, ']=', $src;
+        }
+
+        for (0 .. $i) {
+          w_report '  cache', $_, '=', $sources[$_]->cache;
+        }
+
+        w_report @out if $self->verbose > 2;
+      }
+      $rc = undef;
+      last;
+
+    } else {
+      # Success, write result to temporary file
+      open FILE, '>', $used or die $used, ': cannot open (', $!, ')';
+      print FILE @out;
+      close FILE or die $used, ': cannot close (', $!, ')';
+
+      # File permission, use most permissive combination of $mine and $yours
+      my $perm = ((stat($mine))[2] & 07777) | ((stat($yours))[2] & 07777);
+      chmod ($perm, $used);
+    }
+  }
+
+  return ($rc, $used);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->run_update($used_file);
+#
+# DESCRIPTION
+#   This method is part of run(). It compares the $used_file with the one in
+#   the destination. If the file does not exist in the destination or if its
+#   content is out of date, the destination is updated with the content in the
+#   $used_file. Returns true on success.
+# ------------------------------------------------------------------------------
+
+sub run_update {
+  my ($self, $used_file) = @_;
+  my ($is_diff, $is_diff_in_perms, $is_in_prev, $rc) = (1, 1, undef, 1);
+
+  # Compare with the previous version if it exists
+  DEST:
+  for my $i (0 .. @{$self->dest()} - 1) {
+    my $prev_file = File::Spec->catfile($self->dest()->[$i], $self->pkgname());
+    if (-f $prev_file) {
+      $is_in_prev = $i;
+      $is_diff = compare($used_file, $prev_file);
+      $is_diff_in_perms = (stat($used_file))[2] != (stat($prev_file))[2];
+      last DEST;
+    }
+  }
+  if (!$is_diff && !$is_diff_in_perms) {
+    return $rc;
+  }
+
+  # Update destination
+  my $dest_file = File::Spec->catfile($self->dest()->[0], $self->pkgname());
+  if ($is_diff) {
+    my $dir = dirname($dest_file);
+    if (!-d $dir) {
+      mkpath($dir);
+    }
+    $rc = copy($used_file, $dest_file);
+  }
+  $rc &&= chmod((stat($used_file))[2] & oct(7777), $dest_file);
+  if ($rc) {
+    $self->dest_status(
+        $is_in_prev          ? 'a'
+      : defined($is_in_prev) ? 'M'
+      :                        'A'
+    );
+  }
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractSrc.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractSrc.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ExtractSrc.pm	(revision 5095)
@@ -0,0 +1,100 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::ExtractSrc
+#
+# DESCRIPTION
+#   This class is used by the extract system to define the functionalities of a
+#   source file (or directory) in a branch.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::ExtractSrc;
+@ISA = qw(FCM1::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# FCM component modules
+use FCM1::Base;
+
+# List of scalar property methods for this class
+my @scalar_properties = (
+  'cache',   # location of the cache of this file in the current extract
+  'id',      # short ID of the branch where this file is from
+  'ignore',  # if set to true, ignore this file from this source
+  'pkgname', # package name of this file
+  'rev',     # last changed revision/timestamp of this file
+  'uri',     # URL/source path of this file
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::ExtractSrc->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::ExtractSrc class. See
+#   @scalar_properties above for allowed list of properties in the constructor.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  for (@scalar_properties) {
+    $self->{$_} = exists $args{$_} ? $args{$_} : undef;
+  }
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive.pm	(revision 5095)
@@ -0,0 +1,144 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Interactive;
+use base qw{Exporter};
+
+our @EXPORT_OK = qw{get_input};
+
+use FCM1::Util::ClassLoader;
+
+my $DEFAULT_IMPL_CLASS = 'FCM1::Interactive::InputGetter::CLI';
+my %DEFAULT_IMPL_CLASS_OPTIONS = ();
+
+my $IMPL_CLASS = $DEFAULT_IMPL_CLASS;
+my %IMPL_CLASS_OPTIONS = %DEFAULT_IMPL_CLASS_OPTIONS;
+
+################################################################################
+# Returns the name of the current class/settings for getting input
+sub get_impl {
+    return (wantarray() ? ($IMPL_CLASS, \%IMPL_CLASS_OPTIONS) : $IMPL_CLASS);
+}
+
+################################################################################
+# Returns the name of the current class/settings for getting input
+sub get_default_impl {
+    return (
+        wantarray()
+        ? ($DEFAULT_IMPL_CLASS, \%DEFAULT_IMPL_CLASS_OPTIONS)
+        : $DEFAULT_IMPL_CLASS
+    );
+}
+
+################################################################################
+# Sets the name of the class/settings for getting input
+sub set_impl {
+    my ($impl_class, $impl_class_options_ref) = @_;
+    if ($impl_class) {
+        $IMPL_CLASS = $impl_class;
+        if ($impl_class_options_ref) {
+            %IMPL_CLASS_OPTIONS = (%{$impl_class_options_ref});
+        }
+        else {
+            %IMPL_CLASS_OPTIONS = ();
+        }
+    }
+}
+
+################################################################################
+# Gets an input from the user and returns it
+sub get_input {
+    my (%options) = @_;
+    my ($class_name, $class_options_ref) = get_impl();
+    FCM1::Util::ClassLoader::load($class_name);
+    %options = map {lc($_), $options{$_}} keys(%options);
+    return $class_name->new({%{$class_options_ref}, %options})->invoke();
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::Interactive
+
+=head1 SYNOPSIS
+
+    use FCM1::Interactive;
+    FCM1::Interactive::set_impl('My::InputGetter', {option1 => 'value1', ...});
+    $answer = FCM1::Interactive::get_input(
+        title   => 'My title',
+        message => 'Would you like to ...?',
+        type    => 'yn',
+        default => 'n',
+    );
+
+=head1 DESCRIPTION
+
+Common interface for getting an interactive user reply. The default is to use a
+L<FCM1::Interactive::InputGetter::CLI|FCM1::Interactive::InputGetter::CLI> object
+with no extra options.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item get_impl()
+
+Returns the class that implements the function for get_input(%options). In
+scalar context, returns the class name only. In list context, returns the class
+name and the extra hash options that would be passed to its constructor.
+
+=item get_default_impl()
+
+Returns the defaut values for get_impl().
+
+=item set_impl($impl_class,$impl_class_options_ref)
+
+Sets the class that implements the function for get_input(%options). The name
+of the class is given in $impl_class. Any extra options that should be given to
+the constructor should be set in the hash reference $impl_class_options_ref.
+
+=item get_input(%options)
+
+Calls the appropriate function to get an input string from the user, and
+returns it.
+
+Input options are: I<title>, for a short title of the prompt, I<message>, for
+the message prompt, I<type> for the prompt type, and I<default> for the default
+value of the return value.
+
+Prompt type can be YN (yes or no), YNA (yes, no or all) or input (for an input
+string).
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter>,
+L<FCM1::Interactive::InputGetter::CLI|FCM1::Interactive::InputGetter::CLI>,
+L<FCM1::Interactive::InputGetter::GUI|FCM1::Interactive::InputGetter::GUI>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter.pm	(revision 5095)
@@ -0,0 +1,135 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Interactive::InputGetter;
+
+use Carp qw{croak};
+
+################################################################################
+# Constructor
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Methods: get_*
+for my $key (
+    ############################################################################
+    # Returns the title of the prompt
+    'title',
+    ############################################################################
+    # Returns the message of the prompt
+    'message',
+    ############################################################################
+    # Returns the of the prompt
+    'type',
+    ############################################################################
+    # Returns the default return value
+    'default',
+) {
+    no strict qw{refs};
+    my $getter = "get_$key";
+    *$getter = sub {
+        my ($self) = @_;
+        return $self->{$key};
+    }
+}
+
+################################################################################
+# Invokes the getter
+sub invoke {
+    my ($self) = @_;
+    croak("FCM1::Interactive::InputGetter->invoke() not implemented.");
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::Interactive::TxtInputGetter
+
+=head1 SYNOPSIS
+
+    use FCM1::Interactive::TxtInputGetter;
+    $answer = FCM1::Interactive::get_input(
+        title   => 'My title',
+        message => 'Would you like to ...?',
+        type    => 'yn',
+        default => 'n',
+    );
+
+=head1 DESCRIPTION
+
+An object of this abstract class is used by
+L<FCM1::Interactive|FCM1::Interactive> to get a user reply.
+
+=head1 METHODS
+
+=over 4
+
+=item new($args_ref)
+
+Constructor, normally invoked via L<FCM1::Interactive|FCM1::Interactive>.
+
+Input options are: I<title>, for a short title of the prompt, I<message>, for
+the message prompt, I<type> for the prompt type, and I<default> for the default
+value of the return value.
+
+Prompt type can be YN (yes or no), YNA (yes, no or all) or input (for an input
+string).
+
+=item get_title()
+
+Returns the title of the prompt.
+
+=item get_message()
+
+Returns the message of the prompt.
+
+=item get_type()
+
+Returns the type of the prompt, can be YN (yes or no), YNA (yes, no or all) or
+input (for an input string).
+
+=item get_default()
+
+Returns the default return value of invoke().
+
+=item invoke()
+
+Gets an input string from the user, and returns it. Sub-classes must override
+this method.
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM1::Interactive|FCM1::Interactive>,
+L<FCM1::Interactive::TxtInputGetter|FCM1::Interactive::TxtInputGetter>,
+L<FCM1::Interactive::GUIInputGetter|FCM1::Interactive::GUIInputGetter>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter/CLI.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter/CLI.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter/CLI.pm	(revision 5095)
@@ -0,0 +1,100 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Interactive::InputGetter::CLI;
+use base qw{FCM1::Interactive::InputGetter};
+
+my $DEF_MSG = q{ (or just press <return> for "%s")};
+my %EXTRA_MSG_FOR = (
+    yn  => qq{\nEnter "y" or "n"},
+    yna => qq{\nEnter "y", "n" or "a"},
+);
+my %CHECKER_FOR = (
+    yn  => sub {$_[0] eq 'y' || $_[0] eq 'n'},
+    yna => sub {$_[0] eq 'y' || $_[0] eq 'n' || $_[0] eq 'a'},
+);
+
+sub invoke {
+    my ($self) = @_;
+    my $type = $self->get_type() ? lc($self->get_type()) : q{};
+    my $message
+        = $self->get_message()
+        . (exists($EXTRA_MSG_FOR{$type}) ? $EXTRA_MSG_FOR{$type} : q{})
+        . ($self->get_default() ? sprintf($DEF_MSG, $self->get_default()) : q{})
+        . q{: }
+        ;
+    while (1) {
+        print($message);
+        my $answer = readline(STDIN);
+        chomp($answer);
+        if (!$answer && $self->get_default()) {
+            $answer = $self->get_default();
+        }
+        if (!exists($CHECKER_FOR{$type}) || $CHECKER_FOR{$type}->($answer)) {
+            return $answer;
+        }
+    }
+    return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::Interactive::InputGetter::CLI
+
+=head1 SYNOPSIS
+
+    use FCM1::Interactive;
+    $answer = FCM1::Interactive::get_input(
+        title   => 'My title',
+        message => 'Would you like to ...?',
+        type    => 'yn',
+        default => 'n',
+    );
+
+=head1 DESCRIPTION
+
+This is a solid implementation of
+L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter>. It gets a user
+reply from STDIN using a prompt on STDOUT.
+
+=head1 METHODS
+
+See L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter> for a list of
+methods.
+
+=head1 TO DO
+
+Use IO::Prompt.
+
+=head1 SEE ALSO
+
+L<FCM1::Interactive|FCM1::Interactive>,
+L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter>,
+L<FCM1::Interactive::InputGetter::GUI|FCM1::Interactive::InputGetter::GUI>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter/GUI.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter/GUI.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Interactive/InputGetter/GUI.pm	(revision 5095)
@@ -0,0 +1,261 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Interactive::InputGetter::GUI;
+use base qw{FCM1::Interactive::InputGetter};
+
+use Tk;
+
+################################################################################
+# Returns the geometry string for the pop up message box
+sub get_geometry {
+    my ($self) = @_;
+    return $self->{geometry};
+}
+
+################################################################################
+# Invokes the getter
+sub invoke {
+    my ($self) = @_;
+    my $answer;
+    local $| = 1;
+
+    # Create a main window
+    my $mw = MainWindow->new();
+    $mw->title($self->get_title());
+
+    # Define the default which applies if the dialog box is just closed or
+    # the user selects 'cancel'
+    $answer = $self->get_default() ? $self->get_default() : q{};
+
+    if (defined($self->get_type()) && $self->get_type() =~ qr{\A yn}ixms) {
+        # Create a yes-no(-all) dialog box
+
+        # If TYPE is YNA then add a third button: 'all'
+        my $buttons = $self->get_type() =~ qr{a \z}ixms ? 3 : 2;
+
+        # Message of the dialog box
+        $mw->Label('-text' => $self->get_message())->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 {$answer = '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 {$answer = '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 {$answer = 'a'; $mw->destroy()},
+            )
+            ->grid('-row' => 1, '-column' => 2, '-padx' => 5, '-pady' => 5);
+        }
+
+        # Keyboard binding
+        if ($buttons == 3) {
+            $mw->bind('<Key>' => sub {
+                my $button
+                    = $Tk::event->K() eq 'Y' || $Tk::event->K() eq 'y' ? $y_b
+                    : $Tk::event->K() eq 'N' || $Tk::event->K() eq 'n' ? $n_b
+                    : $Tk::event->K() eq 'A' || $Tk::event->K() eq 'a' ? $a_b
+                    :                                                    undef
+                    ;
+                if (defined($button)) {
+                    $button->invoke();
+                }
+            });
+        }
+        else {
+            $mw->bind('<Key>' => sub {
+                my $button
+                    = $Tk::event->K() eq 'Y' || $Tk::event->K() eq 'y' ? $y_b
+                    : $Tk::event->K() eq 'N' || $Tk::event->K() eq 'n' ? $n_b
+                    :                                                    undef
+                    ;
+                if (defined($button)) {
+                    $button->invoke();
+                }
+            });
+        }
+
+        # Handle the situation when the user attempts to quit the window
+        $mw->protocol('WM_DELETE_WINDOW', sub {
+            if (self->get_default()) {
+                $answer = $self->get_default();
+            }
+            $mw->destroy();
+        });
+    }
+    else {
+        # Create a dialog box to obtain an input string
+        # Message of the dialog box
+        $mw->Label('-text' => $self->get_message())->grid(
+            '-row'    => 0,
+            '-column' => 0,
+            '-padx'   => 5,
+            '-pady'   => 5,
+        );
+
+        # Entry box for the user to type in the input string
+        my $entry   = $answer;
+        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 {$answer = $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 {$answer = 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($self->get_geometry());
+
+    # 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 $answer;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::Interactive::InputGetter::GUI
+
+=head1 SYNOPSIS
+
+    use FCM1::Interactive;
+    $answer = FCM1::Interactive::get_input(
+        title   => 'My title',
+        message => 'Would you like to ...?',
+        type    => 'yn',
+        default => 'n',
+    );
+
+=head1 DESCRIPTION
+
+This is a solid implementation of
+L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter>. It gets a user
+reply from a TK pop up message box.
+
+=head1 METHODS
+
+See L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter> for a list of
+inherited methods.
+
+=over 4
+
+=item new($args_ref)
+
+As in L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter>, but also
+accept a I<geometry> element for setting the geometry string of the pop up
+message box.
+
+=item get_geometry()
+
+Returns the geometry string for the pop up message box.
+
+=back
+
+=head1 TO DO
+
+Tidy up the logic of invoke(). Separate the logic for YN/A box and string input
+box, probably using a strategy pattern. Factor out the logic for the display
+and the return value.
+
+=head1 SEE ALSO
+
+L<FCM1::Interactive|FCM1::Interactive>,
+L<FCM1::Interactive::InputGetter|FCM1::Interactive::InputGetter>,
+L<FCM1::Interactive::InputGetter::CLI|FCM1::Interactive::InputGetter::CLI>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Keyword.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Keyword.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Keyword.pm	(revision 5095)
@@ -0,0 +1,175 @@
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Keyword;
+
+use FCM::Context::Locator;
+
+# Returns/Sets the FCM 2 utility functional object.
+{   my $UTIL;
+    sub get_util {
+        $UTIL;
+    }
+    sub set_util {
+        $UTIL = $_[0];
+    }
+}
+
+# Expands (the keywords in) the specfied location (and REV), and returns them
+sub expand {
+    my ($in_loc, $in_rev) = @_;
+    my $target = $in_rev ? $in_loc . '@' . $in_rev : $in_loc;
+    my $locator = FCM::Context::Locator->new($target);
+    _unparse_loc(get_util()->loc_as_normalised($locator), $in_rev);
+}
+
+# Returns the corresponding browser URL for the input VC location
+sub get_browser_url {
+    my ($in_loc, $in_rev) = @_;
+    my $target = $in_rev ? $in_loc . '@' . $in_rev : $in_loc;
+    my $locator = FCM::Context::Locator->new($target);
+    get_util()->loc_browser_url($locator);
+}
+
+# Un-expands the specfied location (and REV) to keywords, and returns them
+sub unexpand {
+    my ($in_loc, $in_rev) = @_;
+    my $target = $in_rev ? $in_loc . '@' . $in_rev : $in_loc;
+    my $locator = FCM::Context::Locator->new($target);
+    _unparse_loc(get_util()->loc_as_keyword($locator), $in_rev);
+}
+
+# If $in_rev, returns (LOC, REV). Otherwise, returns LOC@REV
+sub _unparse_loc {
+    my ($loc, $in_rev) = @_;
+    if (!$loc) {
+        return;
+    }
+    if ($in_rev) {
+        my ($l, $r) = $loc =~ qr{\A (.*?) @([^@]+) \z}msx;
+        if ($l && $r) {
+            return ($l, $r);
+        }
+    }
+    return $loc;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::Keyword
+
+=head1 SYNOPSIS
+
+    use FCM1::Keyword;
+
+    $loc = FCM1::Keyword::expand('fcm:namespace/path@rev-keyword');
+    $loc = FCM1::Keyword::unexpand('svn://host/namespace/path@1234');
+
+    ($loc, $rev) = FCM1::Keyword::expand('fcm:namespace/path', 'rev-keyword');
+    ($loc, $rev) = FCM1::Keyword::unexpand('svn://host/namespace/path', 1234);
+
+    $loc = FCM1::Keyword::get_browser_url('fcm:namespace/path');
+    $loc = FCM1::Keyword::get_browser_url('svn://host/namespace/path');
+
+    $loc = FCM1::Keyword::get_browser_url('fcm:namespace/path@1234');
+    $loc = FCM1::Keyword::get_browser_url('svn://host/namespace/path@1234');
+
+    $loc = FCM1::Keyword::get_browser_url('fcm:namespace/path', 1234);
+    $loc = FCM1::Keyword::get_browser_url('svn://host/namespace/path', 1234);
+
+=head1 DESCRIPTION
+
+Provides a compatibility layer for code in FCM1::* name space by wrapping the
+keyword related functions in L<FCM::Util|FCM::Util>. An instance of
+L<FCM::Util|FCM::Util> must be set via the set_util($value) function before
+using the other functions.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item expand($loc)
+
+Expands FCM keywords in $loc and returns the result.
+
+If $loc is a I<fcm> scheme URI, the leading part (before any "/" or "@"
+characters) of the URI opaque is the namespace of a FCM location keyword. This
+is expanded into the actual value. Optionally, $loc can be suffixed with a peg
+revision (an "@" followed by any characters). If a peg revision is a FCM
+revision keyword, it is expanded into the actual revision.
+
+=item expand($loc,$rev)
+
+Same as C<expand($loc)>, but $loc should not contain a peg revision. Returns a
+list containing the expanded version of $loc and $rev.
+
+=item get_browser_url($loc)
+
+Given a repository $loc in a known keyword namespace, returns the corresponding
+URL for the code browser.
+
+Optionally, $loc can be suffixed with a peg revision (an "@" followed by any
+characters).
+
+=item get_browser_url($loc,$rev)
+
+Same as get_browser_url($loc), but the revision should be specified using $rev
+but not pegged with $loc.
+
+=item get_util()
+
+Returns the L<FCM::Util|FCM::Util> instance (set by set_util($value)).
+
+=item set_util($value)
+
+Sets the L<FCM::Util|FCM::Util> instance.
+
+=item unexpand($loc)
+
+Does the opposite of expand($loc). Returns the FCM location keyword equivalence
+of $loc. If the $loc can be mapped using 2 or more namespaces, the namespace
+that results in the longest substitution is used. Optionally, $loc can be
+suffixed with a peg revision (an "@" followed by any characters). If a peg
+revision is a known revision, it is turned into its corresponding revision
+keyword.
+
+=item unexpand($loc,$rev)
+
+Same as unexpand($loc), but $loc should not contain a peg revision. Returns a
+list containing the unexpanded version of $loc and $rev 
+
+=item 
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM::System|FCM::System>,
+L<FCM::Util|FCM::Util>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ReposBranch.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ReposBranch.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/ReposBranch.pm	(revision 5095)
@@ -0,0 +1,528 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::ReposBranch
+#
+# DESCRIPTION
+#   This class contains methods for gathering information for a repository
+#   branch. It currently supports Subversion repository and local user
+#   directory.
+#
+# ------------------------------------------------------------------------------
+
+use warnings;
+use strict;
+
+package FCM1::ReposBranch;
+use base qw{FCM1::Base};
+
+use FCM1::CfgLine;
+use FCM1::Keyword;
+use FCM1::Util      qw{expand_tilde is_url run_command w_report};
+use File::Basename qw{dirname};
+use File::Find     qw{find};
+use File::Spec;
+
+# List of scalar property methods for this class
+my @scalar_properties = (
+  'package',  # package name of which this repository belongs
+  'repos',    # repository branch root URL/path
+  'revision', # the revision of this branch
+  'tag',      # "tag" name of this branch of the repository
+  'type',     # repository type
+);
+
+# List of hash property methods for this class
+my @hash_properties = (
+  'dirs',    # list of non-recursive directories in this branch
+  'expdirs', # list of recursive directories in this branch
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::ReposBranch->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::ReposBranch class. See
+#   @scalar_properties above for allowed list of properties in the constructor.
+#   (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  for (@scalar_properties) {
+    $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
+  }
+
+  $self->{$_} = {} for (@hash_properties);
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %hash = %{ $obj->X () };
+#   $obj->X (\%hash);
+#
+#   $value = $obj->X ($index);
+#   $obj->X ($index, $value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @hash_properties.
+#
+#   If no argument is set, this method returns a hash containing a list of
+#   objects. If an argument is set and it is a reference to a hash, the objects
+#   are replaced by the specified hash.
+#
+#   If a scalar argument is specified, this method returns a reference to an
+#   object, if the indexed object exists or undef if the indexed object does
+#   not exist. If a second argument is set, the $index element of the hash will
+#   be set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+for my $name (@hash_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my ($self, $arg1, $arg2) = @_;
+
+    # Ensure property is defined as a reference to a hash
+    $self->{$name} = {} if not defined ($self->{$name});
+
+    # Argument 1 can be a reference to a hash or a scalar index
+    my ($index, %hash);
+
+    if (defined $arg1) {
+      if (ref ($arg1) eq 'HASH') {
+        %hash = %$arg1;
+
+      } else {
+        $index = $arg1;
+      }
+    }
+
+    if (defined $index) {
+      # A scalar index is defined, set and/or return the value of an element
+      $self->{$name}{$index} = $arg2 if defined $arg2;
+
+      return (
+        exists $self->{$name}{$index} ? $self->{$name}{$index} : undef
+      );
+
+    } else {
+      # A scalar index is not defined, set and/or return the hash
+      $self->{$name} = \%hash if defined $arg1;
+      return $self->{$name};
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->expand_revision;
+#
+# DESCRIPTION
+#   This method expands the revision keywords of the current branch to a
+#   revision number. It returns true on success.
+# ------------------------------------------------------------------------------
+
+sub expand_revision {
+  my $self = shift;
+
+  my $rc = 1;
+  if ($self->type eq 'svn') {
+    # Expand revision keyword
+    my $rev = (FCM1::Keyword::expand($self->repos(), $self->revision()))[1];
+
+    # Get last changed revision of the specified revision
+    my $info_ref = $self->_svn_info($self->repos(), $rev);
+    if (!defined($info_ref->{'Revision'})) {
+      my $url = $self->repos() . ($rev ? '@' . $rev : q{});
+      w_report("ERROR: $url: not a valid URL\n");
+      return 0;
+    }
+    my $lc_rev = $info_ref->{'Last Changed Rev'};
+    $rev       = $info_ref->{'Revision'};
+
+    # Print info if specified revision is not the last commit revision
+    if (uc($self->revision()) ne 'HEAD' && $lc_rev != $rev) {
+      my $message = $self->repos . '@' . $rev . ': last changed at [' .
+                    $lc_rev . '].';
+      if ($self->setting ('EXT_REVMATCH') and uc ($self->revision) ne 'HEAD') {
+        w_report "ERROR: specified and last changed revisions differ:\n",
+                 '       ', $message, "\n";
+        $rc = 0;
+
+      } else {
+        print 'INFO: ', $message, "\n";
+      }
+    }
+
+    if ($self->verbose > 1 and uc ($self->revision) ne 'HEAD') {
+      # See if there is a later change of the branch at the HEAD
+      my $head_lc_rev = $self->_svn_info($self->repos())->{'Last Changed Rev'};
+
+      if (defined($head_lc_rev) && $head_lc_rev != $lc_rev) {
+        # Ensure that this is the same branch by checking its history
+        my @lines = &run_command (
+          [qw/svn log -q --incremental -r/, $lc_rev, $self->repos . '@HEAD'],
+          METHOD => 'qx', TIME => $self->verbose > 2, ERROR => 'ignore',
+        );
+
+        print 'INFO: ', $self->repos, '@', $rev,
+              ': newest commit at [', $head_lc_rev, '].', "\n"
+          if @lines;
+      }
+    }
+
+    $self->revision ($rev) if $rev ne $self->revision;
+
+  } elsif ($self->type eq 'user') {
+    1; # Do nothing
+
+  } else {
+    w_report 'ERROR: ', $self->repos, ': repository type "', $self->type,
+             '" not supported.';
+    $rc = 0;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->expand_path;
+#
+# DESCRIPTION
+#   This method expands the relative path names of sub-directories to full
+#   path names. It returns true on success.
+# ------------------------------------------------------------------------------
+
+sub expand_path {
+  my $self = shift;
+
+  my $rc = 1;
+  if ($self->type eq 'svn') {
+    # SVN repository
+    # Do nothing unless there is a declared repository for this branch
+    return unless $self->repos;
+
+    # Remove trailing /
+    my $repos = $self->repos;
+    $self->repos ($repos) if $repos =~ s#/+$##;
+
+    # 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) ? ('/' . $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 /
+      my $repos = $self->repos;
+      $self->repos ($repos) if $repos =~ s#/+$##;
+
+      # 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)
+                  ? File::Spec->catfile ($self->repos, $self->$name ($dir))
+                  : $self->repos
+          );
+        }
+      }
+    }
+
+  } else {
+    w_report 'ERROR: ', $self->repos, ': repository type "', $self->type,
+             '" not supported.';
+    $rc = 0;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->expand_all();
+#
+# DESCRIPTION
+#   This method searches the expandable source directories recursively for
+#   source directories containing regular files. The namespaces and the locators
+#   of these sub-directories are then added to the source directory hash table.
+#   Returns true on success.
+# ------------------------------------------------------------------------------
+
+sub expand_all {
+  my ($self) = @_;
+  my %finder_of = (
+    user => sub {
+      my ($root_locator) = @_;
+      my %ns_of;
+      my $wanted = sub {
+        my $base_name = $_;
+        my $path = $File::Find::name;
+        if (-f $path && !-l $path) {
+          my $dir_path      = dirname($path);
+          my $rel_dir_path  = File::Spec->abs2rel($dir_path, $root_locator);
+          if ($rel_dir_path eq q{.}) {
+             $rel_dir_path = q{};
+          }
+          if (!exists($ns_of{$dir_path})) {
+            $ns_of{$dir_path} = [File::Spec->splitdir($rel_dir_path)];
+          }
+        }
+      };
+      find($wanted, $root_locator);
+      return \%ns_of;
+    },
+    svn  => sub {
+      my ($root_locator) = @_;
+      my $runner = sub {
+        map {chomp($_); $_} run_command(
+          ['svn', @_,  '-R', join('@', $root_locator, $self->revision())],
+          METHOD => 'qx', TIME => $self->config()->verbose() > 2,
+        );
+      };
+      # FIXME: check for symlink switched off due to "svn pg" being very slow
+      #my %symlink_in
+      #  = map {($_ =~ qr{\A(.+)\s-\s(\*)\z}xms)} ($runner->(qw{pg svn:special}));
+      #my @locators
+      #  = grep {$_ !~ qr{/\z}xms && !$symlink_in{$_}} ($runner->('ls'));
+      my @locators = grep {$_ !~ qr{/\z}xms} ($runner->('ls'));
+      my %ns_of;
+      for my $locator (@locators) {
+        my ($rel_dir_locator) = $locator =~ qr{\A(.*)/[^/]+\z}xms; # dirname
+        $rel_dir_locator ||= q{};
+        my $dir_locator
+          = $rel_dir_locator ? join(q{/}, $root_locator, $rel_dir_locator)
+          :                    $root_locator
+          ;
+        if (!exists($ns_of{$dir_locator})) {
+          $ns_of{$dir_locator} = [split(q{/}, $rel_dir_locator)];
+        }
+      }
+      return \%ns_of;
+    },
+  );
+
+  if (!defined($finder_of{$self->type()})) {
+    w_report(sprintf(
+        qq{ERROR: %s: resource type "%s" not supported},
+        $self->repos(),
+        $self->type(),
+    ));
+    return;
+  }
+  while (my ($root_ns, $root_locator) = each(%{$self->expdirs()})) {
+    my @root_ns_list = split(qr{$FCM1::Config::DELIMITER}xms, $root_ns);
+    my $ns_hash_ref = $finder_of{$self->type()}->($root_locator);
+    while (my ($dir_path, $ns_list_ref) = each(%{$ns_hash_ref})) {
+      if (!grep {$_ =~ qr{\A\.}xms || $_ =~ qr{~\z}xms} @{$ns_list_ref}) {
+        my $ns = join($FCM1::Config::DELIMITER, @root_ns_list, @{$ns_list_ref});
+        $self->dirs($ns, $dir_path);
+      }
+    }
+  }
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $n = $obj->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 FCM1::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->dirs ($key, $base_dirs{$key});
+  }
+
+  # Expand relative path names of sub-directories
+  $self->expand_path;
+
+  return scalar keys %{ $self->dirs };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @cfglines = $obj->to_cfglines ();
+#
+# DESCRIPTION
+#   This method returns a list of configuration lines for the current branch.
+# ------------------------------------------------------------------------------
+
+sub to_cfglines {
+  my ($self) = @_;
+  my @return = ();
+
+  my $suffix = $self->package . $FCM1::Config::DELIMITER . $self->tag;
+  push @return, FCM1::CfgLine->new (
+    label => $self->cfglabel ('REPOS') . $FCM1::Config::DELIMITER . $suffix,
+    value => $self->repos,
+  ) if $self->repos;
+
+  push @return, FCM1::CfgLine->new (
+    label => $self->cfglabel ('REVISION') . $FCM1::Config::DELIMITER . $suffix,
+    value => $self->revision,
+  ) if $self->revision;
+
+  for my $key (sort keys %{ $self->dirs }) {
+    my $value = $self->dirs ($key);
+
+    # Use relative path where possible
+    if ($self->repos) {
+      if ($value eq $self->repos) {
+        $value = '';
+
+      } elsif (index ($value, $self->repos) == 0) {
+        $value = substr ($value, length ($self->repos) + 1);
+      }
+    }
+
+    # Use top package name where possible
+    my $dsuffix = $key . $FCM1::Config::DELIMITER . $self->tag;
+    $dsuffix = $suffix if $value ne $self->dirs ($key) and $key eq join (
+      $FCM1::Config::DELIMITER, $self->package, File::Spec->splitdir ($value)
+    );
+
+    push @return, FCM1::CfgLine->new (
+      label => $self->cfglabel ('DIRS') . $FCM1::Config::DELIMITER . $dsuffix,
+      value => $value,
+    );
+  }
+
+  push @return, FCM1::CfgLine->new ();
+
+  return @return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   my $hash_ref = $self->_svn_info($url[, $rev]);
+#
+# DESCRIPTION
+#   Executes "svn info" and returns each field in a hash.
+# ------------------------------------------------------------------------------
+sub _svn_info {
+  my ($self, $url, $rev) = @_;
+  return {
+    map {
+      chomp();
+      my ($key, $value) = split(qr{\s*:\s*}xms, $_, 2);
+      $key ? ($key, $value) : ();
+    } run_command(
+      [qw{svn info}, ($rev ? ('-r', $rev, join('@', $url, $rev)) : $url)], 
+      DEVNULL => 1,
+      ERROR   => 'ignore',
+      METHOD  => 'qx',
+      TIME    => $self->verbose() > 2,
+    )
+  };
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/SrcDirLayer.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/SrcDirLayer.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/SrcDirLayer.pm	(revision 5095)
@@ -0,0 +1,277 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::SrcDirLayer
+#
+# DESCRIPTION
+#   This class contains methods to manipulate the extract of a source
+#   directory from a branch of a (Subversion) repository.
+#
+# ------------------------------------------------------------------------------
+use warnings;
+use strict;
+
+package FCM1::SrcDirLayer;
+use base qw{FCM1::Base};
+
+use FCM1::Util      qw{run_command e_report w_report};
+use File::Basename qw{dirname};
+use File::Path     qw{mkpath};
+use File::Spec;
+
+# List of property methods for this class
+my @scalar_properties = (
+  'cachedir',  # cache directory for this directory branch
+  'commit',    # revision at which the source directory was changed
+  'extracted', # is this branch already extracted?
+  'files',     # list of source files in this directory branch
+  'location',  # location of the source directory in the branch
+  'name',      # sub-package name of the source directory
+  'package',   # top level package name of which the current repository belongs
+  'reposroot', # repository root URL
+  'revision',  # revision of the repository branch
+  'tag',       # package/revision tag of the current repository branch
+  'type',      # type of the repository branch ("svn" or "user")
+);
+
+my %ERR_MESS_OF = (
+    CACHE_WRITE => '%s: cannot write to cache',
+    SYMLINK     => '%s/%s: ignore symbolic link',
+    VC_TYPE     => '%s: repository type not supported',
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = FCM1::SrcDirLayer->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the FCM1::SrcDirLayer class. See
+#   above for allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = FCM1::Base->new (%args);
+
+  for (@scalar_properties) {
+    $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
+  }
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $value = $obj->X;
+#   $obj->X ($value);
+#
+# DESCRIPTION
+#   Details of these properties are explained in @scalar_properties.
+# ------------------------------------------------------------------------------
+
+for my $name (@scalar_properties) {
+  no strict 'refs';
+
+  *$name = sub {
+    my $self = shift;
+
+    # Argument specified, set property to specified argument
+    if (@_) {
+      $self->{$name} = $_[0];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'files') {
+        # Reference to an array
+        $self->{$name} = [];
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# Handles error/warning events.
+sub _err {
+    my ($key, $args_ref, $warn_only) = @_;
+    my $reporter = $warn_only ? \&w_report : \&e_report;
+    $args_ref ||= [];
+    $reporter->(sprintf($ERR_MESS_OF{$key} . ".\n", @{$args_ref}));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dir = $obj->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
+#   $user = $obj->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
+#   $rev = $obj->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 -r/, $self->revision, $self->location . '@' . $self->revision],
+      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 revision of this source directory
+    $self->commit ($rev);
+
+    return $self->commit;
+
+  } elsif ($self->type eq 'user') {
+    return;
+
+  } else {
+    _err('VC_TYPE', [$self->type()]);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $obj->update_cache;
+#
+# DESCRIPTION
+#   If the current repository type is "svn", this method attempts to extract
+#   the current revision 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 (!-d $dirname) {
+    _err('CACHE_WRITE', [$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->revision,
+      $self->cachedir,
+    );
+
+    &run_command (\@command, TIME => $self->config->verbose > 2);
+
+  } elsif ($self->type eq 'user') {
+    return;
+
+  } else {
+    _err('VC_TYPE', [$self->type()]);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @files = $obj->get_files();
+#
+# DESCRIPTION
+#   This method returns a list of file base names in the (cache of) this source
+#   directory in the current branch.
+# ------------------------------------------------------------------------------
+
+sub get_files {
+  my ($self) = @_;
+  opendir(my $dir, $self->localdir())
+    || die($self->localdir(), ': cannot read directory');
+  my @base_names = ();
+  BASE_NAME:
+  while (my $base_name = readdir($dir)) {
+    if ($base_name =~ qr{\A\.}xms || $base_name =~ qr{~\z}xms) {
+        next BASE_NAME;
+    }
+    my $path = File::Spec->catfile($self->localdir(), $base_name);
+    if (-d $path) {
+        next BASE_NAME;
+    }
+    if (-l $path) {
+        _err('SYMLINK', [$self->location(), $base_name], 1);
+        next BASE_NAME;
+    }
+    push(@base_names, $base_name);
+  }
+  closedir($dir);
+  $self->files(\@base_names);
+  return @base_names;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Timer.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Timer.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Timer.pm	(revision 5095)
@@ -0,0 +1,85 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Timer
+#
+# DESCRIPTION
+#   This is a package of timer utility used by the FCM command.
+#
+# ------------------------------------------------------------------------------
+
+package FCM1::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 = &FCM1::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: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Util.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Util.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Util.pm	(revision 5095)
@@ -0,0 +1,564 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#   FCM1::Util
+#
+# DESCRIPTION
+#   This is a package of misc utilities used by the FCM command.
+#
+# ------------------------------------------------------------------------------
+
+use warnings;
+use strict;
+
+package FCM1::Util;
+require Exporter;
+our @ISA = qw{Exporter};
+
+sub expand_tilde;
+sub e_report;
+sub find_file_in_path;
+sub get_command_string;
+sub get_rev_of_wc;
+sub get_url_of_wc;
+sub get_url_peg_of_wc;
+sub get_wct;
+sub is_url;
+sub is_wc;
+sub print_command;
+sub run_command;
+sub svn_date;
+sub tidy_url;
+sub touch_file;
+sub w_report;
+
+our @EXPORT = qw{
+    expand_tilde
+    e_report
+    find_file_in_path
+    get_command_string
+    get_rev_of_wc
+    get_url_of_wc
+    get_url_peg_of_wc
+    get_wct
+    is_url
+    is_wc
+    print_command
+    run_command
+    svn_date
+    tidy_url
+    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 SIGINT SIGKILL SIGTERM WEXITSTATUS WIFSIGNALED WTERMSIG};
+
+# FCM component modules
+use FCM1::Timer;
+
+# ------------------------------------------------------------------------------
+
+# Module level variables
+my %svn_info       = (); # "svn info" log, (key1 = path,
+                         # key2 = URL, Revision, Last Changed Rev)
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %srcdir = &FCM1::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 = &FCM1::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;
+
+  # Expand . and ..
+  while ($file =~ s#/+\.(?:/+|$)#/#g) {next}
+  while ($file =~ s#/+[^/]+/+\.\.(?:/+|$)#/#g) {next}
+
+  # Remove trailing /
+  $file =~ s#/*$##;
+
+  return $file;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &FCM1::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
+#   $flag = &is_wc ([$path]);
+#
+# DESCRIPTION
+#   Returns true if current working directory (or $path) is a Subversion
+#   working copy.
+# ------------------------------------------------------------------------------
+
+sub is_wc {
+  my $path = shift() || cwd();
+  my $path_of_dir = -f $path ? dirname($path) : $path;
+  if (-e File::Spec->catfile($path_of_dir, qw{.svn entries})) {
+      return 1;
+  }
+  my $inforc = &run_command (
+    [qw/svn info/, $path_of_dir],
+    METHOD => 'qx', DEVNULL => 1, ERROR => 'ignore'
+  );
+  return $inforc != 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
+#   $url = tidy_url($url);
+#
+# DESCRIPTION
+#   Returns a tidied version of $url by removing . and .. in the path.
+# ------------------------------------------------------------------------------
+
+sub tidy_url {
+    my ($url) = @_;
+    if (!is_url($url)) {
+        return $url;
+    }
+    my $DOT_PATTERN     = qr{/+ \. (?:/+|(@|\z))}xms;
+    my $DOT_DOT_PATTERN = qr{/+ [^/]+ /+ \.\. (?:/+|(@|\z))}xms;
+    my $TRAILING_SLASH_PATTERN = qr{([^/]+) /* (@|\z)}xms;
+    my $RIGHT_EVAL = q{'/' . ($1 ? $1 : '')};
+    DOT:
+    while ($url =~ s{$DOT_PATTERN}{$RIGHT_EVAL}eegxms) {
+        next DOT;
+    }
+    DOT_DOT:
+    while ($url =~ s{$DOT_DOT_PATTERN}{$RIGHT_EVAL}eegxms) {
+        next DOT_DOT;
+    }
+    $url =~ s{$TRAILING_SLASH_PATTERN}{$1$2}xms;
+    return $url;
+}
+
+# ------------------------------------------------------------------------------
+# 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
+#   $string = &get_url_peg_of_wc ([$path[, $refresh]]);
+#
+# DESCRIPTION
+#   If current working directory (or $path) is a Subversion working copy,
+#   returns the URL@REV of the associated Subversion repository; otherwise
+#   returns an empty string. If $refresh is specified, do not use the cached
+#   information.
+# ------------------------------------------------------------------------------
+
+sub get_url_peg_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} . '@' . $svn_info{$path}{Revision};
+  }
+
+  return $url;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &_invoke_svn_info (PATH => $path);
+#
+# 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.
+# ------------------------------------------------------------------------------
+
+sub _invoke_svn_info {
+  my %args = @_;
+  my $path = $args{PATH};
+  my $cfg  = FCM1::Config->instance();
+
+  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, %input_opt_of) = @_;
+  my %opt_of = (
+    DEVNULL => undef,
+    ERROR   => 'die',
+    METHOD  => 'system',
+    PRINT   => undef,
+    RC      => undef,
+    TIME    => undef,
+    %input_opt_of,
+  );
+  local($|) = 1; # Make sure STDOUT is flushed before running command
+
+  # Print the command before execution, if necessary
+  if ($opt_of{TIME}) {
+    print(timestamp_command(get_command_string($cmd)));
+  }
+  elsif ($opt_of{PRINT}) {
+    print_command($cmd);
+  }
+
+  # Re-direct STDERR to /dev/null if necessary
+  if ($opt_of{DEVNULL}) {
+    no warnings;
+    open(OLDERR, ">&STDERR") || croak("Cannot dup STDERR ($!), abort");
+    use warnings;
+    open(STDERR, '>', File::Spec->devnull())
+      || croak("Cannot redirect STDERR ($!), abort");
+    # Make sure the channels are unbuffered
+    my $select = select();
+    select(STDERR); local($|) = 1;
+    select($select);
+  }
+
+  my @return = ();
+  if (ref($cmd) && ref($cmd) eq 'ARRAY') {
+    # $cmd is an array
+    my @command = @{$cmd};
+    if ($opt_of{METHOD} eq 'qx') {
+      @return = qx(@command);
+    }
+    elsif ($opt_of{METHOD} eq 'exec') {
+      exec(@command);
+    }
+    else {
+      system(@command);
+      @return = $? ? () : (1);
+    }
+  }
+  else {
+    # $cmd is an scalar
+    if ($opt_of{METHOD} eq 'qx') {
+      @return = qx($cmd);
+    }
+    elsif ($opt_of{METHOD} eq 'exec') {
+      exec($cmd);
+    }
+    else {
+      system($cmd);
+      @return = $? ? () : (1);
+    }
+  }
+  my $rc = $?;
+
+  # Put STDERR back to normal, if redirected previously
+  if ($opt_of{DEVNULL}) {
+    close(STDERR);
+    open(STDERR, ">&OLDERR") || croak("Cannot dup STDERR ($!), abort");
+  }
+
+  # Print the time taken for command after execution, if necessary
+  if ($opt_of{TIME}) {
+    print(timestamp_command(get_command_string($cmd), 'end'));
+  }
+
+  # Signal and return code
+  my ($signal, $status) = (WTERMSIG($rc), WEXITSTATUS($rc));
+  if (exists($opt_of{RC})) {
+    ${$opt_of{RC}} = $status;
+  }
+  if (WIFSIGNALED($rc) && grep {$signal == $_} (SIGINT, SIGKILL, SIGTERM)) {
+    croak(sprintf('%s terminated (%d)', get_command_string($cmd), $signal));
+  }
+  if ($status && $opt_of{ERROR} ne 'ignore') {
+    my $func_ref = $opt_of{ERROR} eq 'warn' ? \&carp : \&croak;
+    $func_ref->(sprintf('%s failed (%d)', get_command_string($cmd), $status));
+  }
+  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__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Util/ClassLoader.pm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Util/ClassLoader.pm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/lib/FCM1/Util/ClassLoader.pm	(revision 5095)
@@ -0,0 +1,93 @@
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package FCM1::Util::ClassLoader;
+use base qw{Exporter};
+
+our @EXPORT_OK = qw{load};
+
+use Carp qw{croak};
+use FCM1::Exception;
+
+sub load {
+    my ($class, $test_method) = @_;
+    if (!$test_method) {
+        $test_method = 'new';
+    }
+    if (!UNIVERSAL::can($class, $test_method)) {
+        eval('require ' . $class);
+        if ($@) {
+            croak(FCM1::Exception->new({message => sprintf(
+                "%s: class loading failed: %s", $class, $@,
+            )}));
+        }
+    }
+    return $class;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+FCM1::ClassLoader
+
+=head1 SYNOPSIS
+
+    use FCM1::Util::ClassLoader;
+    $load_ok = FCM1::Util::ClassLoader::load($class);
+
+=head1 DESCRIPTION
+
+A wrapper for loading a class dynamically.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item load($class,$test_method)
+
+If $class can call $test_method, returns $class. Otherwise, attempts to
+require() $class and returns it. If this fails, croak() with a
+L<FCM1::Exception|FCM1::Exception>.
+
+=item load($class)
+
+Shorthand for C<load($class, 'new')>.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<FCM1::Exception|FCM1::Exception>
+
+The load($class,$test_method) function croak() with this exception if it fails
+to load the specified class.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/licences/Apache2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/licences/Apache2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/licences/Apache2	(revision 5095)
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/licences/GPL3
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/licences/GPL3	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/licences/GPL3	(revision 5095)
@@ -0,0 +1,1 @@
+link ../COPYING
Index: LMDZ6/branches/Amaury_dev/tools/fcm/licences/MIT
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/licences/MIT	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/licences/MIT	(revision 5095)
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2011-2015 Twitter, Inc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/man/man1/fcm.1
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/man/man1/fcm.1	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/man/man1/fcm.1	(revision 5095)
@@ -0,0 +1,42 @@
+.\" Process this file with
+.\" groff -man -Tascii fcm.1
+.\"
+.TH fcm 1 "" "" "User Commands"
+.SH NAME
+fcm - FCM, tools for managing and building source code.
+.SH SYNOPSIS
+.B fcm
+.I command
+[
+.I options
+] [
+.I args
+]
+.SH OVERVIEW
+.B fcm
+is a set of tools for managing and building source code.
+For full detail of the system, please refer to the FCM user guide, which you
+should receive with this distribution.
+.PP
+Run "fcm help" to access the built-in tool documentation.
+.SH AUTHOR
+FCM Team <metomi@metoffice.gov.uk>.
+Please feedback any bug reports or feature requests to us by e-mail.
+.SH COPYRIGHT
+\(co Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+.PP
+FCM is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+.PP
+FCM is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+.PP
+You should have received a copy of the GNU General Public License
+along with FCM. If not, see <http://www.gnu.org/licenses/>.
+.SH SEE ALSO
+.BR svn (1),
+.BR perl (1)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-svn-repos
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-svn-repos	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-svn-repos	(revision 5095)
@@ -0,0 +1,105 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{add_svn_repository};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{svn-live-dir=s},
+        q{svn-project-suffix=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    if (@ARGV != 1) {
+        my $message = sprintf(
+            qq{Expected exactly 1 argument, %d given.}, scalar(@ARGV),
+        );
+        pod2usage({q{-exitval} => 1, q{-message} => $message});
+    }
+    my ($project_name) = @ARGV;
+    add_svn_repository($project_name);
+}
+
+__END__
+=head1 NAME
+
+fcm-add-trac-env
+
+=head1 SYNOPSIS
+
+    fcm-add-svn-repos [OPTIONS] PROJECT
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --svn-live-dir=DIR
+
+Specifies the root location of the live directory of Subversion repositories.
+See L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-project-suffix=NAME
+
+Specifies the suffix added to the project name for Subversion repositories. The
+default is "_svn".
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies the name of the project to add.
+
+=back
+
+=head1 DESCRIPTION
+
+This program adds a new Subversion repository.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-svn-repos-and-trac-env
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-svn-repos-and-trac-env	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-svn-repos-and-trac-env	(revision 5095)
@@ -0,0 +1,119 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{add_svn_repository add_trac_environment};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{svn-live-dir=s},
+        q{svn-project-suffix=s},
+        q{trac-host-id=s},
+        q{trac-live-dir=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    if (@ARGV != 1) {
+        my $message = sprintf(
+            qq{Expected exactly 1 argument, %d given.}, scalar(@ARGV),
+        );
+        pod2usage({q{-exitval} => 1, q{-message} => $message});
+    }
+    my ($project_name) = @ARGV;
+    add_svn_repository($project_name);
+    add_trac_environment($project_name);
+}
+
+__END__
+=head1 NAME
+
+fcm-add-trac-env
+
+=head1 SYNOPSIS
+
+    fcm-add-svn-repos-and-trac-env [OPTIONS] PROJECT
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --svn-live-dir=DIR
+
+Specifies the root location of the live directory of Subversion repositories.
+See L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-project-suffix=NAME
+
+Specifies the suffix added to the project name for Subversion repositories. The
+default is "_svn".
+
+=item --trac-host-id=HOST
+
+Specifies the host ID of the Trac server for the new Trac environment. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --trac-live-dir=DIR
+
+Specifies the root location of the live directory of Trac environments. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies the name of the project to add.
+
+=back
+
+=head1 DESCRIPTION
+
+This program adds a new Subversion repository with an associated Trac
+environment to their live directories.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-trac-env
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-trac-env	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-add-trac-env	(revision 5095)
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{add_trac_environment};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{svn-live-dir=s},
+        q{svn-project-suffix=s},
+        q{trac-host-id=s},
+        q{trac-live-dir=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    if (@ARGV != 1) {
+        my $message = sprintf(
+            qq{Expected exactly 1 argument, %d given.}, scalar(@ARGV),
+        );
+        pod2usage({q{-exitval} => 1, q{-message} => $message});
+    }
+    my ($project_name) = @ARGV;
+    add_trac_environment($project_name);
+}
+
+__END__
+=head1 NAME
+
+fcm-add-trac-env
+
+=head1 SYNOPSIS
+
+    fcm-add-trac-env [OPTIONS] PROJECT
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --svn-live-dir=DIR
+
+Specifies the root location of the live directory of Subversion repositories.
+See L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-project-suffix=NAME
+
+Specifies the suffix added to the project name for Subversion repositories. The
+default is "_svn".
+
+=item --trac-host-id=HOST
+
+Specifies the host ID of the Trac server for the new Trac environment. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --trac-live-dir=DIR
+
+Specifies the root location of the live directory of Trac environments. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies the name of the project to add.
+
+=back
+
+=head1 DESCRIPTION
+
+This program adds a new Trac environment to the live directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-backup-svn-repos
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-backup-svn-repos	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-backup-svn-repos	(revision 5095)
@@ -0,0 +1,137 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    backup_svn_repository
+    filter_projects
+    get_projects_from_svn_live
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{no-housekeep-dumps},
+        q{no-pack},
+        q{no-verify-integrity},
+        q{svn-backup-dir=s},
+        q{svn-dump-dir=s},
+        q{svn-live-dir=s},
+        q{svn-project-suffix=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    my @projects = filter_projects([get_projects_from_svn_live()], \@ARGV);
+    for my $project (@projects) {
+        backup_svn_repository(\%option, $project);
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-backup-svn-repos
+
+=head1 SYNOPSIS
+
+    fcm-backup-svn-repos [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --no-housekeep-dumps
+
+If this option is specified, the program will not housekeep the backup revision
+dumps of each repository.
+
+=item --no-pack
+
+If this option is specified, the program will not pack each repository before
+running the backup.
+
+=item --no-verify-integrity
+
+If this option is specified, the program will not verify the integrity of a
+repository before running the backup.
+
+=item --svn-backup-dir=DIR
+
+Specifies the root location of the backup directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-dump-dir=DIR
+
+Specifies the root location of the directory where revision dumps are kept. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-live-dir=DIR
+
+Specifies the root location of the live directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-project-suffix=NAME
+
+Specifies the suffix added to the project name. The default is "_svn".
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies one or more project to back up. If no project is specified, the
+program searches the live directory for projects to back up.
+
+=back
+
+=head1 DESCRIPTION
+
+This program archives Subversion repositories in the live directory to the
+backup directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-backup-trac-env
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-backup-trac-env	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-backup-trac-env	(revision 5095)
@@ -0,0 +1,118 @@
+#!/usr/bin/perl
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    backup_trac_environment
+    backup_trac_files
+    filter_projects
+    get_projects_from_trac_live
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{no-verify-integrity},
+        q{trac-backup-dir=s},
+        q{trac-live-dir=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    my @projects = filter_projects([get_projects_from_trac_live()], \@ARGV);
+    for my $project (@projects) {
+        backup_trac_environment(\%option, $project);
+    }
+    if (!@ARGV) {
+        backup_trac_files();
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-backup-trac-env
+
+=head1 SYNOPSIS
+
+    fcm-backup-trac-env [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --no-verify-integrity
+
+If this option is specified, the program will not verify the integrity of the
+database before running the backup.
+
+=item --trac-backup-dir=DIR
+
+Specifies the root location of the backup directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --trac-live-dir=DIR
+
+Specifies the root location of the live directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies one or more project to back up. If no project is specified, the
+program searches the live directory for projects and other files to back up.
+
+=back
+
+=head1 DESCRIPTION
+
+This program archives Trac environments, and other files in the live directory
+to the backup directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-commit-update
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-commit-update	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-commit-update	(revision 5095)
@@ -0,0 +1,170 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use File::Basename qw{basename};
+use File::Spec;
+
+use FCM::Admin::Config;
+use FCM::Admin::Runner;
+use FCM::Admin::System qw{
+    distribute_wc
+    filter_projects
+    get_projects_from_svn_live
+    install_svn_hook
+};
+use FCM::Admin::Util qw{
+    run_mkpath
+    run_rmtree
+    run_svn_info
+    run_svn_update
+    write_file
+};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+use Text::ParseWords qw{shellwords};
+
+# ------------------------------------------------------------------------------
+my $CONFIG = FCM::Admin::Config->instance();
+my %PATTERN_OF = (
+    q{}      => qr{.*}xms,
+    SRC_HOOK => qr{svn-hooks/}xms,
+);
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+# ------------------------------------------------------------------------------
+# The main logic.
+sub main {
+    local(@ARGV) = @_;
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{force},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    create_lock() || return;
+    my $RUNNER = FCM::Admin::Runner->instance();
+    my $is_force = $option{'force'};
+    UPDATE:
+    while (1) {
+        my @updates;
+        for my $source_key (shellwords($CONFIG->get_mirror_keys())) {
+            my $method = "get_$source_key";
+            push(@updates, run_svn_update($CONFIG->$method()));
+        }
+        if (!$is_force && !@updates) {
+            last UPDATE;
+        }
+        if ($is_force || grep {$_ =~ $PATTERN_OF{'SRC_HOOK'}} @updates) {
+            $RUNNER->run(
+                '(re-)installing hook scripts',
+                sub {
+                    for my $project (get_projects_from_svn_live()) {
+                        install_svn_hook($project);
+                    }
+                    return 1;
+                }
+            );
+        }
+        if ($is_force || grep {$_ =~ $PATTERN_OF{q{}}} @updates) {
+            $RUNNER->run(
+                'distributing FCM to standard locations', \&distribute_wc);
+        }
+        $is_force = 0;
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Creates a lock. Returns true on success. Removes lock when program finishes.
+our $LOCK;
+sub create_lock {
+    my $home = (getpwuid($<))[7];
+    $LOCK = File::Spec->catfile($home, sprintf(".%s.lock", basename($0)));
+    if (-e $LOCK) {
+        $LOCK = undef;
+        return;
+    }
+    return run_mkpath($LOCK);
+    END {
+        if ($LOCK) {
+            run_rmtree($LOCK);
+        }
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-commit-update
+
+=head1 SYNOPSIS
+
+    fcm-commit-update
+
+=head1 DESCRIPTION
+
+This program performs the post-commit update for the FCM system. It runs
+continuously until no more update is available. It prevent another copy from
+running by creating a lock. If another copy detects a lock, it exits without
+doing anything.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --force
+
+Force an update.
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item REPOS-NAME
+
+The name of the repository invoking this program.
+
+=item LOG-DIR-PATH
+
+The path to the log directory.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-daily-update
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-daily-update	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-daily-update	(revision 5095)
@@ -0,0 +1,190 @@
+#!/usr/bin/perl
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use File::Basename qw{basename};
+use File::Spec::Functions qw{catfile};
+use IO::File;
+use Mail::Mailer;
+use Time::Piece qw{gmtime};
+
+use FCM::Admin::Config;
+use FCM::Admin::Runner;
+use FCM::Admin::System qw{
+    backup_svn_repository
+    backup_trac_environment
+    backup_trac_files
+    get_projects_from_svn_live
+    get_projects_from_trac_live
+    get_users
+    housekeep_svn_hook_logs
+    manage_users_in_svn_passwd
+    manage_users_in_trac_passwd
+    manage_users_in_trac_db_of
+};
+
+my $THIS = basename($0);
+my $CONFIG = FCM::Admin::Config->instance();
+my $UTIL = $FCM::Admin::Config::UTIL;
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+
+    # Redirects STDOUT and STDERR to the $out_file
+    open(my $old_out, q{>&}, \*STDOUT)
+        || die("$THIS: cannot duplicate STDOUT ($!)\n");
+    open(my $old_err, q{>&}, \*STDERR)
+        || die("$THIS: cannot duplicate STDERR ($!)\n");
+    my $log_dir = $UTIL->file_tilde_expand($CONFIG->get_log_dir());
+    my $now = gmtime();
+    my $day_of_week = lc($now->day_of_week()); # 0=sun, 1=mon, etc
+    my $day = lc($now->day()); # lower case day of week, e.g. sun, mon
+    my $out_file = catfile($log_dir, "$THIS-$day_of_week$day.log");
+    open(STDOUT, q{>}, $out_file)
+        || die("$THIS: cannot redirect STDOUT ($!)\n");
+    open(STDERR, q{>&}, \*STDOUT)
+        || die("$THIS: cannot redirect STDERR ($!)\n");
+
+    do_tasks();
+
+    # Restores STDOUT and STDERR
+    open(STDERR, q{>&}, $old_err)
+        || die("$THIS: cannot reinstate STDERR ($!)\n");
+    open(STDOUT, q{>&}, $old_out)
+        || die("$THIS: cannot reinstate STDOUT ($!)\n");
+
+    notify($out_file);
+}
+
+# ------------------------------------------------------------------------------
+# Performs the daily update tasks.
+sub do_tasks {
+    # (no argument)
+    my $RUNNER = FCM::Admin::Runner->instance();
+    my @svn_projects = get_projects_from_svn_live();
+    my @trac_projects = get_projects_from_trac_live();
+    my $user_ref = undef;
+    $RUNNER->run_continue(
+        "retrieving user accounts",
+        sub {$user_ref = get_users(); 1;},
+    );
+    if (defined($user_ref)) {
+        if ($CONFIG->get_svn_passwd_file()) {
+            $RUNNER->run_continue(
+                "updating SVN user accounts",
+                sub {manage_users_in_svn_passwd($user_ref)},
+            );
+        }
+        if ($CONFIG->get_trac_passwd_file()) {
+            $RUNNER->run_continue(
+                "updating Trac user accounts",
+                sub {manage_users_in_trac_passwd($user_ref)},
+            );
+        }
+        for my $project (@trac_projects) {
+            $RUNNER->run_continue(
+                "updating Trac accounts in $project",
+                sub {manage_users_in_trac_db_of($project, $user_ref)},
+            );
+        }
+    }
+    for my $project (@svn_projects) {
+        $RUNNER->run_continue(
+            "housekeep SVN repository logs for $project",
+            sub {housekeep_svn_hook_logs($project)},
+        );
+        $RUNNER->run_continue(
+            "backing up SVN repository for $project",
+            sub {backup_svn_repository({}, $project)},
+        );
+    }
+    for my $project (@trac_projects) {
+        $RUNNER->run_continue(
+            "backing up Trac environment for $project",
+            sub {backup_trac_environment({}, $project)},
+        );
+    }
+    $RUNNER->run_continue("backing up Trac files", \&backup_trac_files);
+}
+
+# ------------------------------------------------------------------------------
+# Notifies on completion.
+sub notify {
+    my ($out_file) = @_;
+    # Reports number of arguments in subject.
+    my @exceptions = FCM::Admin::Runner->instance()->get_exceptions();
+    my $subject
+        = sprintf(qq{$THIS finished with %d error(s)}, scalar(@exceptions));
+
+    my $mailer = Mail::Mailer->new();
+    $mailer->open({
+        From    => $CONFIG->get_notification_from(),
+        To      => $CONFIG->get_admin_email(),
+        Subject => $subject,
+    });
+
+    # Summarises the exceptions at the beginning of the message
+    $mailer->print(qq{$subject:\n});
+    for my $exception (@exceptions) {
+        $mailer->print(qq{    $exception});
+    }
+    $mailer->print(qq{\n});
+
+    # Prints content of the output
+    $mailer->print(q{#} x 72 . qq{\n});
+    $mailer->print(qq{# Output and error output of $THIS:\n});
+    $mailer->print(q{#} x 72 . qq{\n});
+    my $out_file_handle = IO::File->new($out_file);
+    if (defined($out_file_handle)) {
+        while (my $line = $out_file_handle->getline()) {
+            $mailer->print($line);
+        }
+        $out_file_handle->close();
+    }
+    $mailer->close();
+}
+
+__END__
+
+=head1 NAME
+
+fcm-daily-update
+
+=head1 SYNOPSIS
+
+    fcm-daily-update
+
+=head1 DESCRIPTION
+
+This program performs the daily update for the FCM system.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-daily-update2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-daily-update2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-daily-update2	(revision 5095)
@@ -0,0 +1,134 @@
+#!/usr/bin/perl
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+# This is a simplified version of fcm-daily-update which
+# a) doesn't create a log file
+# b) doesn't send an email notification
+# c) sets the exit code to the number of errors
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use File::Basename qw{basename};
+
+use FCM::Admin::Config;
+use FCM::Admin::Runner;
+use FCM::Admin::System qw{
+    backup_svn_repository
+    backup_trac_environment
+    backup_trac_files
+    get_projects_from_svn_live
+    get_projects_from_trac_live
+    get_users
+    housekeep_svn_hook_logs
+    manage_users_in_svn_passwd
+    manage_users_in_trac_passwd
+    manage_users_in_trac_db_of
+};
+
+my $THIS = basename($0);
+my $CONFIG = FCM::Admin::Config->instance();
+my $UTIL = $FCM::Admin::Config::UTIL;
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+
+    do_tasks();
+    my @exceptions = FCM::Admin::Runner->instance()->get_exceptions();
+    printf(qq{$THIS finished with %d error(s) \n}, scalar(@exceptions));
+    exit(scalar(@exceptions));
+}
+
+# ------------------------------------------------------------------------------
+# Performs the daily update tasks.
+sub do_tasks {
+    # (no argument)
+    my $RUNNER = FCM::Admin::Runner->instance();
+    my @svn_projects = get_projects_from_svn_live();
+    my @trac_projects = get_projects_from_trac_live();
+    my $user_ref = undef;
+    $RUNNER->run_continue(
+        "retrieving user accounts",
+        sub {$user_ref = get_users(); 1;},
+    );
+    if (defined($user_ref)) {
+        if ($CONFIG->get_svn_passwd_file()) {
+            $RUNNER->run_continue(
+                "updating SVN user accounts",
+                sub {manage_users_in_svn_passwd($user_ref)},
+            );
+        }
+        if ($CONFIG->get_trac_passwd_file()) {
+            $RUNNER->run_continue(
+                "updating Trac user accounts",
+                sub {manage_users_in_trac_passwd($user_ref)},
+            );
+        }
+        for my $project (@trac_projects) {
+            $RUNNER->run_continue(
+                "updating Trac accounts in $project",
+                sub {manage_users_in_trac_db_of($project, $user_ref)},
+            );
+        }
+    }
+    for my $project (@svn_projects) {
+        $RUNNER->run_continue(
+            "housekeep SVN repository logs for $project",
+            sub {housekeep_svn_hook_logs($project)},
+        );
+        $RUNNER->run_continue(
+            "backing up SVN repository for $project",
+            sub {backup_svn_repository({}, $project)},
+        );
+    }
+    for my $project (@trac_projects) {
+        $RUNNER->run_continue(
+            "backing up Trac environment for $project",
+            sub {backup_trac_environment({}, $project)},
+        );
+    }
+    $RUNNER->run_continue("backing up Trac files", \&backup_trac_files);
+}
+
+__END__
+
+=head1 NAME
+
+fcm-daily-update
+
+=head1 SYNOPSIS
+
+    fcm-daily-update
+
+=head1 DESCRIPTION
+
+This program performs the daily update for the FCM system.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-install-svn-hook
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-install-svn-hook	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-install-svn-hook	(revision 5095)
@@ -0,0 +1,115 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    filter_projects
+    get_projects_from_svn_live
+    housekeep_svn_hook_logs
+    install_svn_hook
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{clean},
+        q{help|usage|h},
+        q{svn-live-dir=s},
+        q{svn-project-suffix=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    my @projects = filter_projects([get_projects_from_svn_live()], \@ARGV);
+    for my $project (sort {$a->get_name() cmp $b->get_name()} @projects) {
+        install_svn_hook($project, $option{clean});
+        housekeep_svn_hook_logs($project);
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-install-svn-hook
+
+=head1 SYNOPSIS
+
+    fcm-install-svn-hook [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --clean
+
+Removes items (except logs) that are not in the install sources.
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --svn-live-dir=DIR
+
+Specifies the root location of the live directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-project-suffix=NAME
+
+Specifies the suffix added to the project name. The default is "_svn".
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies one or more project requiring hooks scripts to be installed. If no
+project is specified, the program install the hook scripts to all projects in
+the live directory.
+
+=back
+
+=head1 DESCRIPTION
+
+This program install hook scripts for Subversion repositories in the live
+directory, and install/housekeep the log files for the hook scripts.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-manage-trac-env-session
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-manage-trac-env-session	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-manage-trac-env-session	(revision 5095)
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    filter_projects
+    get_projects_from_trac_live
+    get_users
+    manage_users_in_trac_db_of
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{trac-live-dir=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    my $user_ref = get_users();
+    for my $project (filter_projects([get_projects_from_trac_live()], \@ARGV)) {
+        manage_users_in_trac_db_of($project, $user_ref),
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-manage-trac-env-session
+
+=head1 SYNOPSIS
+
+    fcm-manage-trac-env-session [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --trac-live-dir=DIR
+
+Specifies the root location of the live directory of the Trac environments. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 DESCRIPTION
+
+This program manages session and session attributes for authenticated users in
+Trac environments. If no PROJECT is specified, the program acts on all trac
+environments in the live directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-manage-users
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-manage-users	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-manage-users	(revision 5095)
@@ -0,0 +1,119 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    get_projects_from_trac_live
+    get_users
+    manage_users_in_svn_passwd
+    manage_users_in_trac_passwd
+    manage_users_in_trac_db_of
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{svn-live-dir=s},
+        q{svn-passwd-file=s},
+        q{trac-live-dir=s},
+        q{trac-passwd-file=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    if (@ARGV) {
+        my $message = sprintf("No argument expected, %d given", scalar(@ARGV));
+        pod2usage({q{-exitval} => 1, q{-message} => $message});
+    }
+    option2config(\%option);
+    my $user_ref = get_users();
+    manage_users_in_svn_passwd($user_ref);
+    manage_users_in_trac_passwd($user_ref);
+    my @projects = get_projects_from_trac_live();
+    for my $project (@projects) {
+        manage_users_in_trac_db_of($project, $user_ref),
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-manage-users
+
+=head1 SYNOPSIS
+
+    fcm-manage-users [OPTIONS]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --svn-live-dir=DIR
+
+Specifies the root location of the live directory of the Subversion
+repositories. See L<FCM::Admin::Config|FCM::Admin::Config> for the current
+default.
+
+=item --svn-passwd-file=FILE
+
+Specifies the base name of the Subversion password file. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --trac-live-dir=DIR
+
+Specifies the root location of the live directory of the Trac environments. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --trac-passwd-file=FILE
+
+Specifies the base name of the Trac password file. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 DESCRIPTION
+
+This program manages user (login) information for Subversion repositories and
+Trac environments hosted by FCM.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-recover-svn-repos
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-recover-svn-repos	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-recover-svn-repos	(revision 5095)
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    filter_projects
+    get_projects_from_svn_backup
+    recover_svn_repository
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{no-recover-dumps},
+        q{no-recover-hooks},
+        q{svn-backup-dir=s},
+        q{svn-dump-dir=s},
+        q{svn-live-dir=s},
+        q{svn-project-suffix=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    my @projects = filter_projects([get_projects_from_svn_backup()], \@ARGV);
+    for my $project (sort {$a->get_name() cmp $b->get_name()} @projects) {
+        recover_svn_repository(
+            $project,
+            !$option{q{no-recover-dumps}},
+            !$option{q{no-recover-hooks}},
+        );
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-recover-svn-repos
+
+=head1 SYNOPSIS
+
+    fcm-recover-svn-repos [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --no-recover-dumps
+
+If this option is specified, the program will not attempt to load the revision
+dumps of each repository after it has been restored from the backup.
+
+=item --no-recover-hooks
+
+If this option is specified, the program will not attempt to reinstate the hook
+scripts for each repository after it has been restored from the backup.
+
+=item --svn-backup-dir=DIR
+
+Specifies the root location of the backup directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-dump-dir=DIR
+
+Specifies the root location of the directory where revision dumps are kept. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-live-dir=DIR
+
+Specifies the root location of the live directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --svn-project-suffix=NAME
+
+Specifies the suffix added to the project name. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies one or more project to recover. If no project is specified, the
+program searches the backup directory for projects to recover.
+
+=back
+
+=head1 DESCRIPTION
+
+This program archives Subversion repositories in the live directory to the
+backup directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-recover-trac-env
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-recover-trac-env	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-recover-trac-env	(revision 5095)
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    filter_projects
+    get_projects_from_trac_backup
+    recover_trac_environment
+    recover_trac_files
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{trac-backup-dir=s},
+        q{trac-live-dir=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    my @projects = filter_projects([get_projects_from_trac_backup()], \@ARGV);
+    for my $project (@projects) {
+        recover_trac_environment($project);
+    }
+    if (!@ARGV) {
+        recover_trac_files();
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-recover-trac-env
+
+=head1 SYNOPSIS
+
+    fcm-recover-trac-env [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --trac-backup-dir=DIR
+
+Specifies the root location of the backup directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=item --trac-live-dir=DIR
+
+Specifies the root location of the live directory. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies one or more project to recover. If no project is specified, the
+program searches the backup directory for projects and files to recover.
+
+=back
+
+=head1 DESCRIPTION
+
+This program recovers Trac environments and files from the backup directory to
+the live directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-rpmbuild
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-rpmbuild	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-rpmbuild	(revision 5095)
@@ -0,0 +1,112 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# NAME
+#     fcm-rpmbuild
+#
+# SYNOPSIS
+#     fcm-rpmbuild [--gui] [REV]
+#     E.g.:
+#     fcm rpmbuild 2014-03
+#
+# DESCRIPTION
+#     Build an RPM for distributing FCM.
+#     Assume that the current working directory is a local Git clone containing
+#     the FCM project.
+#
+# OPTIONS
+#     --gui - add GUI packages such as "perl-Tk" and "xxdiff".
+#
+# ARGUMENTS
+#     REV - Revision to build. Default to HEAD.
+#-------------------------------------------------------------------------------
+
+set -eu
+THIS=$(basename $0)
+NAME='fcm'
+
+rpmdev-setuptree
+
+# Build RPM without no dependency on subversion or xxdiff?
+REQUIRES_GUI=
+if (($# > 0)) && [[ $1 == '--gui' ]]; then
+    REQUIRES_GUI='perl-Tk xxdiff'
+    shift 1
+fi
+
+# Create the source tree
+REV=${1:-HEAD}
+REV_NAME=$(git describe $REV)
+REV_BASE=$(git describe --abbrev=0 $REV)
+RELEASE=1
+if [[ $REV_NAME != $REV_BASE ]]; then
+    COMMIT_DATE=$(date -u +%Y%m%dT%H%M "--date=$(git show -s --format=%ci $REV)")
+    RELEASE=${COMMIT_DATE}git${REV_NAME#$REV_BASE-*-g}
+fi
+REV_BASE_DOT=$(sed 's/-/./g' <<<$REV_BASE)
+git archive --format=tar --prefix=$NAME-$REV_BASE_DOT/ $REV \
+    | (cd ~/rpmbuild/SOURCES/ && tar -xf -)
+SOURCE=~/rpmbuild/SOURCES/$NAME-$REV_BASE_DOT
+echo "FCM.VERSION=\"$REV_NAME\";" >$SOURCE/doc/etc/$NAME-version.js
+rm -r $SOURCE/{test,t}
+if [[ -z $REQUIRES_GUI ]]; then
+    rm $SOURCE/{bin/fcm_gui,lib/FCM1/Interactive/InputGetter/GUI.pm}
+fi
+
+# Create the rpmbuild spec file
+{
+    cat <<__SPEC__
+Name: $NAME
+Version: $REV_BASE_DOT
+Release: $RELEASE
+Summary: A modern Fortran build system + wrappers to SVN
+Group: Development/Tools
+License: GPLv3
+URL: https://github.com/metomi/$NAME/
+Source0: https://github.com/metomi/$NAME/releases/
+BuildArch: noarch
+Requires: diffutils filesystem gzip make perl-core perl-Config-IniFiles perl-MailTools perl-XML-Parser subversion subversion-perl $REQUIRES_GUI
+
+%description
+FCM: A modern Fortran build system + wrappers to Subversion for scientific
+software development
+
+%prep
+
+%build
+
+%install
+rm -fr %{buildroot}
+mkdir -p %{buildroot}/opt/ %{buildroot}/usr/bin
+cp -pr %_sourcedir/$NAME-$REV_BASE_DOT %{buildroot}/opt/$NAME
+cp -p %_sourcedir/$NAME-$REV_BASE_DOT/usr/bin/fcm %{buildroot}/usr/bin
+
+%clean 
+rm -fr %{buildroot}
+
+%files
+/opt/$NAME
+/usr/bin/fcm
+__SPEC__
+} >~/rpmbuild/SPECS/$NAME.spec
+
+cd ~/rpmbuild/SPECS
+rpmbuild -ba $NAME.spec
+rm -fr ~/rpmbuild/SOURCES/$NAME-$REV_BASE_DOT
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-user-to-email
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-user-to-email	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-user-to-email	(revision 5095)
@@ -0,0 +1,66 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{get_users};
+use FCM::Util;
+
+my $UTIL = FCM::Util->new();
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+    if (!@ARGV) {
+        return;
+    }
+    my @names = @ARGV;
+    local($FCM::Admin::System::UTIL) = $UTIL;
+    my @emails
+        = sort grep {$_} map {$_->get_email()} values(%{get_users(@names)});
+    print(join(q{,}, @emails) . "\n");
+}
+
+1;
+__END__
+
+=head1 NAME
+
+fcm-users-to-emails
+
+=head1 SYNOPSIS
+
+    fcm-user-to-email USER ...
+
+=head1 DESCRIPTION
+
+Print email addresses (comma separated) of users in argument list.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-vacuum-trac-env-db
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-vacuum-trac-env-db	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/fcm-vacuum-trac-env-db	(revision 5095)
@@ -0,0 +1,101 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    filter_projects
+    get_projects_from_trac_live
+    vacuum_trac_env_db
+};
+use FCM::Admin::Util qw{option2config};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option;
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{trac-live-dir=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    option2config(\%option);
+    my @projects = filter_projects([get_projects_from_trac_live()], \@ARGV);
+    for my $project (@projects) {
+        vacuum_trac_env_db($project);
+    }
+}
+
+__END__
+=head1 NAME
+
+fcm-vacuum-trac-env-db
+
+=head1 SYNOPSIS
+
+    fcm-vacuum-trac-env-db [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --trac-live-dir=DIR
+
+Specifies the root location of the live directory of Trac environments. See
+L<FCM::Admin::Config|FCM::Admin::Config> for the current default.
+
+=back
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item PROJECT
+
+Specifies one or more project to vacuum. If no project is specified, the
+program searches the live directory for projects to vacuum.
+
+=back
+
+=head1 DESCRIPTION
+
+This program issues the "VACUUM" SQL command in the databases of the Trac
+environments in the live directory.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/my-regular-update.example
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/my-regular-update.example	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/my-regular-update.example	(revision 5095)
@@ -0,0 +1,43 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+set -eu
+LOCK=/path/to/lock
+WC_OF_PROJECT=/path/to/working/copy/of/project
+
+if [[ -e $LOCK ]]; then
+    exit
+fi
+
+mkdir -p $LOCK
+echo "$(whoami)@$(hostname):$$" >$LOCK/info # info on who created the lock
+
+while true; do
+    UPDATED=$(cd $WC_OF_PROJECT && svn update | sed '$d' | cut -c6-)
+    if [[ -z $UPDATED ]]; then
+        break
+    fi
+    if [[ -n $(echo "$UPDATED" | grep '^foo/bar/[^/]*\.baz$') ]]; then
+        : # Performs some update if foo/bar/*.baz has changed
+    fi
+done
+
+rm $LOCK/info
+rmdir $LOCK
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-commit-bg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-commit-bg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-commit-bg	(revision 5095)
@@ -0,0 +1,192 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# NAME
+#   post-commit-bg
+#
+# SYNOPSIS
+#   post-commit-bg REPOS REV
+#
+# ARGUMENTS
+#   REPOS - the path to the Subversion repository
+#   REV - the revision of the commit
+#   TXN - the commit transaction that becomes the revision
+#
+# DESCRIPTION
+#   This script performs the post-commit tasks of a Subversion repository in
+#   the background.
+#
+#   The script does the following:
+#   1. Creates an incremental revision dump of the current revision.
+#   2. Update corresponding Trac environment, if relevant.
+#   3. Size check. Warn if transaction exceeds 2MB, or the number of
+#      MB specified in "$REPOS/hooks/post-commit-size-threshold.conf".
+#   4. If this changeset has a change to "^/svnperms.conf", install its HEAD
+#      revision at "$REPOS/hooks/", or remove it from "$REPOS/hooks/" if it is
+#      removed from the HEAD.
+#   5. Runs "$REPOS/hooks/post-commit-bg-custom" and/or
+#      "$REPOS/hooks/post-commit-background-custom", if available.
+#   6. E-mails the host user account on error.
+#
+# ENVIRONMENT VARIABLES
+#   FCM_SITE_HOME
+#     The root location of site configuration.
+#   FCM_SVN_HOOK_COMMIT_DUMP_DIR
+#     The path to dump commit deltas. Generate a commit delta if specified.
+#   FCM_SVN_HOOK_TRAC_ROOT_DIR
+#     The root directories of Trac environments. Update corresponding Trac
+#     environment if specified.
+#   FCM_SVN_HOOK_REPOS_SUFFIX
+#     A suffix that should be removed from the basename of REPOS to get the
+#     name of the Trac environment. (Default is "".)
+#
+# FILES
+#   $REPOS/hooks/post-commit-bg-custom
+#   $REPOS/hooks/post-commit-background-custom
+#-------------------------------------------------------------------------------
+set -eu
+. "$(dirname $0)/trac_hook"
+
+REPOS=$1
+REV=$2
+TXN=$3
+
+export PATH=${PATH:-'/usr/local/bin:/bin:/usr/bin'}:$(dirname $0)
+THIS=$(basename $0)
+USER=${USER:-$(whoami)}
+LOG_REV="$REPOS/log/$THIS-$REV.log"
+
+main() {
+    local RET_CODE=0
+    local NOW=$(date -u +%FT%H:%M:%SZ)
+    local AUTHOR=$(svnlook author -r "$REV" "$REPOS")
+    echo "$NOW+ $REV by $AUTHOR"
+
+    # Dump revision delta
+    if [[ -n ${FCM_SVN_HOOK_COMMIT_DUMP_DIR:-} ]]; then
+        if [[ ! -d "$FCM_SVN_HOOK_COMMIT_DUMP_DIR" ]]; then
+            mkdir -p "$FCM_SVN_HOOK_COMMIT_DUMP_DIR" || true
+        fi
+        local NAME=$(basename "$REPOS")
+        local DUMP="$FCM_SVN_HOOK_COMMIT_DUMP_DIR/$NAME-$REV.gz"
+        local TMP_DUMP="$FCM_SVN_HOOK_COMMIT_DUMP_DIR/$NAME-$REV-tmp.gz"
+        echo "svnadmin dump -r$REV --incremental --deltas $REPOS | gzip \\"
+        echo "    | (dd 'conv=fsync' \"of=$TMP_DUMP\" 2>/dev/null)"
+        svnadmin dump "-r$REV" --incremental --deltas "$REPOS" | gzip \
+            | (dd 'conv=fsync' "of=$TMP_DUMP" 2>/dev/null) || RET_CODE=$?
+        if [[ -s "${TMP_DUMP}" ]]; then
+            echo "mv \"${TMP_DUMP}\" \"${DUMP}\""
+            mv "${TMP_DUMP}" "${DUMP}" || RET_CODE=$?
+        else
+            echo "[WARN] ${NAME}-${REV}: zero dump size" >&2
+            rm -f "${TMP_DUMP}"
+        fi
+    fi
+
+    # Resync Trac
+    trac_hook "$REPOS" "$REV" added || RET_CODE=$?
+
+    # Check size - send warning email if threshold exceeded
+    local MB=1048576
+    local THRESHOLD=2
+    local SIZE_THRESHOLD_FILE="${REPOS}/hooks/post-commit-size-threshold.conf"
+    if [[ -f "${SIZE_THRESHOLD_FILE}" && -r "${SIZE_THRESHOLD_FILE}" ]]; then
+        THRESHOLD=$(<"${SIZE_THRESHOLD_FILE}")
+    fi
+    local REV_FILE="${REPOS}/db/revs/$((${REV} / 1000))/${REV}"
+    local REV_FILE_SIZE=$(du -b -s "${REV_FILE}" | cut -f 1)
+    if ((${REV_FILE_SIZE} > ${THRESHOLD} * ${MB})); then
+        echo "REV_FILE_SIZE=${REV_FILE_SIZE} # >${THRESHOLD}MB"
+        RET_CODE=1
+    else
+        echo "REV_FILE_SIZE=${REV_FILE_SIZE} # <${THRESHOLD}MB"
+    fi
+
+    # Install commit.conf and svnperms.conf, if necessary
+    local NAME=$(basename "${REPOS}")
+    if [[ -n "${FCM_SVN_HOOK_REPOS_SUFFIX:-}" ]]; then
+        NAME="${NAME%${FCM_SVN_HOOK_REPOS_SUFFIX}}"
+    fi
+    local CHANGED=$(svnlook changed -r "${REV}" "${REPOS}")
+    local FILE=
+    for FILE in 'commit.conf' 'svnperms.conf'; do
+        # Ignore if there is a site override
+        if [[ -n "${FCM_SITE_HOME:-}" \
+            && -e "${FCM_SITE_HOME:-}/svn-hooks/${NAME}/${FILE}" ]]
+        then
+            continue
+        fi
+        if grep -q "^....${FILE}\$" <<<"${CHANGED}"; then
+            # Don't specify revision, so always look at latest.
+            if svnlook filesize "${REPOS}" "${FILE}" >/dev/null 2>&1; then
+                echo "svnlook cat ${REPOS} ${FILE} >${REPOS}/hooks/${FILE}"
+                svnlook cat "${REPOS}" "${FILE}" >"${REPOS}/hooks/${FILE}"
+            else
+                echo "rm -f ${REPOS}/hooks/${FILE}"
+                rm -f "${REPOS}/hooks/${FILE}"
+            fi
+        fi
+    done
+
+    # If relevant, notify owners
+    local COMMIT_CONFIG="${REPOS}/hooks/commit.conf"
+    if grep -q 'notify-owner' "$COMMIT_CONFIG" 2>/dev/null; then
+        local ADDRS=$('post-commit-bg-notify-who' "$REPOS" "$REV" "$TXN")
+        if [[ -n $ADDRS ]]; then
+            SUBJECT="-s$(basename $REPOS)@$REV by $AUTHOR"
+            FROM=
+            if [[ -n ${FCM_SVN_HOOK_NOTIFICATION_FROM:-} ]]; then
+                FROM="-r${FCM_SVN_HOOK_NOTIFICATION_FROM:-}"
+            fi
+            echo -n "svn log -v -r \"$REV\" \"file://$REPOS\""
+            echo " | mail \"$FROM\" \"$SUBJECT\" \"$ADDRS\""
+            svn log -v -r "$REV" "file://$REPOS" \
+                | mail "$FROM" "$SUBJECT" "$ADDRS"
+        fi
+    fi
+
+    # Custom hook
+    local CUSTOM_HOOK=
+    for CUSTOM_HOOK in \
+        "$REPOS/hooks/$THIS-custom" \
+        "$REPOS/hooks/post-commit-background-custom"
+    do
+        if [[ -x "$CUSTOM_HOOK" ]]; then
+            echo "$CUSTOM_HOOK $REPOS $REV $TXN"
+            "$CUSTOM_HOOK" "$REPOS" "$REV" "$TXN" || RET_CODE=$?
+        fi
+    done
+
+    echo "RET_CODE=$RET_CODE"
+    return $RET_CODE
+}
+
+if ! main 1>$LOG_REV 2>&1; then
+    if [[ -n ${FCM_SVN_HOOK_ADMIN_EMAIL:-} ]]; then
+        FROM=
+        if [[ -n ${FCM_SVN_HOOK_NOTIFICATION_FROM:-} ]]; then
+            FROM="-r${FCM_SVN_HOOK_NOTIFICATION_FROM:-}"
+        fi
+        mail "$FROM" -s "[$THIS] $REPOS@$REV" \
+            "$FCM_SVN_HOOK_ADMIN_EMAIL" <"$LOG_REV" || true
+    fi
+fi
+cat "$LOG_REV"
+rm -f "$LOG_REV"
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-commit-bg-notify-who
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-commit-bg-notify-who	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-commit-bg-notify-who	(revision 5095)
@@ -0,0 +1,153 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{get_users};
+use FCM::System::CM::SVN;
+use FCM::Util;
+use Text::ParseWords qw{shellwords};
+
+our @IGNORES = qw{Config Rel Share};
+
+my $UTIL = FCM::Util->new();
+my $CM_SYS = FCM::System::CM::SVN->new({'util' => $UTIL});
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+    my ($repos, $rev, $txn) = @ARGV;
+
+    my %layout_config = $CM_SYS->load_layout_config('file://' . $repos);
+    if (!$layout_config{'level-owner-branch'} && !$layout_config{'owner'}) {
+        return;
+    }
+
+    my @owners;
+    if (open(my $handle, "$repos/hooks/commit.conf")) {
+        COMMIT_CONF_LINE:
+        while (my $line = readline($handle)) {
+            chomp($line);
+            my ($owners_str) = $line =~ qr{\A\s*owner\s*=\s*(.*)\z}msx;
+            if ($owners_str) {
+                @owners = shellwords($owners_str);
+                last COMMIT_CONF_LINE;
+            }
+        }
+        close($handle);
+    }
+
+    my ($author) = $CM_SYS->stdout(qw{svnlook author -r}, $rev, $repos);
+
+    # Get list of new paths
+    my %branches = ();  # {$project/$branch1 => 1, $project/$branch2 => 1, ...}
+    my %names = (); # {$name1 => 1, $name2 => 1, ...}
+    my @lines = $CM_SYS->stdout(qw{svnlook changed -r}, $rev, $repos);
+    CHANGED_LINE:
+    for my $line (@lines) {
+        my $status = substr($line, 0, 1);
+        my $path = substr($line, 4);
+        my $layout = $CM_SYS->get_layout_common(
+            $repos,
+            ($status eq 'D' ? $rev - 1 : $rev),
+            '/' . $path, # must start with a '/'
+            1, # $is_local=1
+        );
+        my $project = $layout->get_project();
+        my $branch = $layout->get_branch();
+        if (!$branch || exists($branches{"$project/$branch"})) {
+            next CHANGED_LINE;
+        }
+        $branches{"$project/$branch"} = 1; # so we only do each branch once
+        if (@owners && $layout->is_trunk()) {
+            for my $owner (grep {$_ ne $author} @owners) {
+                $names{$owner} = 1;
+            }
+        }
+        elsif ($layout->is_branch()) {
+            my $owner = $layout->get_branch_owner();
+            if (!$owner || $layout->is_shared()) {
+                # If owner is not in the branch name,
+                # assume owner is branch creator
+                my $url = 'file://' . $layout->get_root();
+                if ($project) {
+                    $url .= '/' . $project;
+                }
+                $url .= '/' . $branch . '@' . $rev;
+                my @lines = $CM_SYS->stdout(
+                    qw{svn log -q --incremental --stop-on-copy --limit 1},
+                    '-r1:' . $rev,
+                    $url,
+                );
+                LOG_LINE:
+                for my $line (reverse(@lines)) {
+                    ($owner) = $line =~ qr{\Ar\d+\s\|\s([^\|]+)\s\|}msx;
+                    if ($owner) {
+                        last LOG_LINE;
+                    }
+                }
+            }
+            if ($owner && $owner ne $author) {
+                $names{$owner} = 1;
+            }
+        }
+    }
+
+    # Get emails, if necessary
+    if (%names) {
+        local($FCM::Admin::System::UTIL) = $UTIL;
+        my @names = ($author, keys(%names));
+        my @emails
+            = sort grep {$_} map {$_->get_email()} values(%{get_users(@names)});
+        print(join(q{,}, @emails) . "\n");
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+post-commit-bg-notify-who
+
+=head1 SYNOPSIS
+
+    post-commit-bg-notify-who $REPOS $REV $TXN
+
+=head1 ARGUMENTS
+
+Accept the same arguments as a Subversion post-commit hook.
+
+=head1 DESCRIPTION
+
+This program prints email addresses who should be notified of the change. E.g.
+If this commit is performed by an author on someone else's branch.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-revprop-change-bg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-revprop-change-bg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/post-revprop-change-bg	(revision 5095)
@@ -0,0 +1,115 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# NAME
+#   post-revprop-change-bg
+#
+# SYNOPSIS
+#   post-revprop-change-bg REPOS REV PROP_AUTHOR PROP_NAME ACTION
+#
+# ARGUMENTS
+#   REPOS - the path to the Subversion repository
+#   REV - the revision relevant for the property
+#   PROP_AUTHOR - the author of this property change
+#   PROP_NAME - the name of the property, should only be "svn:log"
+#   ACTION - the action of the property change, should only be "M"
+#
+# DESCRIPTION
+#   This script performs the post-revprop-change tasks of a Subversion
+#   repository in the background.
+#
+#   The script does the following:
+#   1. Write diff between old and new property.
+#   2. Update corresponding Trac environment, if relevant.
+#   3. E-mails the host user account on error.
+#   4. E-mails the changeset author if property author is not changeset author.
+#
+# ENVIRONMENT VARIABLES
+#   FCM_SVN_HOOK_TRAC_ROOT_DIR
+#     The root directories of Trac environments. Update corresponding Trac
+#     environment if specified.
+#   FCM_SVN_HOOK_REPOS_SUFFIX
+#     A suffix that should be removed from the basename of REPOS to get the
+#     name of the Trac environment. (Default is "".)
+#-------------------------------------------------------------------------------
+set -eu
+. "$(dirname $0)/trac_hook"
+
+REPOS=$1
+REV=$2
+PROP_AUTHOR=$3
+PROP_NAME=$4
+ACTION=$5
+
+export PATH=${PATH:-'/usr/local/bin:/bin:/usr/bin'}:$(dirname $0)
+THIS=$(basename $0)
+USER=${USER:-$(whoami)}
+LOG_TMP=$(mktemp "$REPOS/log/$THIS.log.XXXXXXXXXX")
+NAME=$(basename "$REPOS")
+
+main() {
+    local RET_CODE=0
+    local NOW=$(date -u +%FT%H:%M:%SZ)
+    echo "$NOW+ $ACTION $PROP_NAME @$REV by $PROP_AUTHOR"
+
+    # Diff old/new in log
+    local OLD_FILE=$(mktemp "$REPOS/log/$THIS.$REV.XXXXXXXXXX.old")
+    cat >"$OLD_FILE"
+    local DIFF_FILE=$(mktemp "$REPOS/log/$THIS.$REV.XXXXXXXXXX.diff")
+    {
+        echo "$NOW+ $ACTION $PROP_NAME @$REV by $PROP_AUTHOR"
+        printf '=%.0s' {1..72}
+        echo
+    } >"$DIFF_FILE"
+    svnlook pg -r "$REV" --revprop "$REPOS" "$PROP_NAME" \
+        | diff -u --label="old-value" --label="new-value" "$OLD_FILE" - \
+        | tee -a "$DIFF_FILE"
+
+    # Email to changeset author if not the same as property change author
+    REV_AUTHOR=$(svnlook author -r "$REV" "$REPOS")
+    if [[ "$REV_AUTHOR" != "$PROP_AUTHOR" ]]; then
+        SUBJECT="-s$(basename $REPOS)@$REV [$ACTION $PROP_NAME] by $PROP_AUTHOR"
+        FROM=
+        if [[ -n ${FCM_SVN_HOOK_NOTIFICATION_FROM:-} ]]; then
+            FROM="-r${FCM_SVN_HOOK_NOTIFICATION_FROM:-}"
+        fi
+        ADDRS=$(fcm-user-to-email "$REV_AUTHOR" "$PROP_AUTHOR" 2>'/dev/null')
+        mail "$FROM" "$SUBJECT" "$ADDRS" <"$DIFF_FILE" || true
+    fi
+    rm -f "$OLD_FILE" "$DIFF_FILE"
+
+    # Resync Trac
+    trac_hook "$REPOS" "$REV" modified || RET_CODE=$?
+
+    echo "RET_CODE=$RET_CODE"
+    return $RET_CODE
+}
+
+if ! main 1>$LOG_TMP 2>&1 && [[ -n ${FCM_SVN_HOOK_ADMIN_EMAIL:-} ]]; then
+    FROM=
+    if [[ -n ${FCM_SVN_HOOK_NOTIFICATION_FROM:-} ]]; then
+        FROM="-r${FCM_SVN_HOOK_NOTIFICATION_FROM:-}"
+    fi
+    mail "$FROM" -s "[ERROR $THIS] $NAME" \
+        "$FCM_SVN_HOOK_ADMIN_EMAIL" <"$LOG_TMP" || true
+fi
+
+cat "$LOG_TMP"
+rm -f "$LOG_TMP"
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-commit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-commit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-commit	(revision 5095)
@@ -0,0 +1,125 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# NAME
+#   pre-commit
+#
+# SYNOPSIS
+#   pre-commit REPOS TXN
+#
+# ARGUMENTS
+#   REPOS - the path to the Subversion repository
+#   TXN - the commit transaction
+#
+# DESCRIPTION
+#   This script performs pre-commit check, including:
+#   1. Path-based permission check using "svnperms.py", if
+#      "$REPOS/hooks/svnperms.conf" exists.
+#   2. Size check. Transaction should occupy less than 10MB, or the number of
+#      MB specified in "$REPOS/hooks/pre-commit-size-threshold.conf".
+#   3. Runs "$REPOS/hooks/pre-commit-custom" if it exists.
+#
+# ENVIRONMENT VARIABLES
+#   FCM_SVN_HOOK_ADMIN_EMAIL
+#     The name of the admin team. (Default is "Admin".)
+#-------------------------------------------------------------------------------
+set -eu
+
+REPOS=$1
+TXN=$2
+
+export PATH=${PATH:-'/usr/local/bin:/bin:/usr/bin'}:$(dirname $0)
+THIS=$(basename $0)
+USER=${USER:-$(whoami)}
+NAME=$(basename "$REPOS")
+LOG_TXN="$REPOS/log/$THIS-$TXN.log"
+
+begin() {
+    local NOW=$(date -u +%FT%H:%M:%SZ)
+    local AUTHOR=$(svnlook author -t "$TXN" "$REPOS")
+    echo "$NOW+ $TXN by $AUTHOR"
+    svnlook changed -t "$TXN" "$REPOS"
+}
+
+main() {
+    local ADMIN_EMAIL=${FCM_SVN_HOOK_ADMIN_EMAIL:-Admin}
+
+    # Check size.
+    local MB=1048576
+    local THRESHOLD=10
+    local SIZE_THRESHOLD_FILE="$REPOS/hooks/$THIS-size-threshold.conf"
+    if [[ -f "$SIZE_THRESHOLD_FILE" && -r "$SIZE_THRESHOLD_FILE" ]]; then
+        THRESHOLD=$(<"$SIZE_THRESHOLD_FILE")
+    fi
+    local TXN_FILE="$REPOS/db/txn-protorevs/$TXN.rev"
+    local SIZE=$(du -b "$TXN_FILE" | cut -f 1)
+    if ((SIZE > THRESHOLD * MB)); then
+        SIZE=$(du -h "$TXN_FILE" | cut -f 1)
+        echo "$NAME@$TXN: changeset size ${SIZE}B exceeds ${THRESHOLD}MB." >&2
+        echo "Email $ADMIN_EMAIL if you need to bypass this restriction." >&2
+        return 1
+    elif ((SIZE > MB)); then
+        # Log any changesets bigger than 1MB
+        SIZE=$(du -h "$TXN_FILE" | cut -f 1)
+        echo "$NAME@$TXN: changeset size ${SIZE}B exceeds 1MB." >&2
+    fi
+
+    # Check permission.
+    local PERM_CONFIG="$REPOS/hooks/svnperms.conf"
+    if [[ -r "$PERM_CONFIG" && -s "$PERM_CONFIG" ]]; then
+        svnperms.py -r "$REPOS" -t "$TXN" -f "$PERM_CONFIG" || return $?
+    elif [[ -L "$PERM_CONFIG" ]]; then
+        echo "$NAME: permission configuration file not found." >&2
+        echo "$ADMIN_EMAIL has been notified." >&2
+        return 1
+    fi
+
+    # Verify owner of any new branches, if relevant
+    local COMMIT_CONFIG="${REPOS}/hooks/commit.conf"
+    if grep -q 'verify-branch-owner' "$COMMIT_CONFIG" 2>/dev/null; then
+        pre-commit-verify-branch-owner "$REPOS" "$TXN" || return $?
+    fi
+
+    # Custom checking, if required
+    local CUSTOM_HOOK="$REPOS/hooks/$THIS-custom"
+    if [[ -x "$CUSTOM_HOOK" ]]; then
+        "$CUSTOM_HOOK" "$REPOS" "$TXN" || return $?
+    fi
+}
+
+RET_CODE=0
+begin 1>"${LOG_TXN}" 2>&1
+LOG_TXN_SIZE="$(stat -c '%s' "${LOG_TXN}")"
+if ! main 1>>"${LOG_TXN}" 2>&1; then
+    if [[ -n ${FCM_SVN_HOOK_ADMIN_EMAIL:-} ]]; then
+        FROM=
+        if [[ -n ${FCM_SVN_HOOK_NOTIFICATION_FROM:-} ]]; then
+            FROM="-r${FCM_SVN_HOOK_NOTIFICATION_FROM:-}"
+        fi
+        mail "$FROM" -s "[$THIS] $REPOS@$TXN" \
+            "$FCM_SVN_HOOK_ADMIN_EMAIL" <"$LOG_TXN" || true
+    fi
+    cat "$LOG_TXN"
+    cat "$LOG_TXN" >&2
+    RET_CODE=1
+elif [[ "${LOG_TXN_SIZE}" != "$(stat -c '%s' "${LOG_TXN}")" ]]; then
+    cat "$LOG_TXN"
+fi
+rm -f "$LOG_TXN"
+exit $RET_CODE
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-commit-verify-branch-owner
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-commit-verify-branch-owner	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-commit-verify-branch-owner	(revision 5095)
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{verify_users};
+use FCM::System::CM::SVN;
+use FCM::Util;
+
+our @IGNORES = qw{Config Rel Share};
+
+my $UTIL = $FCM::Admin::System::UTIL;
+my $CM_SYS = FCM::System::CM::SVN->new({'util' => $UTIL});
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+    my ($repos, $txn) = @ARGV;
+
+    my %layout_config = $CM_SYS->load_layout_config('file://' . $repos);
+    if (!$layout_config{'level-owner-branch'}) {
+        return;
+    }
+
+    # Get list of new paths
+    my %lines_of; # $lines_of{$owner} = [$path, ...]
+    LINE:
+    for my $line ($CM_SYS->stdout(qw{svnlook changed -t}, $txn, $repos)) {
+        my $status = substr($line, 0, 1);
+        if ($status eq 'A') {
+            my $path = substr($line, 4);
+            # $is_local=1
+            my $layout = $CM_SYS->get_layout_common($repos, $txn, $path, 1);
+            my $owner = $layout->get_branch_owner();
+            if ($owner && !grep {$_ eq $owner} @IGNORES) {
+                if (!exists($lines_of{$owner})) {
+                    $lines_of{$owner} = [];
+                }
+                push(@{$lines_of{$owner}}, $line);
+            }
+        }
+    }
+
+    # Verify branch owners, if necessary
+    my @bad_users = verify_users(keys(%lines_of));
+    for my $bad_user (@bad_users) {
+        for my $line (@{$lines_of{$bad_user}}) {
+            warn("[INVALID BRANCH OWNER] $line\n");
+        }
+    }
+    exit(scalar(@bad_users));
+}
+
+1;
+__END__
+
+=head1 NAME
+
+pre-commit-verify-branch-owner
+
+=head1 SYNOPSIS
+
+    pre-commit-verify-branch-owner REPOS TXN
+
+=head1 ARGUMENTS
+
+Accept the same arguments as a Subversion pre-commit hook.
+
+=head1 DESCRIPTION
+
+This program ensures that users create a branch with its owner correctly set.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+
+=cut
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-revprop-change
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-revprop-change	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/pre-revprop-change	(revision 5095)
@@ -0,0 +1,88 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# NAME
+#   pre-revprop-change
+#
+# SYNOPSIS
+#   pre-revprop-change REPOS REV PROP_AUTHOR PROP_NAME ACTION
+#
+# ARGUMENTS
+#   REPOS - the path to the Subversion repository
+#   REV - the revision relevant for the property
+#   PROP_AUTHOR - the author of this property change
+#   PROP_NAME - the name of the property, should only be "svn:log"
+#   ACTION - the action of the property change, should only be "M"
+#
+# DESCRIPTION
+#   This script enables users to change revision properties.
+#
+#   By default, only "M svn:log" is allowed. If
+#   "$REPOS/hooks/pre-revprop-change-ok.conf" exists, the contents should be a
+#   list of allowed changes to revision properties. E.g.:
+#
+#   M svn:author
+#   M svn:log
+#
+#   An empty file disables all changes.
+#
+#   It e-mails the host user account whenever an action is blocked.
+#-------------------------------------------------------------------------------
+set -eu
+
+REPOS=$1
+REV=$2
+USER=$3
+PROPNAME=$4
+ACTION=$5
+
+export PATH=${PATH:-'/usr/local/bin:/bin:/usr/bin'}:$(dirname $0)
+THIS=$(basename "$0")
+USER=${USER:-(whoami)}
+NAME=$(basename "$REPOS")
+
+OK_CHANGES=$(echo 'M svn:log')
+OK_CHANGES_FILE="$REPOS/hooks/$THIS-ok.conf"
+if [[ -f $OK_CHANGES_FILE ]]; then
+    OK_CHANGES=$(<$OK_CHANGES_FILE)
+fi
+
+NOW=$(date -u +%FT%H:%M:%SZ)
+if ! grep -q "$ACTION  *$PROPNAME" <<<"$OK_CHANGES"; then
+    if [[ -n "$OK_CHANGES" ]]; then
+        echo -n "[$ACTION $PROPNAME] permission denied. Can only do:" >&2
+        while read; do
+            echo -n " [$REPLY]" >&2
+        done <<<"$OK_CHANGES"
+        echo >&2
+    else
+        echo "[$ACTION $PROPNAME] permission denied." >&2
+    fi
+    if [[ -n ${FCM_SVN_HOOK_ADMIN_EMAIL:-} ]]; then
+        FROM=
+        if [[ -n ${FCM_SVN_HOOK_NOTIFICATION_FROM:-} ]]; then
+            FROM="-r${FCM_SVN_HOOK_NOTIFICATION_FROM:-}"
+        fi
+        mail "$FROM" -s "$NAME:$THIS" \
+            "$FCM_SVN_HOOK_ADMIN_EMAIL" <<<"[! $NOW] $@" || true
+    fi
+    echo "[! $NOW] $@"
+    exit 1
+fi
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/svnperms.py
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/svnperms.py	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/svnperms.py	(revision 5095)
@@ -0,0 +1,369 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+
+# THIS DISTRIBUTION HAS BEEN MODIFIED.
+# Original source downloaded from r1295006 at:
+# https://svn.apache.org/viewvc/subversion/trunk/tools/hook-scripts/svnperms.py
+# This version is modified to allow custom permission message per repository.
+# It also fixes the Config.get method.
+
+import sys, os
+import getopt
+import shlex
+
+try:
+  # Python >=3.0
+  from subprocess import getstatusoutput as subprocess_getstatusoutput
+except ImportError:
+  # Python <3.0
+  from commands import getstatusoutput as subprocess_getstatusoutput
+try:
+    my_getopt = getopt.gnu_getopt
+except AttributeError:
+    my_getopt = getopt.getopt
+import re
+
+__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
+
+class Error(Exception): pass
+
+SECTION = re.compile(r'\[([^]]+?)(?:\s+extends\s+([^]]+))?\]')
+OPTION = re.compile(r'(\S+)\s*=\s*(.*)$')
+
+class Config:
+    def __init__(self, filename):
+        # Options are stored in __sections_list like this:
+        # [(sectname, [(optname, optval), ...]), ...]
+        self._sections_list = []
+        self._sections_dict = {}
+        self._read(filename)
+
+    def _read(self, filename):
+        # Use the same logic as in ConfigParser.__read()
+        file = open(filename)
+        cursectdict = None
+        optname = None
+        lineno = 0
+        for line in file:
+            lineno = lineno + 1
+            if line.isspace() or line[0] == '#':
+                continue
+            if line[0].isspace() and cursectdict is not None and optname:
+                value = line.strip()
+                cursectdict[optname] = "%s %s" % (cursectdict[optname], value)
+                cursectlist[-1][1] = "%s %s" % (cursectlist[-1][1], value)
+            else:
+                m = SECTION.match(line)
+                if m:
+                    sectname = m.group(1)
+                    parentsectname = m.group(2)
+                    if parentsectname is None:
+                        # No parent section defined, so start a new section
+                        cursectdict = self._sections_dict.setdefault \
+                            (sectname, {})
+                        cursectlist = []
+                    else:
+                        # Copy the parent section into the new section
+                        parentsectdict = self._sections_dict.get \
+                            (parentsectname, {})
+                        cursectdict = self._sections_dict.setdefault \
+                            (sectname, parentsectdict.copy())
+                        cursectlist = self.walk(parentsectname)
+                    self._sections_list.append((sectname, cursectlist))
+                    optname = None
+                elif cursectdict is None:
+                    raise Error("%s:%d: no section header" % \
+                                 (filename, lineno))
+                else:
+                    m = OPTION.match(line)
+                    if m:
+                        optname, optval = m.groups()
+                        optval = optval.strip()
+                        cursectdict[optname] = optval
+                        cursectlist.append([optname, optval])
+                    else:
+                        raise Error("%s:%d: parsing error" % \
+                                     (filename, lineno))
+
+    def sections(self):
+        return list(self._sections_dict.keys())
+
+    def options(self, section):
+        return list(self._sections_dict.get(section, {}).keys())
+
+    def get(self, section, option, default=None):
+        return self._sections_dict.get(section, {}).get(option, default)
+
+    def walk(self, section, option=None):
+        ret = []
+        for sectname, options in self._sections_list:
+            if sectname == section:
+                for optname, value in options:
+                    if not option or optname == option:
+                        ret.append((optname, value))
+        return ret
+
+
+class Permission:
+    def __init__(self):
+        self._group = {}
+        self._permlist = []
+
+    def parse_groups(self, groupsiter):
+        for option, value in groupsiter:
+            groupusers = []
+            for token in shlex.split(value):
+                # expand nested groups in place; no forward decls
+                if token[0] == "@":
+                    try:
+                        groupusers.extend(self._group[token[1:]])
+                    except KeyError:
+                        raise Error, "group '%s' not found" % token[1:]
+                else:
+                    groupusers.append(token)
+            self._group[option] = groupusers
+
+    def parse_perms(self, permsiter):
+        for option, value in permsiter:
+            # Paths never start with /, so remove it if provided
+            if option[0] == "/":
+                option = option[1:]
+            pattern = re.compile("^%s$" % option)
+            for entry in value.split():
+                openpar, closepar = entry.find("("), entry.find(")")
+                groupsusers = entry[:openpar].split(",")
+                perms = entry[openpar+1:closepar].split(",")
+                users = []
+                for groupuser in groupsusers:
+                    if groupuser[0] == "@":
+                        try:
+                            users.extend(self._group[groupuser[1:]])
+                        except KeyError:
+                            raise Error("group '%s' not found" % \
+                                         groupuser[1:])
+                    else:
+                        users.append(groupuser)
+                self._permlist.append((pattern, users, perms))
+
+    def get(self, user, path):
+        ret = []
+        for pattern, users, perms in self._permlist:
+            if pattern.match(path) and (user in users or "*" in users):
+                ret = perms
+        return ret
+
+class SVNLook:
+    def __init__(self, repospath, txn=None, rev=None):
+        self.repospath = repospath
+        self.txn = txn
+        self.rev = rev
+
+    def _execcmd(self, *cmd, **kwargs):
+        cmdstr = " ".join(cmd)
+        status, output = subprocess_getstatusoutput(cmdstr)
+        if status != 0:
+            sys.stderr.write(cmdstr)
+            sys.stderr.write("\n")
+            sys.stderr.write(output)
+            raise Error("command failed: %s\n%s" % (cmdstr, output))
+        return status, output
+
+    def _execsvnlook(self, cmd, *args, **kwargs):
+        execcmd_args = ["svnlook", cmd, self.repospath]
+        self._add_txnrev(execcmd_args, kwargs)
+        execcmd_args += args
+        execcmd_kwargs = {}
+        keywords = ["show", "noerror"]
+        for key in keywords:
+            if key in kwargs:
+                execcmd_kwargs[key] = kwargs[key]
+        return self._execcmd(*execcmd_args, **execcmd_kwargs)
+
+    def _add_txnrev(self, cmd_args, received_kwargs):
+        if "txn" in received_kwargs:
+            txn = received_kwargs.get("txn")
+            if txn is not None:
+                cmd_args += ["-t", txn]
+        elif self.txn is not None:
+            cmd_args += ["-t", self.txn]
+        if "rev" in received_kwargs:
+            rev = received_kwargs.get("rev")
+            if rev is not None:
+                cmd_args += ["-r", rev]
+        elif self.rev is not None:
+            cmd_args += ["-r", self.rev]
+
+    def changed(self, **kwargs):
+        status, output = self._execsvnlook("changed", **kwargs)
+        if status != 0:
+            return None
+        changes = []
+        for line in output.splitlines():
+            line = line.rstrip()
+            if not line: continue
+            entry = [None, None, None]
+            changedata, changeprop, path = None, None, None
+            if line[0] != "_":
+                changedata = line[0]
+            if line[1] != " ":
+                changeprop = line[1]
+            path = line[4:]
+            changes.append((changedata, changeprop, path))
+        return changes
+
+    def author(self, **kwargs):
+        status, output = self._execsvnlook("author", **kwargs)
+        if status != 0:
+            return None
+        return output.strip()
+
+
+def check_perms(filename, section, repos, txn=None, rev=None, author=None):
+    svnlook = SVNLook(repos, txn=txn, rev=rev)
+    if author is None:
+        author = svnlook.author()
+    changes = svnlook.changed()
+    try:
+        config = Config(filename)
+    except IOError:
+        raise Error("can't read config file "+filename)
+    if not section in config.sections():
+        raise Error("section '%s' not found in config file" % section)
+    perm = Permission()
+    perm.parse_groups(config.walk("groups"))
+    perm.parse_groups(config.walk(section+" groups"))
+    perm.parse_perms(config.walk(section))
+    permerrors = []
+    for changedata, changeprop, path in changes:
+        pathperms = perm.get(author, path)
+        if changedata == "A" and "add" not in pathperms:
+            permerrors.append("you can't add "+path)
+        elif changedata == "U" and "update" not in pathperms:
+            permerrors.append("you can't update "+path)
+        elif changedata == "D" and "remove" not in pathperms:
+            permerrors.append("you can't remove "+path)
+        elif changeprop == "U" and "update" not in pathperms:
+            permerrors.append("you can't update properties of "+path)
+        #else:
+        #    print "cdata=%s cprop=%s path=%s perms=%s" % \
+        #          (str(changedata), str(changeprop), path, str(pathperms))
+    if permerrors:
+        message = config.get(" ".join((section, "message")),
+                             "permerrors_prefix")
+        if message is None:
+            message = config.get("message", "permerrors_prefix")
+        if message is None:
+            message = "you don't have enough permissions for this transaction:"
+        permerrors.insert(0, message)
+        raise Error("\n".join(permerrors))
+
+
+# Command:
+
+USAGE = """\
+Usage: svnperms.py OPTIONS
+
+Options:
+    -r PATH    Use repository at PATH to check transactions
+    -t TXN     Query transaction TXN for commit information
+    -f PATH    Use PATH as configuration file (default is repository
+               path + /conf/svnperms.conf)
+    -s NAME    Use section NAME as permission section (default is
+               repository name, extracted from repository path)
+    -R REV     Query revision REV for commit information (for tests)
+    -A AUTHOR  Check commit as if AUTHOR had committed it (for tests)
+    -h         Show this message
+"""
+
+class MissingArgumentsException(Exception):
+    "Thrown when required arguments are missing."
+    pass
+
+def parse_options():
+    try:
+        opts, args = my_getopt(sys.argv[1:], "f:s:r:t:R:A:h", ["help"])
+    except getopt.GetoptError, e:
+        raise Error(e.msg)
+    class Options: pass
+    obj = Options()
+    obj.filename = None
+    obj.section = None
+    obj.repository = None
+    obj.transaction = None
+    obj.revision = None
+    obj.author = None
+    for opt, val in opts:
+        if opt == "-f":
+            obj.filename = val
+        elif opt == "-s":
+            obj.section = val
+        elif opt == "-r":
+            obj.repository = val
+        elif opt == "-t":
+            obj.transaction = val
+        elif opt == "-R":
+            obj.revision = val
+        elif opt == "-A":
+            obj.author = val
+        elif opt in ["-h", "--help"]:
+            sys.stdout.write(USAGE)
+            sys.exit(0)
+    missingopts = []
+    if not obj.repository:
+        missingopts.append("repository")
+    if not (obj.transaction or obj.revision):
+        missingopts.append("either transaction or a revision")
+    if missingopts:
+        raise MissingArgumentsException("missing required option(s): " + ", ".join(missingopts))
+    obj.repository = os.path.abspath(obj.repository)
+    if obj.filename is None:
+        obj.filename = os.path.join(obj.repository, "conf", "svnperms.conf")
+    if obj.section is None:
+        obj.section = os.path.basename(obj.repository)
+    if not (os.path.isdir(obj.repository) and
+            os.path.isdir(os.path.join(obj.repository, "db")) and
+            os.path.isdir(os.path.join(obj.repository, "hooks")) and
+            os.path.isfile(os.path.join(obj.repository, "format"))):
+        raise Error("path '%s' doesn't look like a repository" % \
+                     obj.repository)
+
+    return obj
+
+def main():
+    try:
+        opts = parse_options()
+        check_perms(opts.filename, opts.section,
+                    opts.repository, opts.transaction, opts.revision,
+                    opts.author)
+    except MissingArgumentsException, e:
+        sys.stderr.write("%s\n" % str(e))
+        sys.stderr.write(USAGE)
+        sys.exit(1)
+    except Error, e:
+        sys.stderr.write("error: %s\n" % str(e))
+        sys.exit(1)
+
+if __name__ == "__main__":
+    main()
+
+# vim:et:ts=4:sw=4
Index: LMDZ6/branches/Amaury_dev/tools/fcm/sbin/trac_hook
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/sbin/trac_hook	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/sbin/trac_hook	(revision 5095)
@@ -0,0 +1,65 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# NAME
+#     trac_hook
+#
+# SYNOPSIS
+#     . trac_hook
+#     trac_hook "$REPOS" "$REV" added|modified
+#
+# DESCRIPTION
+#   Provide a function called "trac_hook", which updates the corresponding Trac
+#   environment, for post-commit or post-revprop-change.
+#
+# ENVIRONMENT VARIABLES
+#   FCM_SVN_HOOK_TRAC_ROOT_DIR
+#     The root directories of Trac environments. Update corresponding Trac
+#     environment if specified.
+#   FCM_SVN_HOOK_REPOS_SUFFIX
+#     A suffix that should be removed from the basename of REPOS to get the
+#     name of the Trac environment. (Default is "".)
+#-------------------------------------------------------------------------------
+
+trac_hook() {
+    local REPOS=$1
+    local REV=$2
+    local TRAC_ACT=$3
+    if which trac-admin 1>/dev/null 2>&1 \
+        && [[ -n ${FCM_SVN_HOOK_TRAC_ROOT_DIR:-} ]]
+    then
+        local TRAC_NAME=$(basename "$REPOS")
+        if [[ -n ${FCM_SVN_HOOK_REPOS_SUFFIX:-} ]]; then
+            TRAC_NAME=${TRAC_NAME%$FCM_SVN_HOOK_REPOS_SUFFIX}
+        fi
+        local TRAC_DIR="$FCM_SVN_HOOK_TRAC_ROOT_DIR/$TRAC_NAME"
+        if [[ -d "$TRAC_DIR" ]]; then
+            if [[ $(trac-admin --version) == trac-admin\ 0.11* ]]; then
+                # N.B. "added" was automatic on access
+                if [[ "$TRAC_ACT" == 'modified' ]]; then
+                    echo "trac-admin $TRAC_DIR resync $REV"
+                    trac-admin "$TRAC_DIR" resync "$REV" >/dev/null
+                fi
+            else
+                echo "trac-admin $TRAC_DIR changeset $TRAC_ACT $REPOS $REV"
+                trac-admin "$TRAC_DIR" changeset "$TRAC_ACT" "$REPOS" "$REV"
+            fi
+        fi
+    fi
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/lib/python/info/poems.py
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/lib/python/info/poems.py	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/lib/python/info/poems.py	(revision 5095)
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""The Python, by Hilaire Belloc
+
+A Python I should not advise,--
+It needs a doctor for its eyes,
+And has the measles yearly.
+However, if you feel inclined
+To get one (to improve your mind,
+And not from fashion merely),
+Allow no music near its cage;
+And when it flies into a rage
+Chastise it, most severely.
+I had an aunt in Yucatan
+Who bought a Python from a man
+And kept it for a pet.
+She died, because she never knew
+These simple little rules and few;--
+The Snake is living yet.
+"""
+
+import this
+
+print "\n",  __doc__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants.f90	(revision 5095)
@@ -0,0 +1,5 @@
+MODULE Hello_Constants
+
+INCLUDE 'hello_constants_dummy.inc'
+
+END MODULE Hello_Constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants.inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants.inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants.inc	(revision 5095)
@@ -0,0 +1,1 @@
+CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants_dummy.inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants_dummy.inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/module/hello_constants_dummy.inc	(revision 5095)
@@ -0,0 +1,1 @@
+INCLUDE 'hello_constants.inc'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/pro/hello.pro
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/pro/hello.pro	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/pro/hello.pro	(revision 5095)
@@ -0,0 +1,2 @@
+PRO HELLO
+END
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/pro/plot.pro
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/pro/plot.pro	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/pro/plot.pro	(revision 5095)
@@ -0,0 +1,3 @@
+PRO PLOT
+;  Calls    : hello.pro
+END
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/program/hello.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/program/hello.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/program/hello.F90	(revision 5095)
@@ -0,0 +1,20 @@
+PROGRAM Hello
+
+USE Hello_Constants, ONLY: hello_string
+
+IMPLICIT NONE
+
+INTEGER :: integer_arg = 1234
+
+#if defined(CALL_HELLO_SUB)
+INCLUDE 'hello_sub.interface'
+#endif
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello'
+
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
+#if defined(CALL_HELLO_SUB)
+CALL Hello_Sub (integer_arg)
+#endif
+
+END PROGRAM Hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_c.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_c.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_c.c	(revision 5095)
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+void hello_c_ () {
+  printf ("%s\n", "Hello_C: Hello Earth!");
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub.F90	(revision 5095)
@@ -0,0 +1,24 @@
+#if defined(HELLO_SUB)
+SUBROUTINE Hello_Sub (integer_arg)
+
+USE Hello_Constants, ONLY: hello_string
+
+IMPLICIT NONE
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello_Sub'
+INTEGER :: integer_arg
+INTEGER :: integer_common
+COMMON /general/integer_common
+
+! DEPENDS ON: hello_c.o
+EXTERNAL Hello_C
+
+#include "hello_sub_dummy.h"
+
+WRITE (*, '(A,I0)') this // ': integer (arg): ', integer_arg
+WRITE (*, '(A,I0)') this // ': integer (common): ', integer_common
+
+CALL Hello_C ()
+
+END SUBROUTINE Hello_Sub
+#endif
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub.h
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub.h	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub.h	(revision 5095)
@@ -0,0 +1,1 @@
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub_dummy.h
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub_dummy.h	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/etc/repo_files/subroutine/hello_sub_dummy.h	(revision 5095)
@@ -0,0 +1,1 @@
+#include "hello_sub.h"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add-trac-env/00-basic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add-trac-env/00-basic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add-trac-env/00-basic.t	(revision 5095)
@@ -0,0 +1,88 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "fcm-add-trac-env".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+if ! which trac-admin 1>/dev/null 2>/dev/null; then
+    skip_all 'trac-admin not available'
+fi
+tests 31
+#-------------------------------------------------------------------------------
+set -e
+mkdir -p etc srv/{svn,trac}
+# Configuration
+export FCM_CONF_PATH="$PWD/etc"
+ADMIN_USERS='holly ivy'
+cat >'etc/admin.cfg' <<__CONF__
+svn_live_dir=$PWD/srv/svn
+trac_admin_users=$ADMIN_USERS
+trac_live_dir=$PWD/srv/trac
+__CONF__
+# Create some Subversion repositories
+for NAME in bus lorry taxi; do
+    svnadmin create "srv/svn/$NAME"
+done
+set +e
+#-------------------------------------------------------------------------------
+for NAME in bus car lorry taxi; do
+    TEST_KEY="$TEST_KEY_BASE-$NAME"
+    # Command OK
+    run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-add-trac-env" "$NAME"
+    # Trac environment directory exists
+    run_pass "$TEST_KEY-d" test -d "$PWD/srv/trac/$NAME"
+    # Admin users are set
+    trac-admin "$PWD/srv/trac/$NAME" 'permission' 'export' "$TEST_KEY-d-perms"
+    # For some reason, the "echo" for the next test is lost in the ether unless
+    # we have an "echo" here.
+    echo
+    for ADMIN_USER in $ADMIN_USERS; do
+        file_grep "$TEST_KEY-d-perms-$ADMIN_USER" \
+            "$ADMIN_USER,admin" "$TEST_KEY-d-perms"
+    done
+    file_grep "$TEST_KEY-d-perms-owner" "owner,TRAC_ADMIN" "$TEST_KEY-d-perms"
+    file_grep "$TEST_KEY-d-perms-admin" "admin,TRAC_ADMIN" "$TEST_KEY-d-perms"
+    # Subversion repository paths in place
+    if [[ -d "srv/svn/$NAME" ]]; then
+        run_pass "${TEST_KEY}-repos-list" \
+            trac-admin "${PWD}/srv/trac/${NAME}" repository list
+        file_grep "${TEST_KEY}-repos-list.out" \
+            "(default) *${PWD}/srv/svn/${NAME}"  "${TEST_KEY}-repos-list.out"
+    fi
+done
+
+TEST_KEY="$TEST_KEY_BASE-intertrac"
+file_cmp "$TEST_KEY" "srv/trac/intertrac.ini" <<'__CONF__'
+[intertrac]
+bus.title=bus
+bus.url=https://localhost/trac/bus
+bus.compat=false
+car.title=car
+car.url=https://localhost/trac/car
+car.compat=false
+lorry.title=lorry
+lorry.url=https://localhost/trac/lorry
+lorry.compat=false
+taxi.title=taxi
+taxi.url=https://localhost/trac/taxi
+taxi.compat=false
+__CONF__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add-trac-env/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add-trac-env/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add-trac-env/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add/00-simple.t	(revision 5095)
@@ -0,0 +1,142 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm add".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+if [[ $? -ne 0 ]]; then
+    exit 1
+fi
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 24
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch_wc add $REPOS_URL
+mkdir $TEST_DIR/wc/added_directory1
+svn add -q $TEST_DIR/wc/added_directory1
+touch $TEST_DIR/wc/added_directory1/added_file1
+mkdir $TEST_DIR/wc/added_directory2
+touch $TEST_DIR/wc/added_directory2/added_file2
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm add unversioned file
+TEST_KEY=$TEST_KEY_BASE-fcm-add-file
+run_pass "$TEST_KEY" fcm add added_directory1/added_file1
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<'__OUT__'
+A         added_directory1/added_file1
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm add unversioned directory
+TEST_KEY=$TEST_KEY_BASE-fcm-add-dir
+run_pass "$TEST_KEY" fcm add added_directory2
+# Add produces yet another format of status - just use 'sort'...
+sort "$TEST_DIR/$TEST_KEY.out" -o "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<'__OUT__'
+A         added_directory2
+A         added_directory2/added_file2
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+cd $TEST_DIR
+teardown
+#-------------------------------------------------------------------------------
+init_repos
+init_branch_wc add_c $REPOS_URL
+touch $TEST_DIR/wc/unversioned_file
+mkdir $TEST_DIR/wc/unversioned_directory
+touch $TEST_DIR/wc/unversioned_directory/unversioned_file_2
+mkdir $TEST_DIR/wc/versioned_directory
+svn add -q $TEST_DIR/wc/versioned_directory
+touch $TEST_DIR/wc/versioned_directory/unversioned_file_3
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm add -c unversioned file
+TEST_KEY=$TEST_KEY_BASE-fcm-add-c-file
+run_pass "$TEST_KEY" fcm add -c unversioned_file <<'__EOF__'
+y
+__EOF__
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<'__OUT__'
+?       unversioned_file
+Would you like to run "svn add unversioned_file"?
+Enter "y", "n" or "a" (or just press <return> for "n"): A         unversioned_file
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm add -c unversioned directory
+TEST_KEY=$TEST_KEY_BASE-fcm-add-c-dir
+run_pass "$TEST_KEY" fcm add -c unversioned_directory <<'__EOF__'
+y
+__EOF__
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<'__OUT__'
+?       unversioned_directory
+Would you like to run "svn add unversioned_directory"?
+Enter "y", "n" or "a" (or just press <return> for "n"): A         unversioned_directory
+A         unversioned_directory/unversioned_file_2
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm add -c versioned directory
+TEST_KEY=$TEST_KEY_BASE-fcm-add-c-versioned-dir
+run_pass "$TEST_KEY" fcm add -c versioned_directory <<'__EOF__'
+n
+__EOF__
+file_test "$TEST_KEY.out" "$TEST_KEY.out" -s
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm status after above tests
+TEST_KEY=$TEST_KEY_BASE-fcm-add-c-status
+run_pass "$TEST_KEY" fcm st
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<'__OUT__'
+?       versioned_directory/unversioned_file_3
+A       unversioned_directory
+A       unversioned_directory/unversioned_file_2
+A       unversioned_file
+A       versioned_directory
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm add -c with no arguments
+TEST_KEY=$TEST_KEY_BASE-fcm-add-c-no-args
+fcm revert -q -R $TEST_DIR/wc/
+run_pass "$TEST_KEY" fcm add -c <<'__EOF__'
+y
+y
+y
+y
+__EOF__
+file_test "$TEST_KEY.out" "$TEST_KEY.out" -s
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm status after above tests
+TEST_KEY=$TEST_KEY_BASE-fcm-add-c-no-args-status
+run_pass "$TEST_KEY" fcm status
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<'__OUT__'
+A       unversioned_directory
+A       unversioned_directory/unversioned_file_2
+A       unversioned_file
+A       versioned_directory
+A       versioned_directory/unversioned_file_3
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-add/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-backup-svn-repos/00-basic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-backup-svn-repos/00-basic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-backup-svn-repos/00-basic.t	(revision 5095)
@@ -0,0 +1,103 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "fcm-backup-svn-repos".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+if ! which svnadmin 1>/dev/null 2>/dev/null; then
+    skip_all 'svnadmin not available'
+fi
+tests 32
+#-------------------------------------------------------------------------------
+set -e
+mkdir -p etc srv/svn var/svn/{backups,cache,dumps}
+# Configuration
+export FCM_CONF_PATH="$PWD/etc"
+cat >etc/admin.cfg <<__CONF__
+svn_backup_dir=$PWD/var/svn/backups
+svn_dump_dir=$PWD/var/svn/dumps
+svn_live_dir=$PWD/srv/svn
+__CONF__
+# Create some repositories and populate them
+# Repository 1
+svnadmin create srv/svn/bar
+svn co -q file://$PWD/srv/svn/bar
+echo 'A man walks into a bar.' >bar/walk
+echo 'Barley drink.' >bar/barley
+svn add -q bar/*
+svn ci -q -m'test 1' bar
+svnadmin dump srv/svn/bar -r 1 --incremental --deltas -q \
+        | gzip >var/svn/dumps/bar-1.gz
+# Repository 2
+svnadmin create srv/svn/foo
+svn co -q file://$PWD/srv/svn/foo
+echo 'Food is yummy.' >foo/food
+svn add -q foo/*
+svn ci -q -m'test 1' foo
+svnadmin dump srv/svn/foo -r 1 --incremental --deltas -q \
+    | gzip >var/svn/dumps/foo-1.gz
+rm -fr bar foo
+set +e
+
+run_tests() {
+    local TEST_KEY=$1
+    run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-backup-svn-repos"
+    for NAME in bar foo; do
+        file_test "$TEST_KEY-$NAME" var/svn/backups/$NAME.tgz
+        tar -xzf var/svn/backups/$NAME.tgz
+        svnlook youngest srv/svn/$NAME >"$TEST_KEY-$NAME.youngest.orig"
+        svnlook youngest $NAME >"$TEST_KEY-$NAME.youngest"
+        file_cmp "$TEST_KEY-$NAME.youngest" \
+            "$TEST_KEY-$NAME.youngest" "$TEST_KEY-$NAME.youngest.orig"
+        rm -fr $NAME
+        for REV in $(seq 1 $(<"$TEST_KEY-$NAME.youngest")); do
+            run_fail "$TEST_KEY-dumps-$NAME-$REV" ls var/svn/dumps/$NAME-$REV.gz
+        done
+    done
+}
+#-------------------------------------------------------------------------------
+run_tests "$TEST_KEY_BASE-1-1"
+run_tests "$TEST_KEY_BASE-1-2" # Re-run test
+#-------------------------------------------------------------------------------
+# Add more stuffs to repository 1
+svn co -q file://$PWD/srv/svn/bar
+for REV in {2..9}; do
+    echo "$REV men walk into a bar." >bar/walk
+    svn ci -m"test: $REV" bar/walk
+    svnadmin dump srv/svn/bar -r $REV --incremental --deltas -q \
+            | gzip >var/svn/dumps/bar-$REV.gz
+done
+# Add more stuffs to a copy of repository 1, to generate some more dumps To
+# prove that command will not housekeep dumps that are newer than the backed up
+# youngest.
+svnadmin hotcopy srv/svn/bar var/svn/cache/bar
+svn relocate file://$PWD/srv/svn/bar file://$PWD/var/svn/cache/bar bar
+for REV in {10..12}; do
+    echo "$REV men walk into a bar." >bar/walk
+    svn ci -m"test: $REV" bar/walk
+    svnadmin dump var/svn/cache/bar -r $REV --incremental --deltas -q \
+            | gzip >var/svn/dumps/bar-$REV.gz
+done
+run_tests "$TEST_KEY_BASE-2-1"
+for REV in {10..12}; do
+    file_test "$TEST_KEY_BASE-2-1-dumps-bar-$REV" var/svn/dumps/bar-$REV.gz
+done
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-backup-svn-repos/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-backup-svn-repos/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-backup-svn-repos/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-create/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-create/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-create/00-simple.t	(revision 5095)
@@ -0,0 +1,97 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm branch-create".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 12
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm branch-create
+TEST_KEY=$TEST_KEY_BASE-fcm-bc
+run_pass "$TEST_KEY" fcm branch-create -t SHARE --rev-flag=NONE \
+                                        --non-interactive \
+                                        my_branch_test
+branch_tidy "$TEST_KEY.out"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] Source: $ROOT_URL/trunk@1 (4)
+Change summary:
+--------------------------------------------------------------------------------
+A    $ROOT_URL/branches/dev/Share/my_branch_test
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+Created /${PROJECT}branches/dev/Share/my_branch_test from /${PROJECT}trunk@1.
+--------------------------------------------------------------------------------
+Committed revision 5.
+[info] Created: $ROOT_URL/branches/dev/Share/my_branch_test
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests existence of branch
+TEST_KEY=$TEST_KEY_BASE-fcm-bc-branch-exists-sw
+run_pass "$TEST_KEY" svn switch \
+               $ROOT_URL/branches/dev/Share/my_branch_test
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<'__OUT__'
+At revision 5.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
+init_repos
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm branch-create --branch-of-branch
+TEST_KEY=$TEST_KEY_BASE-fcm-bc-branch-of-branch
+run_pass "$TEST_KEY" fcm branch-create -t SHARE --rev-flag=NONE \
+                                       --non-interactive \
+                                       --branch-of-branch my_branch_test
+branch_tidy "$TEST_KEY.out"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] Source: $ROOT_URL/branches/dev/Share/branch_test@4 (4)
+Change summary:
+--------------------------------------------------------------------------------
+A    $ROOT_URL/branches/dev/Share/my_branch_test
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+Created /${PROJECT}branches/dev/Share/my_branch_test from /${PROJECT}branches/dev/Share/branch_test@4.
+--------------------------------------------------------------------------------
+Committed revision 5.
+[info] Created: $ROOT_URL/branches/dev/Share/my_branch_test
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests existence of branch
+TEST_KEY=$TEST_KEY_BASE-fcm-bc-branch-of-branch-exists-sw
+run_pass "$TEST_KEY" svn switch \
+               $ROOT_URL/branches/dev/Share/my_branch_test
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<'__OUT__'
+At revision 5.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-create/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-create/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-create/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/00-simple.t	(revision 5095)
@@ -0,0 +1,66 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm branch-delete".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 12
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch branch_test $REPOS_URL
+init_branch_wc my_branch_test $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm branch-delete
+TEST_KEY=$TEST_KEY_BASE-delete
+run_pass "$TEST_KEY" fcm branch-delete --non-interactive $ROOT_URL/branches/dev/Share/branch_test
+file_grep "$TEST_KEY.out" "Deleting branch $ROOT_URL/branches/dev/Share/branch_test ..." \
+          "$TEST_KEY.out"
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests existence of branch
+TEST_KEY=$TEST_KEY_BASE-delete-branch-exists
+run_fail "$TEST_KEY" svn info \
+               $ROOT_URL/branches/dev/Share/branch_test
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+file_test "$TEST_KEY.err" "$TEST_KEY.err" -s
+teardown
+#-------------------------------------------------------------------------------
+init_repos
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm brm
+TEST_KEY=$TEST_KEY_BASE-brm
+run_pass "$TEST_KEY" fcm brm --non-interactive $ROOT_URL/branches/dev/Share/branch_test
+file_grep "$TEST_KEY.out" "Deleting branch $ROOT_URL/branches/dev/Share/branch_test ..." \
+          "$TEST_KEY.out"
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm brm disappearance of branch
+TEST_KEY=$TEST_KEY_BASE-brm-branch-exists
+run_fail "$TEST_KEY" svn info \
+               $ROOT_URL/branches/dev/Share/branch_test
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+file_test "$TEST_KEY.err" "$TEST_KEY.err" -s
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/01-bad-arg.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/01-bad-arg.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/01-bad-arg.t	(revision 5095)
@@ -0,0 +1,40 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Test "fcm branch-delete" with bad argument and in a working copy.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 3
+#-------------------------------------------------------------------------------
+# Tests fcm branch-delete with bad argument, and in a working copy
+TEST_KEY="${TEST_KEY_BASE}"
+setup
+init_repos
+init_branch 'branch_test' "${REPOS_URL}"
+init_branch_wc 'my_branch_test' "${REPOS_URL}"
+cd "${TEST_DIR}/wc"
+run_fail "${TEST_KEY}" fcm branch-delete --non-interactive 'dark-matter'
+file_cmp "${TEST_DIR}/${TEST_KEY}.out" "${TEST_KEY}.out" </dev/null
+file_cmp "${TEST_DIR}/${TEST_KEY}.err" "${TEST_KEY}.err" <<'__ERR__'
+[FAIL] dark-matter: not a valid working copy or URL.
+
+__ERR__
+teardown
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/02-rosie.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/02-rosie.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/02-rosie.t	(revision 5095)
@@ -0,0 +1,39 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Test "fcm branch-delete" does not produce warnings for rosie branches.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 3
+#-------------------------------------------------------------------------------
+# Tests fcm branch-delete with bad argument, and in a working copy
+TEST_KEY="${TEST_KEY_BASE}"
+setup
+init_repos_layout_roses
+svn copy -q -m 'create a branch' \
+    "${REPOS_URL}/a/a/0/0/0/trunk" "${REPOS_URL}/a/a/0/0/0/my-branch"
+run_pass "${TEST_KEY}" \
+    fcm branch-delete --non-interactive "${REPOS_URL}/a/a/0/0/0/my-branch"
+file_cmp "${TEST_DIR}/${TEST_KEY}.err" "${TEST_KEY}.err" <'/dev/null'
+run_pass "${TEST_KEY}.out" \
+    grep -q -F "Deleting branch ${REPOS_URL}/a/a/0/0/0/my-branch ..." \
+    "../${TEST_KEY}.out"
+teardown
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-delete/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-diff/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-diff/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-diff/00-simple.t	(revision 5095)
@@ -0,0 +1,435 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm branch-diff".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 18
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+FILE_LIST="lib/python/info/__init__.py lib/python/info/poems.py \
+module/hello_constants.f90 module/hello_constants.inc \
+module/hello_constants_dummy.inc"
+for FILE in $FILE_LIST; do 
+    sed -i "s/for/FOR/g; s/fi/end if/g; s/in/IN/g;" $FILE
+    sed -i "/#/d; /^ *!/d" $FILE
+    sed -i "s/!/!!/g; s/q/\nq/g; s/[(]/(\n/g" $FILE
+done
+FILE_DIR=$(dirname $FILE)
+svn copy -q $FILE added_file
+svn copy -q $FILE_DIR added_directory
+svn delete --force -q $FILE_DIR
+svn commit -q -m "make branch diff"
+svn switch -q $ROOT_URL/trunk
+TMPFILE=$(mktemp)
+for FILE in $FILE_LIST; do
+    if [[ -e $FILE ]]; then
+        tac $FILE > $TMPFILE && mv $TMPFILE $FILE
+    fi
+done
+rm -f $TMPFILE
+svn commit -q -m "make trunk diff"
+svn switch -q $ROOT_URL/branches/dev/Share/branch_test
+#-------------------------------------------------------------------------------
+# Tests fcm branch-diff
+TEST_KEY=$TEST_KEY_BASE-fcm-branch-diff
+run_pass "$TEST_KEY" fcm branch-diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: added_directory/hello_constants.f90
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants.f90	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants.f90	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants.f90	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants.f90	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1,5 @@
++MODULE Hello_Constants
++
++INCLUDE 'hello_constants_dummy.INc'
++
++END MODULE Hello_Constants
+Index: added_directory/hello_constants.inc
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants.inc	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants.inc	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants.inc	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants.inc	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1,2 @@
++CHARACTER (
++LEN=80), PARAMETER :: hello_strINg = 'Hello Earth!!'
+Index: added_directory/hello_constants_dummy.inc
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants_dummy.inc	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants_dummy.inc	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants_dummy.inc	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants_dummy.inc	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1 @@
++INCLUDE 'hello_constants.INc'
+Index: added_file
+===================================================================
+#IF SVN1.8 --- added_file	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_file	(revision 6)
+#IF SVN1.9/10/14 --- added_file	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_file	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1 @@
++INCLUDE 'hello_constants.INc'
+Index: lib/python/info/poems.py
+===================================================================
+#IF SVN1.8 --- lib/python/info/poems.py	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ lib/python/info/poems.py	(working copy)
+#IF SVN1.9/10/14 --- lib/python/info/poems.py	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ lib/python/info/poems.py	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1,24 +1,23 @@
+-#!/usr/bin/env python
+-# -*- coding: utf-8 -*-
+ """The Python, by Hilaire Belloc
+ 
+ A Python I should not advise,--
+-It needs a doctor for its eyes,
++It needs a doctor FOR its eyes,
+ And has the measles yearly.
+-However, if you feel inclined
+-To get one (to improve your mind,
++However, if you feel INclINed
++To get one (
++to improve your mINd,
+ And not from fashion merely),
+ Allow no music near its cage;
+-And when it flies into a rage
++And when it flies INto a rage
+ Chastise it, most severely.
+-I had an aunt in Yucatan
++I had an aunt IN Yucatan
+ Who bought a Python from a man
+-And kept it for a pet.
++And kept it FOR a pet.
+ She died, because she never knew
+ These simple little rules and few;--
+-The Snake is living yet.
++The Snake is livINg yet.
+ """
+ 
+ import this
+ 
+-print "\n",  __doc__
++prINt "\n",  __doc__
+Index: module/hello_constants.f90
+===================================================================
+#IF SVN1.8 --- module/hello_constants.f90	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants.f90	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants.f90	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants.f90	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1,5 +0,0 @@
+-MODULE Hello_Constants
+-
+-INCLUDE 'hello_constants_dummy.inc'
+-
+-END MODULE Hello_Constants
+Index: module/hello_constants.inc
+===================================================================
+#IF SVN1.8 --- module/hello_constants.inc	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants.inc	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants.inc	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants.inc	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1 +0,0 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
+Index: module/hello_constants_dummy.inc
+===================================================================
+#IF SVN1.8 --- module/hello_constants_dummy.inc	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants_dummy.inc	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants_dummy.inc	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants_dummy.inc	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1 +0,0 @@
+-INCLUDE 'hello_constants.inc'
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm bdi
+TEST_KEY=$TEST_KEY_BASE-bdi
+run_pass "$TEST_KEY" fcm bdi
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: added_directory/hello_constants.f90
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants.f90	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants.f90	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants.f90	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants.f90	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1,5 @@
++MODULE Hello_Constants
++
++INCLUDE 'hello_constants_dummy.INc'
++
++END MODULE Hello_Constants
+Index: added_directory/hello_constants.inc
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants.inc	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants.inc	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants.inc	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants.inc	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1,2 @@
++CHARACTER (
++LEN=80), PARAMETER :: hello_strINg = 'Hello Earth!!'
+Index: added_directory/hello_constants_dummy.inc
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants_dummy.inc	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants_dummy.inc	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants_dummy.inc	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants_dummy.inc	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1 @@
++INCLUDE 'hello_constants.INc'
+Index: added_file
+===================================================================
+#IF SVN1.8 --- added_file	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_file	(revision 6)
+#IF SVN1.9/10/14 --- added_file	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_file	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1 @@
++INCLUDE 'hello_constants.INc'
+Index: lib/python/info/poems.py
+===================================================================
+#IF SVN1.8 --- lib/python/info/poems.py	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ lib/python/info/poems.py	(working copy)
+#IF SVN1.9/10/14 --- lib/python/info/poems.py	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ lib/python/info/poems.py	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1,24 +1,23 @@
+-#!/usr/bin/env python
+-# -*- coding: utf-8 -*-
+ """The Python, by Hilaire Belloc
+ 
+ A Python I should not advise,--
+-It needs a doctor for its eyes,
++It needs a doctor FOR its eyes,
+ And has the measles yearly.
+-However, if you feel inclined
+-To get one (to improve your mind,
++However, if you feel INclINed
++To get one (
++to improve your mINd,
+ And not from fashion merely),
+ Allow no music near its cage;
+-And when it flies into a rage
++And when it flies INto a rage
+ Chastise it, most severely.
+-I had an aunt in Yucatan
++I had an aunt IN Yucatan
+ Who bought a Python from a man
+-And kept it for a pet.
++And kept it FOR a pet.
+ She died, because she never knew
+ These simple little rules and few;--
+-The Snake is living yet.
++The Snake is livINg yet.
+ """
+ 
+ import this
+ 
+-print "\n",  __doc__
++prINt "\n",  __doc__
+Index: module/hello_constants.f90
+===================================================================
+#IF SVN1.8 --- module/hello_constants.f90	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants.f90	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants.f90	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants.f90	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1,5 +0,0 @@
+-MODULE Hello_Constants
+-
+-INCLUDE 'hello_constants_dummy.inc'
+-
+-END MODULE Hello_Constants
+Index: module/hello_constants.inc
+===================================================================
+#IF SVN1.8 --- module/hello_constants.inc	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants.inc	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants.inc	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants.inc	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1 +0,0 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
+Index: module/hello_constants_dummy.inc
+===================================================================
+#IF SVN1.8 --- module/hello_constants_dummy.inc	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants_dummy.inc	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants_dummy.inc	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants_dummy.inc	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1 +0,0 @@
+-INCLUDE 'hello_constants.inc'
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-diff --wiki
+TEST_KEY=$TEST_KEY_BASE-wiki
+run_pass "$TEST_KEY" fcm branch-diff --wiki
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+diff:/${PROJECT}trunk@1///${PROJECT}branches/dev/Share/branch_test@6
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm bdi --wiki
+TEST_KEY=$TEST_KEY_BASE-bdi-wiki
+run_pass "$TEST_KEY" fcm bdi --wiki
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+diff:/${PROJECT}trunk@1///${PROJECT}branches/dev/Share/branch_test@6
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm bdi on the trunk
+svn switch -q $ROOT_URL/trunk
+TEST_KEY=$TEST_KEY_BASE-bdi-trunk
+run_fail "$TEST_KEY" fcm bdi
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<__ERR__
+[FAIL] $ROOT_URL/trunk@6: not a valid URL of a standard FCM branch.
+
+__ERR__
+#-------------------------------------------------------------------------------
+# Tests fcm bdi with working copy changes
+svn switch -q $ROOT_URL/branches/dev/Share/branch_test
+TEST_KEY=$TEST_KEY_BASE-bdi-wc-changes
+echo "foo" > added_directory/foo$TEST_KEY
+svn add -q added_directory/foo$TEST_KEY
+echo "bar" > added_directory/bar$TEST_KEY
+run_pass "$TEST_KEY" fcm bdi
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: added_directory/foo00-simple-bdi-wc-changes
+===================================================================
+#IF SVN1.8 --- added_directory/foo00-simple-bdi-wc-changes	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/foo00-simple-bdi-wc-changes	(working copy)
+#IF SVN1.9/10/14 --- added_directory/foo00-simple-bdi-wc-changes	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/foo00-simple-bdi-wc-changes	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -0,0 +1 @@
++foo
+Index: added_directory/hello_constants.f90
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants.f90	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants.f90	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants.f90	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants.f90	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1,5 @@
++MODULE Hello_Constants
++
++INCLUDE 'hello_constants_dummy.INc'
++
++END MODULE Hello_Constants
+Index: added_directory/hello_constants.inc
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants.inc	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants.inc	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants.inc	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants.inc	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1,2 @@
++CHARACTER (
++LEN=80), PARAMETER :: hello_strINg = 'Hello Earth!!'
+Index: added_directory/hello_constants_dummy.inc
+===================================================================
+#IF SVN1.8 --- added_directory/hello_constants_dummy.inc	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_directory/hello_constants_dummy.inc	(revision 6)
+#IF SVN1.9/10/14 --- added_directory/hello_constants_dummy.inc	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_directory/hello_constants_dummy.inc	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1 @@
++INCLUDE 'hello_constants.INc'
+Index: added_file
+===================================================================
+#IF SVN1.8 --- added_file	($ROOT_URL/trunk)	(revision 0)
+#IF SVN1.8 +++ added_file	(revision 6)
+#IF SVN1.9/10/14 --- added_file	(.../trunk)	(working copy)
+#IF SVN1.9/10/14 +++ added_file	(.../branches/dev/Share/branch_test)	(revision 6)
+@@ -0,0 +1 @@
++INCLUDE 'hello_constants.INc'
+Index: lib/python/info/poems.py
+===================================================================
+#IF SVN1.8 --- lib/python/info/poems.py	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ lib/python/info/poems.py	(working copy)
+#IF SVN1.9/10/14 --- lib/python/info/poems.py	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ lib/python/info/poems.py	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1,24 +1,23 @@
+-#!/usr/bin/env python
+-# -*- coding: utf-8 -*-
+ """The Python, by Hilaire Belloc
+ 
+ A Python I should not advise,--
+-It needs a doctor for its eyes,
++It needs a doctor FOR its eyes,
+ And has the measles yearly.
+-However, if you feel inclined
+-To get one (to improve your mind,
++However, if you feel INclINed
++To get one (
++to improve your mINd,
+ And not from fashion merely),
+ Allow no music near its cage;
+-And when it flies into a rage
++And when it flies INto a rage
+ Chastise it, most severely.
+-I had an aunt in Yucatan
++I had an aunt IN Yucatan
+ Who bought a Python from a man
+-And kept it for a pet.
++And kept it FOR a pet.
+ She died, because she never knew
+ These simple little rules and few;--
+-The Snake is living yet.
++The Snake is livINg yet.
+ """
+ 
+ import this
+ 
+-print "\n",  __doc__
++prINt "\n",  __doc__
+Index: module/hello_constants.f90
+===================================================================
+#IF SVN1.8 --- module/hello_constants.f90	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants.f90	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants.f90	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants.f90	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1,5 +0,0 @@
+-MODULE Hello_Constants
+-
+-INCLUDE 'hello_constants_dummy.inc'
+-
+-END MODULE Hello_Constants
+Index: module/hello_constants.inc
+===================================================================
+#IF SVN1.8 --- module/hello_constants.inc	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants.inc	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants.inc	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants.inc	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1 +0,0 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
+Index: module/hello_constants_dummy.inc
+===================================================================
+#IF SVN1.8 --- module/hello_constants_dummy.inc	($ROOT_URL/trunk)	(revision 1)
+#IF SVN1.8 +++ module/hello_constants_dummy.inc	(working copy)
+#IF SVN1.9/10/14 --- module/hello_constants_dummy.inc	(.../trunk)	(revision 1)
+#IF SVN1.9/10/14 +++ module/hello_constants_dummy.inc	(.../branches/dev/Share/branch_test)	(working copy)
+@@ -1 +0,0 @@
+-INCLUDE 'hello_constants.inc'
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-diff/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-diff/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-diff/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-info/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-info/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-info/00-simple.t	(revision 5095)
@@ -0,0 +1,156 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm branch-info".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 12
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch sibling_branch_test $REPOS_URL
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+fcm branch-create -t SHARE --rev-flag=NONE \
+                           --non-interactive \
+                           --branch-of-branch my_branch_test >/dev/null
+svn switch -q $ROOT_URL/trunk
+FILE_LIST="lib/python/info/__init__.py lib/python/info/poems.py \
+module/hello_constants.f90 module/hello_constants.inc \
+module/hello_constants_dummy.inc"
+for FILE in $FILE_LIST; do 
+    sed -i "s/for/FOR/g; s/fi/end if/g; s/in/IN/g;" $FILE
+    sed -i "/#/d; /^ *!/d" $FILE
+    sed -i "s/!/!!/g; s/q/\nq/g; s/[(]/(\n/g" $FILE
+done
+svn commit -q -m "add trunk commit"
+svn switch -q $ROOT_URL/branches/dev/Share/branch_test
+#-------------------------------------------------------------------------------
+# Tests fcm branch-info
+TEST_KEY=$TEST_KEY_BASE-info
+run_pass "$TEST_KEY" fcm branch-info
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+URL: $ROOT_URL/branches/dev/Share/branch_test
+Repository Root: $REPOS_URL
+Revision: 7
+Last Changed Author: $LOGNAME
+Last Changed Rev: 5
+--------------------------------------------------------------------------------
+Branch Create Author: $LOGNAME
+Branch Create Rev: 5
+--------------------------------------------------------------------------------
+Branch Parent: $ROOT_URL/trunk@1
+Merges Avail From Parent: 7
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-info -a
+TEST_KEY=$TEST_KEY_BASE-a
+run_pass "$TEST_KEY" fcm branch-info -a
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+URL: $ROOT_URL/branches/dev/Share/branch_test
+Repository Root: $REPOS_URL
+Revision: 7
+Last Changed Author: $LOGNAME
+Last Changed Rev: 5
+--------------------------------------------------------------------------------
+Branch Create Author: $LOGNAME
+Branch Create Rev: 5
+--------------------------------------------------------------------------------
+Branch Parent: $ROOT_URL/trunk@1
+Merges Avail From Parent: 7
+--------------------------------------------------------------------------------
+Searching for siblings ... 1 sibling found.
+No merges with existing siblings.
+--------------------------------------------------------------------------------
+Searching for children ... 1 child found.
+Current children:
+  ------------------------------------------------------------------------------
+  $ROOT_URL/branches/dev/Share/my_branch_test
+  Child Create Rev: 6
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-info --show-children
+TEST_KEY=$TEST_KEY_BASE-show-children
+run_pass "$TEST_KEY" fcm branch-info --show-children
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+URL: $ROOT_URL/branches/dev/Share/branch_test
+Repository Root: $REPOS_URL
+Revision: 7
+Last Changed Author: $LOGNAME
+Last Changed Rev: 5
+--------------------------------------------------------------------------------
+Branch Create Author: $LOGNAME
+Branch Create Rev: 5
+--------------------------------------------------------------------------------
+Branch Parent: $ROOT_URL/trunk@1
+Merges Avail From Parent: 7
+--------------------------------------------------------------------------------
+Searching for children ... 1 child found.
+Current children:
+  ------------------------------------------------------------------------------
+  $ROOT_URL/branches/dev/Share/my_branch_test
+  Child Create Rev: 6
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-info --show-siblings
+TEST_KEY=$TEST_KEY_BASE-show-siblings
+svn switch -q $ROOT_URL/branches/dev/Share/sibling_branch_test
+svn merge -q $ROOT_URL/trunk
+svn commit -q -m "Merged trunk into sibling branch"
+svn switch -q $ROOT_URL/branches/dev/Share/branch_test
+svn merge -q $ROOT_URL/branches/dev/Share/sibling_branch_test
+svn commit -q -m "Merged sibling into test branch"
+svn switch -q $ROOT_URL/branches/dev/Share/sibling_branch_test
+TMPFILE=$(mktemp)
+for FILE in $FILE_LIST; do 
+    cut -f 1 $FILE > $TMPFILE
+    mv $TMPFILE $FILE
+done
+svn commit -q -m "Add sibling commit"
+svn switch -q $ROOT_URL/branches/dev/Share/branch_test
+run_pass "$TEST_KEY" fcm branch-info --show-siblings
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+URL: $ROOT_URL/branches/dev/Share/branch_test
+Repository Root: $REPOS_URL
+Revision: 9
+Last Changed Author: $LOGNAME
+Last Changed Rev: 9
+--------------------------------------------------------------------------------
+Branch Create Author: $LOGNAME
+Branch Create Rev: 5
+--------------------------------------------------------------------------------
+Branch Parent: $ROOT_URL/trunk@1
+Merges Avail From Parent: 7
+Merges Avail Into Parent: 9
+--------------------------------------------------------------------------------
+Searching for siblings ... 1 sibling found.
+No merges with existing siblings.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-info/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-info/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-info/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-list/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-list/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-list/00-simple.t	(revision 5095)
@@ -0,0 +1,136 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm branch-list".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 23
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch sibling_branch_test $REPOS_URL
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+fcm branch-create --rev-flag=NONE \
+                  --non-interactive \
+                  --branch-of-branch my_branch_test >/dev/null
+ROOT_PATH=
+if [[ -n ${TEST_PROJECT:-} ]]; then
+    ROOT_PATH=/$TEST_PROJECT
+fi
+MESSAGE=$(echo -e "Created $ROOT_PATH/branches/dev/fred/donuts from /trunk@1.")
+# Please note: if $LOGNAME is drfooeybar or Share, some tests will fail.
+svn mkdir -q -m "Dr Fooeybar branch" $ROOT_URL/branches/dev/drfooeybar/
+svn copy -q -r1 $ROOT_URL/trunk $ROOT_URL/branches/dev/drfooeybar/donuts \
+            -m "Made a branch $MESSAGE" --non-interactive
+FILE_LIST="lib/python/info/__init__.py lib/python/info/poems.py \
+module/hello_constants.f90 module/hello_constants.inc \
+module/hello_constants_dummy.inc"
+for FILE in $FILE_LIST; do 
+    sed -i "s/for/FOR/g; s/fi/end if/g; s/in/IN/g;" $FILE
+    sed -i "/#/d; /^ *!/d" $FILE
+    sed -i "s/!/!!/g; s/q/\nq/g; s/[(]/(\n/g" $FILE
+done
+svn commit -q -m "add branch commit"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/branch_test
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list
+TEST_KEY=$TEST_KEY_BASE-list
+run_pass "$TEST_KEY" fcm branch-list
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] $ROOT_URL@9: 1 match(es)
+$ROOT_URL/branches/dev/$LOGNAME/my_branch_test@9
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list -a
+TEST_KEY=$TEST_KEY_BASE-a
+run_pass "$TEST_KEY" fcm branch-list -a
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+TMPFILE=$(mktemp)
+sort > $TMPFILE <<__OUT__
+[info] $ROOT_URL@9: 4 match(es)
+$ROOT_URL/branches/dev/Share/branch_test@9
+$ROOT_URL/branches/dev/Share/sibling_branch_test@9
+$ROOT_URL/branches/dev/$LOGNAME/my_branch_test@9
+$ROOT_URL/branches/dev/drfooeybar/donuts@9
+__OUT__
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <$TMPFILE
+rm -f $TMPFILE
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list --user
+TEST_KEY=$TEST_KEY_BASE-a
+run_pass "$TEST_KEY" fcm branch-list --user=drfooeybar
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] $ROOT_URL@9: 1 match(es)
+$ROOT_URL/branches/dev/drfooeybar/donuts@9
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list --only (1)
+TEST_KEY=$TEST_KEY_BASE-only-1
+run_pass "$TEST_KEY" fcm branch-list --only=3:donut
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] $ROOT_URL@9: 1 match(es)
+$ROOT_URL/branches/dev/drfooeybar/donuts@9
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list --only (2)
+TEST_KEY=$TEST_KEY_BASE-only-2
+run_pass "$TEST_KEY" fcm branch-list --only=2:Share
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] $ROOT_URL@9: 2 match(es)
+$ROOT_URL/branches/dev/Share/branch_test@9
+$ROOT_URL/branches/dev/Share/sibling_branch_test@9
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list --only (3)
+TEST_KEY=$TEST_KEY_BASE-only-3
+run_pass "$TEST_KEY" fcm branch-list --only=2:Share --only=3:sibling
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] $ROOT_URL@9: 1 match(es)
+$ROOT_URL/branches/dev/Share/sibling_branch_test@9
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list --only (4)
+TEST_KEY=$TEST_KEY_BASE-only-4
+run_pass "$TEST_KEY" fcm branch-list --only=1:something-not-right
+sed -i "/ Date/d;" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] $ROOT_URL@9: 0 match(es)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm branch-list --only (5)
+TEST_KEY=$TEST_KEY_BASE-only-5
+run_fail "$TEST_KEY" fcm branch-list --only=1:\)
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-list/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-list/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-branch-list/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-build/00-geninterface.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-build/00-geninterface.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-build/00-geninterface.t	(revision 5095)
@@ -0,0 +1,52 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# #156 breaks interface generation. This test ensures that it is not broken
+# again.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 2
+#-------------------------------------------------------------------------------
+
+cat >'bld.cfg' <<'__FCM_BLD_CFG__'
+cfg::type bld
+dest $HERE
+tool::geninterface
+__FCM_BLD_CFG__
+
+mkdir 'src'
+cat >'src/hello.f90' <<'__FORTRAN__'
+subroutine hello(world)
+character(*), intent(in) :: world
+write(*, '(a)') 'Hello ' // trim(world)
+end subroutine hello
+__FORTRAN__
+
+#-------------------------------------------------------------------------------
+run_pass "${TEST_KEY_BASE}-cmd" fcm build -f -s 4
+file_cmp "${TEST_KEY_BASE}-interface" 'inc/hello.interface' <<'__FORTRAN__'
+interface
+subroutine hello(world)
+character(*), intent(in) :: world
+end subroutine hello
+end interface
+__FORTRAN__
+
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-build/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-build/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-build/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-cfg-print/00-env.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-cfg-print/00-env.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-cfg-print/00-env.t	(revision 5095)
@@ -0,0 +1,69 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Configuration file load and dump. Test environment variable substitution
+# issues.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 15
+
+echo 'include=$FCM_WHATEVER' >'foo.cfg'
+echo >'bar.cfg'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-undef"
+run_fail "${TEST_KEY}" fcm 'cfg-print' 'foo.cfg'
+file_cmp "${TEST_KEY}.out" "${TEST_KEY}.out" <'/dev/null'
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <<__ERR__
+[FAIL] ${PWD}/foo.cfg:1: reference to undefined variable
+[FAIL] include = 
+[FAIL] undef(\$FCM_WHATEVER)
+
+__ERR__
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-null"
+FCM_WHATEVER= run_pass "${TEST_KEY}" fcm 'cfg-print' 'foo.cfg'
+file_cmp "${TEST_KEY}.out" "${TEST_KEY}.out" <'/dev/null'
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-bad-0"
+FCM_WHATEVER='0' run_fail "${TEST_KEY}" fcm 'cfg-print' 'foo.cfg'
+file_cmp "${TEST_KEY}.out" "${TEST_KEY}.out" <'/dev/null'
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <<__ERR__
+[FAIL] config-file=${PWD}/foo.cfg:1
+[FAIL] ${PWD}/foo.cfg: cannot load config file
+[FAIL] include=0
+
+__ERR__
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-bad"
+FCM_WHATEVER='bad.cfg' run_fail "${TEST_KEY}" fcm 'cfg-print' 'foo.cfg'
+file_cmp "${TEST_KEY}.out" "${TEST_KEY}.out" <'/dev/null'
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <<__ERR__
+[FAIL] config-file=${PWD}/foo.cfg:1
+[FAIL] ${PWD}/foo.cfg: cannot load config file
+[FAIL] include=bad.cfg
+
+__ERR__
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-good"
+FCM_WHATEVER='bar.cfg' run_pass "${TEST_KEY}" fcm 'cfg-print' 'foo.cfg'
+file_cmp "${TEST_KEY}.out" "${TEST_KEY}.out" <'/dev/null'
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-cfg-print/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-cfg-print/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-cfg-print/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/00-simple.t	(revision 5095)
@@ -0,0 +1,94 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm commit".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 3
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch sibling_branch_test $REPOS_URL
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+FILE_LIST="./lib/python/info/__init__.py
+./lib/python/info/poems.py
+./module/hello_constants.f90
+./module/hello_constants.inc
+./module/hello_constants_dummy.inc
+"
+for FILE in $FILE_LIST; do
+    sed -i "s/for/FOR/g; s/fi/end if/g; s/in/IN/g;" $FILE
+    sed -i "/#/d; /^ *!/d" $FILE
+    sed -i "s/!/!!/g; s/q/\nq/g; s/[(]/(\n/g" $FILE
+done
+FILE_DIR=$(dirname $FILE)
+svn copy -q $FILE added_file
+svn copy -q $FILE_DIR added_directory
+svn delete --force -q $FILE_DIR
+#-------------------------------------------------------------------------------
+# Tests fcm commit
+TEST_KEY=$TEST_KEY_BASE
+export SVN_EDITOR="sed -i 1i\foo" 
+run_pass "$TEST_KEY" fcm commit --svn-non-interactive <<__IN__
+y
+__IN__
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : branches/dev/Share/branch_test]
+[Sub-dir: ]
+A  +    added_directory
+A  +    added_file
+D       module
+#IF SVN1.8/9 D       module/hello_constants.f90
+#IF SVN1.8/9 D       module/hello_constants.inc
+#IF SVN1.8/9 D       module/hello_constants_dummy.inc
+M       lib/python/info/poems.py
+M  +    added_directory/hello_constants.f90
+M  +    added_directory/hello_constants.inc
+M  +    added_directory/hello_constants_dummy.inc
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO A Share BRANCH.
+*** Please ensure that you have the owner's permission.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Adding         added_directory
+Adding         added_file
+Deleting       module
+Sending        added_directory/hello_constants.f90
+Sending        added_directory/hello_constants.inc
+Sending        added_directory/hello_constants_dummy.inc
+Sending        lib/python/info/poems.py
+Committed revision 6.
+Updating '.':
+At revision 6.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/01-subtree.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/01-subtree.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/01-subtree.t	(revision 5095)
@@ -0,0 +1,96 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm commit".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 3
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch sibling_branch_test $REPOS_URL
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+FILE_LIST="./lib/python/info/__init__.py
+./lib/python/info/poems.py
+./module/hello_constants.f90
+./module/hello_constants.inc
+./module/hello_constants_dummy.inc
+"
+for FILE in $FILE_LIST; do
+    sed -i "s/for/FOR/g; s/fi/end if/g; s/in/IN/g;" $FILE
+    sed -i "/#/d; /^ *!/d" $FILE
+    sed -i "s/!/!!/g; s/q/\nq/g; s/[(]/(\n/g" $FILE
+done
+FILE_DIR=$(dirname $FILE)
+svn copy -q $FILE added_file
+svn copy -q $FILE_DIR added_directory
+svn delete --force -q $FILE_DIR
+#-------------------------------------------------------------------------------
+# Tests fcm commit
+TEST_KEY=$TEST_KEY_BASE
+export SVN_EDITOR="sed -i 1i\foo"
+cd program
+run_pass "$TEST_KEY" fcm commit --svn-non-interactive <<__IN__
+y
+__IN__
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+$TEST_DIR/wc: working directory changed to top of working copy.
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : branches/dev/Share/branch_test]
+[Sub-dir: ]
+A  +    added_directory
+A  +    added_file
+D       module
+#IF SVN1.8/9 D       module/hello_constants.f90
+#IF SVN1.8/9 D       module/hello_constants.inc
+#IF SVN1.8/9 D       module/hello_constants_dummy.inc
+M       lib/python/info/poems.py
+M  +    added_directory/hello_constants.f90
+M  +    added_directory/hello_constants.inc
+M  +    added_directory/hello_constants_dummy.inc
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO A Share BRANCH.
+*** Please ensure that you have the owner's permission.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Adding         added_directory
+Adding         added_file
+Deleting       module
+Sending        added_directory/hello_constants.f90
+Sending        added_directory/hello_constants.inc
+Sending        added_directory/hello_constants_dummy.inc
+Sending        lib/python/info/poems.py
+Committed revision 6.
+Updating '.':
+At revision 6.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/02-bad.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/02-bad.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/02-bad.t	(revision 5095)
@@ -0,0 +1,49 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Bad-behaviour tests for "fcm commit".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 3
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch sibling_branch_test $REPOS_URL
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+svn copy -q pro/hello.pro copied_file
+svn copy -q module copied_directory
+svn delete -q --force lib
+rm -rf program/hello.F90
+#-------------------------------------------------------------------------------
+# Tests fcm commit
+TEST_KEY=$TEST_KEY_BASE
+export SVN_EDITOR="sed -i 1i\foo" 
+run_fail "$TEST_KEY" fcm commit --svn-non-interactive
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<__ERR__
+[ERROR] File(s) missing:
+!                5   program/hello.F90
+[FAIL] FCM1::Cm::Abort: abort
+
+__ERR__
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/03-message-file.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/03-message-file.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/03-message-file.t	(revision 5095)
@@ -0,0 +1,59 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Tests for "fcm commit", attempt to add commit message file.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 6
+#-------------------------------------------------------------------------------
+svnadmin create foo
+svn co -q file://$PWD/foo 'test-work'
+touch 'test-work/#commit_message#'
+svn add 'test-work/#commit_message#'
+export SVN_EDITOR='cat'
+#-------------------------------------------------------------------------------
+# Tests fcm commit, bad commit file 1
+TEST_KEY="$TEST_KEY_BASE-1"
+run_fail "$TEST_KEY" fcm commit --svn-non-interactive 'test-work'
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+$PWD/test-work: working directory changed to top of working copy.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERR__'
+[ERROR] Attempt to add commit message file:
+A       #commit_message#
+[FAIL] FCM1::Cm::Abort: abort
+
+__ERR__
+#-------------------------------------------------------------------------------
+# Tests fcm commit, bad commit file 2
+TEST_KEY="$TEST_KEY_BASE-2"
+cd 'test-work'
+run_fail "$TEST_KEY" fcm commit --svn-non-interactive
+cd ..
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERR__'
+[ERROR] Attempt to add commit message file:
+A       #commit_message#
+[FAIL] FCM1::Cm::Abort: abort
+
+__ERR__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/04-externals.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/04-externals.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/04-externals.t	(revision 5095)
@@ -0,0 +1,41 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Tests for "fcm commit", in a working copy with externals.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 2
+#-------------------------------------------------------------------------------
+svnadmin create 'foo'
+svnadmin create 'bar'
+svn co -q "file://${PWD}/foo" 'test-work'
+svn ps svn:externals "test-work/bar file://${PWD}/bar" 'test-work'
+svn ci -q -m 'set external' 'test-work'
+svn update 'test-work'
+echo 'Whatever!' >'test-work/whatever.txt'
+svn add 'test-work/whatever.txt'
+export SVN_EDITOR="sed -i 1i\foo"
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+run_pass "${TEST_KEY}" fcm commit --svn-non-interactive 'test-work' <<<'y'
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-commit/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/00-tree-add-add.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/00-tree-add-add.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/00-tree-add-add.t	(revision 5095)
@@ -0,0 +1,158 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+[[ $SVN_VERSION == "1.13.0" ]] && skip_all "Tests do not work with svn 1.13.0"
+tests 24
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc add_add $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, discard local   
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+echo "Local contents (1)" > new_file
+svn add -q new_file
+svn commit -q -m "Added duplicate-name copy of conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/add_add
+echo "Merge contents (1)" >new_file
+echo "Merge contents (2)" >>new_file
+svn add -q new_file
+svn commit -q -m "Added duplicated-name copy of conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/add_add >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] new_file: in tree conflict.
+Locally: added.
+Externally: added.
+Answer (y) to keep the local file filename.
+Answer (n) to keep the external file filename.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'new_file'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'new_file' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+M       new_file
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, discard local (info)
+TEST_KEY=$TEST_KEY_BASE-discard-info
+run_pass "$TEST_KEY" svn info new_file
+sed -i "/Date:\|Updated:\|UUID:\|Checksum\|Relative URL:\|Working Copy Root Path:/d" $TEST_DIR/"$TEST_KEY.out"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Path: new_file
+Name: new_file
+URL: $ROOT_URL/branches/dev/Share/ctrl/new_file
+Repository Root: $REPOS_URL
+Revision: 7
+Node Kind: file
+Schedule: normal
+Last Changed Author: $LOGNAME
+Last Changed Rev: 6
+
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-discard-cat
+run_pass "$TEST_KEY" cat new_file
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Merge contents (1)
+Merge contents (2)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/add_add >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] new_file: in tree conflict.
+Locally: added.
+Externally: added.
+Answer (y) to keep the local file filename.
+Answer (n) to keep the external file filename.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'new_file'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'new_file' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, keep local (info)
+TEST_KEY=$TEST_KEY_BASE-keep-info
+run_pass "$TEST_KEY" svn info new_file
+sed -i "/Date:\|Updated:\|UUID:\|Checksum\|Relative URL:\|Working Copy Root Path:/d" $TEST_DIR/"$TEST_KEY.out"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Path: new_file
+Name: new_file
+URL: $ROOT_URL/branches/dev/Share/ctrl/new_file
+Repository Root: $REPOS_URL
+Revision: 7
+Node Kind: file
+Schedule: normal
+Last Changed Author: $LOGNAME
+Last Changed Rev: 6
+
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: add, add, keep local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-cat
+run_pass "$TEST_KEY" cat new_file
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Local contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/01-tree-delete-delete.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/01-tree-delete-delete.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/01-tree-delete-delete.t	(revision 5095)
@@ -0,0 +1,100 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 12
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc del_del $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, delete, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+svn delete -q pro/hello.pro
+svn commit -q -m "Deleted conflict file (local)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/del_del
+svn delete -q pro/hello.pro
+svn commit -q -m "Deleted conflict file (merge)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/del_del >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: deleted.
+Externally: deleted.
+Answer (y) to accept the local delete.
+Answer (n) to accept the external delete.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, delete, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, delete, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/del_del >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: deleted.
+Externally: deleted.
+Answer (y) to accept the local delete.
+Answer (n) to accept the external delete.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, delete, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/02-tree-delete-edit.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/02-tree-delete-edit.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/02-tree-delete-edit.t	(revision 5095)
@@ -0,0 +1,136 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+[[ $SVN_VERSION == "1.13.0" ]] && skip_all "Tests do not work with svn 1.13.0"
+tests 18
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc del_ed $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+# Set a special (null) fcm-graphic-merge diff editor.
+export FCM_GRAPHIC_MERGE=fcm-dummy-diff
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, edit, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+svn delete -q pro/hello.pro
+svn commit -q -m "Deleted local copy of conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/del_ed
+echo "Merge contents (1)" >>pro/hello.pro
+svn commit -q -m "Modified merge copy of conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/del_ed >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: deleted.
+Externally: edited.
+Answer (y) to accept the local delete.
+Answer (n) to keep the file.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") A         pro/hello.pro
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, edit, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+A  +    pro/hello.pro
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, edit, discard local (info)
+TEST_KEY=$TEST_KEY_BASE-discard-info
+run_pass "$TEST_KEY" svn info pro/hello.pro
+sed -i "/Date:\|Updated:\|UUID:\|Checksum\|Relative URL:\|Working Copy Root Path:/d" $TEST_DIR/"$TEST_KEY.out"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Path: pro/hello.pro
+Name: hello.pro
+URL: $ROOT_URL/branches/dev/Share/ctrl/pro/hello.pro
+Repository Root: $REPOS_URL
+Revision: 7
+Node Kind: file
+Schedule: add
+Copied From URL: $ROOT_URL/branches/dev/Share/del_ed/pro/hello.pro
+Copied From Rev: 7
+Last Changed Author: $LOGNAME
+Last Changed Rev: 7
+
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, edit, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-discard-cat
+run_pass "$TEST_KEY" cat pro/hello.pro
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Merge contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, edit, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/del_ed >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: deleted.
+Externally: edited.
+Answer (y) to accept the local delete.
+Answer (n) to keep the file.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, edit, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/03-tree-delete-rename.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/03-tree-delete-rename.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/03-tree-delete-rename.t	(revision 5095)
@@ -0,0 +1,115 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+[[ $SVN_VERSION == "1.13.0" ]] && skip_all "Tests do not work with svn 1.13.0"
+tests 15
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc del_ren $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, rename, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+svn delete -q pro/hello.pro
+svn commit -q -m "Deleted conflict file (local)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/del_ren
+svn rename -q pro/hello.pro pro/hello.pro.renamed
+svn commit -q -m "Renamed conflict file (merge)"
+svn update -q
+echo "Merge changes (1)" >>pro/hello.pro.renamed
+svn commit -q -m "Modified conflict file (merge)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/del_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: deleted.
+Externally: renamed to pro/hello.pro.renamed.
+Answer (y) to accept the local delete.
+Answer (n) to accept the external rename.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, rename, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+A  +    pro/hello.pro.renamed
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, rename, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-discard-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Merge changes (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, rename, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/del_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: deleted.
+Externally: renamed to pro/hello.pro.renamed.
+Answer (y) to accept the local delete.
+Answer (n) to accept the external rename.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") Reverted 'pro/hello.pro.renamed'
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, rename, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/04-tree-edit-delete.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/04-tree-edit-delete.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/04-tree-edit-delete.t	(revision 5095)
@@ -0,0 +1,131 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 18
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc ed_del $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+# Set a special (null) fcm-graphic-merge diff editor.
+export FCM_GRAPHIC_MERGE=fcm-dummy-diff
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, delete, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+echo "Local contents (1)" >>pro/hello.pro
+svn commit -q -m "Modified local copy of conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ed_del
+svn delete -q pro/hello.pro
+svn commit -q -m "Deleted merge copy of conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ed_del >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: edited.
+Externally: deleted.
+Answer (y) to keep the file.
+Answer (n) to accept the external delete.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") D         pro/hello.pro
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, delete, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+D       pro/hello.pro
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, delete, discard local (info)
+TEST_KEY=$TEST_KEY_BASE-discard-info
+run_pass "$TEST_KEY" svn info pro/hello.pro
+sed -i "/Date:\|Updated:\|UUID:\|Checksum\|Relative URL:\|Working Copy Root Path:/d" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Path: pro/hello.pro
+Name: hello.pro
+URL: $ROOT_URL/branches/dev/Share/ctrl/pro/hello.pro
+Repository Root: $REPOS_URL
+Revision: 7
+Node Kind: file
+Schedule: delete
+Last Changed Author: $LOGNAME
+Last Changed Rev: 6
+
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, delete, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ed_del >/dev/null 
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: edited.
+Externally: deleted.
+Answer (y) to keep the file.
+Answer (n) to accept the external delete.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, delete, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, delete, keep local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-cat
+run_pass "$TEST_KEY" cat pro/hello.pro
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Local contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/05-tree-edit-rename.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/05-tree-edit-rename.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/05-tree-edit-rename.t	(revision 5095)
@@ -0,0 +1,225 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 24
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc ed_ren $REPOS_URL
+# Set a special (null) fcm-graphic-merge diff editor.
+export FCM_GRAPHIC_MERGE=fcm-dummy-diff
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, rename, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+echo "Local contents (1)" >>pro/hello.pro
+svn commit -q -m "Modified local copy of conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ed_ren
+echo "Merge contents (1)" >>pro/hello.pro
+svn rename -q pro/hello.pro pro/hello.pro.renamed
+svn commit -q -m "Modified and renamed merge copy of conflict file"
+svn update -q
+echo "Merge contents (2)" >>pro/hello.pro.renamed
+svn commit -q -m "Modified the merge copy of renamed conflict file"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ed_ren >/dev/null
+if [[ $SVN_MINOR_VERSION == "1.14" ]]; then
+  run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+else
+  run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+fi
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+#IF SVN1.8/9/10 [info] pro/hello.pro: in tree conflict.
+#IF SVN1.14 [info] pro/hello.pro.renamed: in text conflict.
+#IF SVN1.8/9/10 Locally: edited.
+#IF SVN1.8/9/10 Externally: renamed to pro/hello.pro.renamed.
+#IF SVN1.8/9/10 Answer (y) to keep the file.
+#IF SVN1.8/9/10 Answer (n) to accept the external rename.
+#IF SVN1.8/9/10 You can then merge in changes.
+#IF SVN1.8/9/10 Keep the local version?
+#IF SVN1.8/9/10 Enter "y" or "n" (or just press <return> for "n") diff3 pro/hello.pro.renamed.working pro/hello.pro.renamed.merge-left.r1 pro/hello.pro.renamed.merge-right.r8
+#IF SVN1.14 diff3 $PWD/pro/hello.pro.renamed.3.tmp $PWD/pro/hello.pro.renamed.tmp $PWD/pro/hello.pro.renamed.2.tmp
+====
+1:3c
+  Local contents (1)
+2:2a
+3:3,4c
+  Merge contents (1)
+  Merge contents (2)
+#IF SVN1.8/9/10 D         pro/hello.pro
+#IF SVN1.14 Run "svn resolve --accept working pro/hello.pro.renamed"?
+#IF SVN1.14 Enter "y" or "n" (or just press <return> for "n") Merge conflicts in 'pro/hello.pro.renamed' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, rename, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+D       pro/hello.pro
+#IF SVN1.14         > moved to pro/hello.pro.renamed
+A  +    pro/hello.pro.renamed
+#IF SVN1.14         > moved from pro/hello.pro
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, rename, discard local (info)
+TEST_KEY=$TEST_KEY_BASE-discard-info
+run_pass "$TEST_KEY" svn info pro/hello.pro.renamed
+sed -i "/Date:\|Updated:\|UUID:\|Checksum\|Relative URL:\|Working Copy Root Path:/d" $TEST_DIR/"$TEST_KEY.out"
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Path: pro/hello.pro.renamed
+Name: hello.pro.renamed
+URL: $ROOT_URL/branches/dev/Share/ctrl/pro/hello.pro.renamed
+Repository Root: $REPOS_URL
+Revision: 8
+Node Kind: file
+Schedule: add
+#IF SVN1.8/9/10 Copied From URL: $ROOT_URL/branches/dev/Share/ed_ren/pro/hello.pro.renamed
+#IF SVN1.14 Copied From URL: $ROOT_URL/branches/dev/Share/ctrl/pro/hello.pro
+Copied From Rev: 8
+#IF SVN1.14 Moved From: pro/hello.pro
+Last Changed Author: $LOGNAME
+#IF SVN1.8/9/10 Last Changed Rev: 8
+#IF SVN1.14 Last Changed Rev: 6
+
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, rename, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-discard-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+#IF SVN1.14 <<<<<<< .working
+#IF SVN1.14 Local contents (1)
+#IF SVN1.14 ||||||| .old
+#IF SVN1.14 =======
+Merge contents (1)
+Merge contents (2)
+#IF SVN1.14 >>>>>>> .new
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, rename, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ed_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+#IF SVN1.8/9/10 [info] pro/hello.pro: in tree conflict.
+#IF SVN1.14 [info] pro/hello.pro.renamed: in text conflict.
+#IF SVN1.8/9/10 Locally: edited.
+#IF SVN1.8/9/10 Externally: renamed to pro/hello.pro.renamed.
+#IF SVN1.8/9/10 Answer (y) to keep the file.
+#IF SVN1.8/9/10 Answer (n) to accept the external rename.
+#IF SVN1.8/9/10 You can then merge in changes.
+#IF SVN1.8/9/10 Keep the local version?
+#IF SVN1.8/9/10 Enter "y" or "n" (or just press <return> for "n") diff3 pro/hello.pro.working pro/hello.pro.merge-left.r1 pro/hello.pro.merge-right.r8
+#IF SVN1.14 diff3 $PWD/pro/hello.pro.renamed.3.tmp $PWD/pro/hello.pro.renamed.tmp $PWD/pro/hello.pro.renamed.2.tmp
+====
+1:3c
+  Local contents (1)
+2:2a
+3:3,4c
+  Merge contents (1)
+  Merge contents (2)
+#IF SVN1.8/9/10 Reverted 'pro/hello.pro.renamed'
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10 Tree conflict at 'pro/hello.pro' marked as resolved.
+#IF SVN1.14 Run "svn resolve --accept working pro/hello.pro.renamed"?
+#IF SVN1.14 Enter "y" or "n" (or just press <return> for "n") Merge conflicts in 'pro/hello.pro.renamed' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, rename, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+#IF SVN1.14 D       pro/hello.pro
+#IF SVN1.14         > moved to pro/hello.pro.renamed
+#IF SVN1.14 A  +    pro/hello.pro.renamed
+#IF SVN1.14         > moved from pro/hello.pro
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: edit, rename, keep local (info)
+TEST_KEY=$TEST_KEY_BASE-keep-info
+run_pass "$TEST_KEY" svn info pro/hello.pro
+sed -i "/Date:\|Updated:\|UUID:\|Checksum\|Relative URL:\|Working Copy Root Path:/d" $TEST_DIR/"$TEST_KEY.out"
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Path: pro/hello.pro
+Name: hello.pro
+URL: $ROOT_URL/branches/dev/Share/ctrl/pro/hello.pro
+Repository Root: $REPOS_URL
+Revision: 8
+Node Kind: file
+#IF SVN1.8/9/10 Schedule: normal
+#IF SVN1.14 Schedule: delete
+#IF SVN1.14 Moved To: pro/hello.pro.renamed
+Last Changed Author: $LOGNAME
+Last Changed Rev: 6
+
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: delete, rename, keep local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-cat
+if [[ $SVN_MINOR_VERSION == "1.14" ]]; then
+  run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+else
+  run_pass "$TEST_KEY" cat pro/hello.pro
+fi
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+#IF SVN1.14 <<<<<<< .working
+Local contents (1)
+#IF SVN1.14 ||||||| .old
+#IF SVN1.14 =======
+#IF SVN1.14 Merge contents (1)
+#IF SVN1.14 Merge contents (2)
+#IF SVN1.14 >>>>>>> .new
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/06-tree-rename-delete.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/06-tree-rename-delete.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/06-tree-rename-delete.t	(revision 5095)
@@ -0,0 +1,114 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 15
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc ren_del $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, delete, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+svn rename -q pro/hello.pro pro/hello.pro.renamed
+svn commit -q -m "Renamed conflict file (local)"
+svn update -q
+echo "Local contents (1)" >>pro/hello.pro.renamed
+svn commit -q -m "Modified conflict file (local)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ren_del
+svn delete -q pro/hello.pro
+svn commit -q -m "Deleted conflict file  (merge)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_del >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: renamed to pro/hello.pro.renamed.
+Externally: deleted.
+Answer (y) to accept the local rename.
+Answer (n) to accept the external delete.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") D         pro/hello.pro.renamed
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, delete, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+D       pro/hello.pro.renamed
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, delete, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_del >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: renamed to pro/hello.pro.renamed.
+Externally: deleted.
+Answer (y) to accept the local rename.
+Answer (n) to accept the external delete.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, delete, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, delete, keep local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Local contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/07-tree-rename-edit.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/07-tree-rename-edit.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/07-tree-rename-edit.t	(revision 5095)
@@ -0,0 +1,176 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 18
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc ren_ed $REPOS_URL
+# Set a special (null) fcm-graphic-merge diff editor.
+export FCM_GRAPHIC_MERGE=fcm-dummy-diff
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, edit, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+svn rename -q pro/hello.pro pro/hello.pro.renamed
+svn commit -q -m "Renamed conflict file (local)"
+svn update -q
+echo "Local contents (1)" >>pro/hello.pro.renamed
+svn commit -q -m "Modified conflict file (local)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ren_ed
+echo "Merge contents (1)" >>pro/hello.pro
+svn commit -q -m "Modified conflict file  (merge)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_ed >/dev/null
+if [[ $SVN_MINOR_VERSION == "1.14" ]]; then
+  run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+else
+  run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+fi
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+#IF SVN1.8/9/10 [info] pro/hello.pro: in tree conflict.
+#IF SVN1.14 [info] pro/hello.pro.renamed: in text conflict.
+#IF SVN1.8/9/10 Locally: renamed to pro/hello.pro.renamed.
+#IF SVN1.8/9/10 Externally: edited.
+#IF SVN1.8/9/10 Answer (y) to accept the local rename.
+#IF SVN1.8/9/10 Answer (n) to keep the file.
+#IF SVN1.8/9/10 You can then merge in changes.
+#IF SVN1.8/9/10 Keep the local version?
+#IF SVN1.8/9/10 Enter "y" or "n" (or just press <return> for "n") diff3 pro/hello.pro.renamed.working pro/hello.pro.renamed.merge-left.r1 pro/hello.pro.renamed.merge-right.r8
+#IF SVN1.14 diff3 $PWD/pro/hello.pro.renamed.3.tmp $PWD/pro/hello.pro.renamed.tmp $PWD/pro/hello.pro.renamed.2.tmp
+====
+1:3c
+  Local contents (1)
+2:2a
+3:3c
+  Merge contents (1)
+#IF SVN1.8/9/10 A         pro/hello.pro
+#IF SVN1.8/9/10 D         pro/hello.pro.renamed
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10 Tree conflict at 'pro/hello.pro' marked as resolved.
+#IF SVN1.14 Run "svn resolve --accept working pro/hello.pro.renamed"?
+#IF SVN1.14 Enter "y" or "n" (or just press <return> for "n") Merge conflicts in 'pro/hello.pro.renamed' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, edit, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+#IF SVN1.8/9/10 A  +    pro/hello.pro
+#IF SVN1.8/9/10         > moved from pro/hello.pro.renamed
+#IF SVN1.8/9/10 D       pro/hello.pro.renamed
+#IF SVN1.8/9/10         > moved to pro/hello.pro
+#IF SVN1.14 M       pro/hello.pro.renamed
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, edit, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-discard-cat
+if [[ $SVN_MINOR_VERSION == "1.14" ]]; then
+  run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+else
+  run_pass "$TEST_KEY" cat pro/hello.pro
+fi
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+#IF SVN1.14 <<<<<<< .working
+Local contents (1)
+#IF SVN1.14 ||||||| .old
+#IF SVN1.14 =======
+#IF SVN1.14 Merge contents (1)
+#IF SVN1.14 >>>>>>> .new
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, edit, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_ed >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+#IF SVN1.8/9/10 [info] pro/hello.pro: in tree conflict.
+#IF SVN1.14 [info] pro/hello.pro.renamed: in text conflict.
+#IF SVN1.8/9/10 Locally: renamed to pro/hello.pro.renamed.
+#IF SVN1.8/9/10 Externally: edited.
+#IF SVN1.8/9/10 Answer (y) to accept the local rename.
+#IF SVN1.8/9/10 Answer (n) to keep the file.
+#IF SVN1.8/9/10 You can then merge in changes.
+#IF SVN1.8/9/10 Keep the local version?
+#IF SVN1.8/9/10 Enter "y" or "n" (or just press <return> for "n") diff3 pro/hello.pro.renamed.working pro/hello.pro.renamed.merge-left.r1 pro/hello.pro.renamed.merge-right.r8
+#IF SVN1.14 diff3 $PWD/pro/hello.pro.renamed.3.tmp $PWD/pro/hello.pro.renamed.tmp $PWD/pro/hello.pro.renamed.2.tmp
+====
+1:3c
+  Local contents (1)
+2:2a
+3:3c
+  Merge contents (1)
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10 Tree conflict at 'pro/hello.pro' marked as resolved.
+#IF SVN1.14 Run "svn resolve --accept working pro/hello.pro.renamed"?
+#IF SVN1.14 Enter "y" or "n" (or just press <return> for "n") Merge conflicts in 'pro/hello.pro.renamed' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, edit, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+#IF SVN1.14 M       pro/hello.pro.renamed
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, edit, keep local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+#IF SVN1.14 <<<<<<< .working
+Local contents (1)
+#IF SVN1.14 ||||||| .old
+#IF SVN1.14 =======
+#IF SVN1.14 Merge contents (1)
+#IF SVN1.14 >>>>>>> .new
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/08-tree-rename-rename-diff.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/08-tree-rename-rename-diff.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/08-tree-rename-rename-diff.t	(revision 5095)
@@ -0,0 +1,148 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+[[ $SVN_VERSION == "1.13.0" ]] && skip_all "Tests do not work with svn 1.13.0"
+tests 18
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc ren_ren $REPOS_URL
+# Set a special (null) fcm-graphic-merge diff editor.
+export FCM_GRAPHIC_MERGE=fcm-dummy-diff
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, diff rename, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+svn rename -q pro/hello.pro pro/hello.pro.renamed-local
+svn commit -q -m "Renamed conflict file (local)"
+svn update -q
+echo "Local contents (1)" >>pro/hello.pro.renamed-local
+svn commit -q -m "Modified conflict file (local)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ren_ren
+echo "Merge contents (1)" >>pro/hello.pro
+svn commit -q -m "Modified conflict file  (merge)"
+svn update -q
+svn rename -q pro/hello.pro pro/hello.pro.renamed-merge
+svn commit -q -m "Renamed conflict file (merge)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: renamed to pro/hello.pro.renamed-local.
+Externally: renamed to pro/hello.pro.renamed-merge.
+Answer (y) to accept the local rename.
+Answer (n) to accept the external rename.
+You can then merge in changes.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") diff3 pro/hello.pro.renamed-merge.working pro/hello.pro.renamed-merge.merge-left.r1 pro/hello.pro.renamed-merge.merge-right.r9
+====
+1:3c
+  Local contents (1)
+2:2a
+3:3c
+  Merge contents (1)
+D         pro/hello.pro.renamed-local
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, diff rename, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+D       pro/hello.pro.renamed-local
+A  +    pro/hello.pro.renamed-merge
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, diff rename, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-discard-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed-merge
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Merge contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, diff rename, keep local
+TEST_KEY=$TEST_KEY_BASE-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: renamed to pro/hello.pro.renamed-local.
+Externally: renamed to pro/hello.pro.renamed-merge.
+Answer (y) to accept the local rename.
+Answer (n) to accept the external rename.
+You can then merge in changes.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") diff3 pro/hello.pro.renamed-local.working pro/hello.pro.renamed-local.merge-left.r1 pro/hello.pro.renamed-local.merge-right.r9
+====
+1:3c
+  Local contents (1)
+2:2a
+3:3c
+  Merge contents (1)
+Reverted 'pro/hello.pro.renamed-merge'
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, diff rename, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, diff rename, keep local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed-local
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Local contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/09-tree-rename-rename-same.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/09-tree-rename-rename-same.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/09-tree-rename-rename-same.t	(revision 5095)
@@ -0,0 +1,190 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (tree conflict mode).
+# TODO: The behaviour exhibited by fcm conflicts in this file is wrong.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+[[ $SVN_VERSION == "1.13.0" ]] && skip_all "Tests do not work with svn 1.13.0"
+tests 27
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch ctrl $REPOS_URL
+init_branch_wc ren_ren $REPOS_URL
+# Set a special (null) fcm-graphic-merge diff editor.
+export FCM_GRAPHIC_MERGE=fcm-dummy-diff
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, discard local
+TEST_KEY=$TEST_KEY_BASE-discard
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+svn rename -q pro/hello.pro pro/hello.pro.renamed
+svn commit -q -m "Renamed conflict file (local)"
+svn update -q
+echo "Local contents (1)" >>pro/hello.pro.renamed
+svn commit -q -m "Modified conflict file (local)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ren_ren
+echo "Merge contents (1)" >>pro/hello.pro
+svn commit -q -m "Modified conflict file  (merge)"
+svn update -q
+svn rename -q pro/hello.pro pro/hello.pro.renamed
+svn commit -q -m "Renamed conflict file (merge)"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/ctrl
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: renamed to pro/hello.pro.renamed.
+Externally: deleted.
+Answer (y) to accept the local rename.
+Answer (n) to accept the external delete.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") D         pro/hello.pro.renamed
+#IF SVN1.8/9 Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+D       pro/hello.pro.renamed
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-discard-keep-cat
+run_fail "$TEST_KEY" cat pro/hello.pro.renamed
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<__ERR__
+cat: pro/hello.pro.renamed: No such file or directory
+__ERR__
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, keep local, discard local
+TEST_KEY=$TEST_KEY_BASE-keep-discard
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+n
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: renamed to pro/hello.pro.renamed.
+Externally: deleted.
+Answer (y) to accept the local rename.
+Answer (n) to accept the external delete.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+[info] pro/hello.pro.renamed: in tree conflict.
+Locally: added.
+Externally: added.
+Answer (y) to keep the local file filename.
+Answer (n) to keep the external file filename.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro.renamed'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro.renamed' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, keep local, discard local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-discard-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+M       pro/hello.pro.renamed
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, keep local, discard local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-discard-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Merge contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/ctrl $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, keep local, keep local
+TEST_KEY=$TEST_KEY_BASE-keep-keep
+fcm merge --non-interactive $ROOT_URL/branches/dev/Share/ren_ren >/dev/null
+run_pass "$TEST_KEY" fcm conflicts <<__IN__
+y
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] pro/hello.pro: in tree conflict.
+Locally: renamed to pro/hello.pro.renamed.
+Externally: deleted.
+Answer (y) to accept the local rename.
+Answer (n) to accept the external delete.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+[info] pro/hello.pro.renamed: in tree conflict.
+Locally: added.
+Externally: added.
+Answer (y) to keep the local file filename.
+Answer (n) to keep the external file filename.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro.renamed'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro.renamed' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, keep local, keep local (status)
+TEST_KEY=$TEST_KEY_BASE-keep-keep-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm conflicts: rename, same rename, keep local, keep local (cat)
+TEST_KEY=$TEST_KEY_BASE-keep-keep-cat
+run_pass "$TEST_KEY" cat pro/hello.pro.renamed
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+PRO HELLO
+END
+Local contents (1)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/10-text.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/10-text.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/10-text.t	(revision 5095)
@@ -0,0 +1,145 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm conflicts" (text conflict following merge).
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 11
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-merge
+export SVN_EDITOR="sed -i 1i\foo"
+echo "The End" >> lib/python/info/poems.py
+svn commit -m "Finish off the poem" -q
+svn update -q
+run_pass "$TEST_KEY" fcm merge --non-interactive $ROOT_URL/branches/dev/Share/merge1
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-merge-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<'__OUT__'
+ M      .
+?       lib/python/info/poems.py.merge-left.r1
+?       lib/python/info/poems.py.merge-right.r5
+?       lib/python/info/poems.py.working
+?       unversioned_file
+A  +    added_directory
+A  +    added_file
+A  +    module/tree_conflict_file
+C       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+M       subroutine/hello_sub_dummy.h
+Summary of conflicts:
+  Text conflicts: 1
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-merge-conflicts
+export FCM_GRAPHIC_MERGE=fcm-dummy-diff
+run_pass "$TEST_KEY" fcm conflicts <<'__IN__'
+y
+y
+__IN__
+file_cmp_filtered "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] lib/python/info/poems.py: in text conflict.
+diff3 $PWD/lib/python/info/poems.py.working $PWD/lib/python/info/poems.py.merge-left.r1 $PWD/lib/python/info/poems.py.merge-right.r5
+====3
+1:1,2c
+2:1,2c
+  #!/usr/bin/env python
+  # -*- coding: utf-8 -*-
+3:0a
+====3
+1:6c
+2:6c
+  It needs a doctor for its eyes,
+3:4c
+  It needs a doctor FOR its eyes,
+====3
+1:8,9c
+2:8,9c
+  However, if you feel inclined
+  To get one (to improve your mind,
+3:6,8c
+  However, if you feel INclINed
+  To get one (
+  to improve your mINd,
+====3
+1:12c
+2:12c
+  And when it flies into a rage
+3:11c
+  And when it flies INto a rage
+====3
+1:14c
+2:14c
+  I had an aunt in Yucatan
+3:13c
+  I had an aunt IN Yucatan
+====3
+1:16c
+2:16c
+  And kept it for a pet.
+3:15c
+  And kept it FOR a pet.
+====3
+1:19c
+2:19c
+  The Snake is living yet.
+3:18c
+  The Snake is livINg yet.
+====
+1:24,25c
+  print "\n",  __doc__
+  The End
+2:24c
+  print "\n",  __doc__
+3:23c
+  prINt "\n",  __doc__
+Run "svn resolve --accept working lib/python/info/poems.py"?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'lib/python/info/poems.py'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Merge conflicts in 'lib/python/info/poems.py' marked as resolved.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-merge-conflicts-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<'__OUT__'
+ M      .
+?       unversioned_file
+A  +    added_directory
+A  +    added_file
+A  +    module/tree_conflict_file
+M       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+M       subroutine/hello_sub_dummy.h
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/11-tree-edit-replace.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/11-tree-edit-replace.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/11-tree-edit-replace.t	(revision 5095)
@@ -0,0 +1,80 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Tree conflict: local file edit, incoming file replace upon merge
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 6
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch replace "${REPOS_URL}"
+init_branch_wc edit "${REPOS_URL}"
+cd "${TEST_DIR}/wc"
+svn switch -q "${ROOT_URL}/branches/dev/Share/replace"
+HELLO="$(<'pro/hello.pro')"
+svn delete -q 'pro/hello.pro'
+svn commit -q -m 'Remove local copy of conflict file'
+echo 'Replace contents (1)' >'pro/hello.pro'
+svn add 'pro/hello.pro'
+svn commit -q -m 'Replace local copy of conflict file'
+svn switch -q "${ROOT_URL}/branches/dev/Share/edit"
+echo 'Merge contents (1)' >>'pro/hello.pro'
+svn commit -q -m 'Modified and renamed merge copy of conflict file'
+svn update -q
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-leip-y"
+fcm merge --non-interactive "${ROOT_URL}/branches/dev/Share/replace" >'/dev/null'
+run_pass "${TEST_KEY}" fcm conflicts <<<'y'
+sed -i "/^Resolved conflicted state of 'pro\/hello.pro'$/d" \
+    ${TEST_DIR}/"${TEST_KEY}.out"
+file_cmp_filtered "${TEST_KEY}.out" "${TEST_KEY}.out" <<'__OUT__'
+[info] pro/hello.pro: in tree conflict.
+Locally: edited.
+Externally: replaced.
+Answer (y) to keep the file.
+Answer (n) to accept the external replace.
+Keep the local version?
+#IF SVN1.8/9 Enter "y" or "n" (or just press <return> for "n") Resolved conflicted state of 'pro/hello.pro'
+#IF SVN1.10/14 Enter "y" or "n" (or just press <return> for "n") Tree conflict at 'pro/hello.pro' marked as resolved.
+__OUT__
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-leip-n"
+svn revert -R -q .
+rm '#commit_message#'
+fcm merge --non-interactive "${ROOT_URL}/branches/dev/Share/replace" >'/dev/null'
+run_pass "${TEST_KEY}" fcm conflicts <<<'n'
+sed -i "/^Resolved conflicted state of 'pro\/hello.pro'$/d" \
+    ${TEST_DIR}/"${TEST_KEY}.out"
+file_cmp "${TEST_KEY}.out" "${TEST_KEY}.out" <<'__OUT__'
+[info] pro/hello.pro: in tree conflict.
+Locally: edited.
+Externally: replaced.
+Answer (y) to keep the file.
+Answer (n) to accept the external replace.
+Keep the local version?
+Enter "y" or "n" (or just press <return> for "n") D         pro/hello.pro
+A         pro/hello.pro
+__OUT__
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-conflicts/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-diff/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-diff/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-diff/00-simple.t	(revision 5095)
@@ -0,0 +1,149 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm diff".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 3
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_branch_wc branch_test $REPOS_URL
+cd $TEST_DIR/wc
+FILE_LIST="./lib/python/info/__init__.py
+./lib/python/info/poems.py
+./module/hello_constants.f90
+./module/hello_constants.inc
+./module/hello_constants_dummy.inc
+"
+for FILE in $FILE_LIST; do 
+    sed -i "s/for/FOR/g; s/fi/end if/g; s/in/IN/g;" $FILE
+    sed -i "/#/d; /^ *!/d" $FILE
+    sed -i "s/!/!!/g; s/q/\nq/g; s/[(]/(\n/g" $FILE
+done
+FILE_DIR=$(dirname $FILE)
+svn copy -q $FILE added_file
+svn copy -q $FILE_DIR added_directory
+svn delete --force -q $FILE_DIR
+#-------------------------------------------------------------------------------
+# Tests fcm branch-diff
+TEST_KEY=$TEST_KEY_BASE-fcm-diff
+run_pass "$TEST_KEY" fcm diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<'__OUT__'
+
+Index: added_directory/hello_constants.f90
+===================================================================
+--- added_directory/hello_constants.f90	(revision 4)
++++ added_directory/hello_constants.f90	(working copy)
+@@ -1,5 +1,5 @@
+ MODULE Hello_Constants
+ 
+-INCLUDE 'hello_constants_dummy.inc'
++INCLUDE 'hello_constants_dummy.INc'
+ 
+ END MODULE Hello_Constants
+Index: added_directory/hello_constants.inc
+===================================================================
+--- added_directory/hello_constants.inc	(revision 4)
++++ added_directory/hello_constants.inc	(working copy)
+@@ -1 +1,2 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
++CHARACTER (
++LEN=80), PARAMETER :: hello_strINg = 'Hello Earth!!'
+Index: added_directory/hello_constants_dummy.inc
+===================================================================
+--- added_directory/hello_constants_dummy.inc	(revision 4)
++++ added_directory/hello_constants_dummy.inc	(working copy)
+@@ -1 +1 @@
+-INCLUDE 'hello_constants.inc'
++INCLUDE 'hello_constants.INc'
+Index: added_file
+===================================================================
+--- added_file	(revision 4)
++++ added_file	(working copy)
+@@ -1 +1 @@
+-INCLUDE 'hello_constants.inc'
++INCLUDE 'hello_constants.INc'
+Index: lib/python/info/poems.py
+===================================================================
+--- lib/python/info/poems.py	(revision 4)
++++ lib/python/info/poems.py	(working copy)
+@@ -1,24 +1,23 @@
+-#!/usr/bin/env python
+-# -*- coding: utf-8 -*-
+ """The Python, by Hilaire Belloc
+ 
+ A Python I should not advise,--
+-It needs a doctor for its eyes,
++It needs a doctor FOR its eyes,
+ And has the measles yearly.
+-However, if you feel inclined
+-To get one (to improve your mind,
++However, if you feel INclINed
++To get one (
++to improve your mINd,
+ And not from fashion merely),
+ Allow no music near its cage;
+-And when it flies into a rage
++And when it flies INto a rage
+ Chastise it, most severely.
+-I had an aunt in Yucatan
++I had an aunt IN Yucatan
+ Who bought a Python from a man
+-And kept it for a pet.
++And kept it FOR a pet.
+ She died, because she never knew
+ These simple little rules and few;--
+-The Snake is living yet.
++The Snake is livINg yet.
+ """
+ 
+ import this
+ 
+-print "\n",  __doc__
++prINt "\n",  __doc__
+Index: module/hello_constants.f90
+===================================================================
+--- module/hello_constants.f90	(revision 4)
++++ module/hello_constants.f90	(working copy)
+@@ -1,5 +0,0 @@
+-MODULE Hello_Constants
+-
+-INCLUDE 'hello_constants_dummy.inc'
+-
+-END MODULE Hello_Constants
+Index: module/hello_constants.inc
+===================================================================
+--- module/hello_constants.inc	(revision 4)
++++ module/hello_constants.inc	(working copy)
+@@ -1 +0,0 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
+Index: module/hello_constants_dummy.inc
+===================================================================
+--- module/hello_constants_dummy.inc	(revision 4)
++++ module/hello_constants_dummy.inc	(working copy)
+@@ -1 +0,0 @@
+-INCLUDE 'hello_constants.inc'
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-diff/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-diff/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-diff/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic.t	(revision 5095)
@@ -0,0 +1,161 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "fcm-install-svn-hook".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+if ! which svnadmin 1>/dev/null 2>/dev/null; then
+    skip_all 'svnadmin not available'
+fi
+tests 173
+#-------------------------------------------------------------------------------
+FCM_REAL_HOME=$(readlink -f "$FCM_HOME")
+TODAY=$(date -u +%Y%m%d)
+mkdir -p conf/
+export FCM_CONF_PATH="$PWD/conf"
+cat >conf/admin.cfg <<__CONF__
+fcm_site_home=$PWD
+svn_group=
+svn_live_dir=$PWD/svn-repos
+svn_project_suffix=
+__CONF__
+cat >hooks-env <<__CONF__
+[default]
+FCM_HOME=$FCM_REAL_HOME
+FCM_SITE_HOME=$PWD
+FCM_SVN_HOOK_ADMIN_EMAIL=$USER
+FCM_SVN_HOOK_COMMIT_DUMP_DIR=/var/svn/dumps
+FCM_SVN_HOOK_TRAC_ROOT_DIR=/srv/trac
+TZ=UTC
+__CONF__
+#-------------------------------------------------------------------------------
+# Live directory does not exist
+TEST_KEY="$TEST_KEY_BASE-no-live-dir"
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" /dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" /dev/null
+#-------------------------------------------------------------------------------
+# Project does not exist
+TEST_KEY="$TEST_KEY_BASE-no-project"
+run_fail "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook" foo
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" /dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERR__'
+foo: not found
+__ERR__
+#-------------------------------------------------------------------------------
+# Live directory is empty
+TEST_KEY="$TEST_KEY_BASE-empty"
+mkdir svn-repos
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" /dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" /dev/null
+#-------------------------------------------------------------------------------
+run_tests() {
+    # Create repository and add content if necessary
+    rm -fr svn-repos/foo
+    svnadmin create svn-repos/foo
+    if [[ -d svn-import ]]; then
+        svn import -q -m't' svn-import file://$PWD/svn-repos/foo
+    fi
+    # Hooks before
+    local HOOK_TMPLS=$(ls svn-repos/foo/hooks/*)
+    # Install
+    run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook" "$@"
+    # Hooks env
+    file_cmp "$TEST_KEY-hooks-env" svn-repos/foo/conf/hooks-env hooks-env
+    # Make sure all hooks are installed
+    local FILE=
+    for FILE in $(cd "$FCM_HOME/etc/svn-hooks" && ls); do
+        file_cmp "$TEST_KEY-$FILE" \
+            "$FCM_HOME/etc/svn-hooks/$FILE" "svn-repos/foo/hooks/$FILE"
+        file_test "$TEST_KEY-$FILE-chmod" "svn-repos/foo/hooks/$FILE" -x
+        file_test "$TEST_KEY-$FILE.log.$TODAY" \
+            "svn-repos/foo/log/$FILE.log.$TODAY"
+        readlink "svn-repos/foo/log/$FILE.log" >"$TEST_KEY-$FILE.log.link"
+        file_cmp "$TEST_KEY-$FILE.log" \
+            "$TEST_KEY-$FILE.log.link" <<<"$FILE.log.$TODAY"
+    done
+    # Hooks after
+    if [[ "$@" == *--clean* ]]; then
+        run_fail "$TEST_KEY-ls-tmpl" ls $HOOK_TMPLS
+    else
+        run_pass "$TEST_KEY-ls-tmpl" ls $HOOK_TMPLS
+    fi
+    # STDOUT and STDERR
+    date2datefmt "$TEST_KEY.out" >"$TEST_KEY.out.parsed"
+    m4 -DFCM_REAL_HOME=$FCM_REAL_HOME -DPWD=$PWD -DTODAY=$TODAY \
+        "$TEST_SOURCE_DIR/$TEST_KEY_BASE/$NAME.out" >"$TEST_KEY.out.exp"
+    file_cmp "$TEST_KEY.out" "$TEST_KEY.out.parsed" "$TEST_KEY.out.exp"
+    file_cmp "$TEST_KEY.err" "$TEST_KEY.err" /dev/null
+    # Run command a second time, should no longer install logs
+    run_pass "$TEST_KEY-2" "$FCM_HOME/sbin/fcm-install-svn-hook" "$@"
+    date2datefmt "$TEST_KEY-2.out" >"$TEST_KEY-2.out.parsed"
+    m4 -DFCM_REAL_HOME=$FCM_REAL_HOME -DPWD=$PWD \
+        "$TEST_SOURCE_DIR/$TEST_KEY_BASE/$NAME-2.out" >"$TEST_KEY-2.out.exp"
+    file_cmp "$TEST_KEY-2.out" "$TEST_KEY-2.out.parsed" "$TEST_KEY-2.out.exp"
+}
+
+# New install, single repository
+TEST_KEY="$TEST_KEY_BASE-new"
+NAME=new run_tests
+TEST_KEY="$TEST_KEY_BASE-new-foo"
+NAME=new run_tests foo
+
+# Clean install, single repository
+TEST_KEY="$TEST_KEY_BASE-clean"
+NAME=clean run_tests --clean
+TEST_KEY="$TEST_KEY_BASE-clean-foo"
+NAME=clean run_tests --clean foo
+
+# New install, single repository, with svnperms.conf
+TEST_KEY="$TEST_KEY_BASE-svnperms.conf"
+mkdir -p 'svn-import'
+echo '[foo]' >'svn-import/svnperms.conf'
+NAME='svnperms-conf' run_tests
+file_cmp "$TEST_KEY-cmp" \
+    'svn-repos/foo/hooks/svnperms.conf' 'svn-import/svnperms.conf'
+
+# New install, single repository, with commit.conf
+TEST_KEY="$TEST_KEY_BASE-commit.conf"
+{
+    echo 'notify-branch-owner'
+    echo 'verify-branch-owner'
+} >'svn-import/commit.conf'
+NAME='commit-conf' run_tests
+file_cmp "$TEST_KEY-cmp" \
+    'svn-repos/foo/hooks/commit.conf' 'svn-import/commit.conf'
+
+# New install, single repository, with commit.conf and site override
+TEST_KEY="$TEST_KEY_BASE-commit.conf-site-override"
+{
+    echo 'notify-branch-owner'
+    echo 'verify-branch-owner'
+} >'svn-import/commit.conf'
+mkdir -p 'svn-hooks/foo'
+{
+    echo '# This is the override'
+    echo '# It is actually empty'
+} >'svn-hooks/foo/commit.conf'
+NAME='commit-conf-site-override' run_tests
+file_cmp "$TEST_KEY-cmp" \
+    'svn-repos/foo/hooks/commit.conf' 'svn-hooks/foo/commit.conf'
+rm -fr 'svn-hooks'
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/clean-2.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/clean-2.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/clean-2.out	(revision 5095)
@@ -0,0 +1,1 @@
+link new-2.out
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/clean.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/clean.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/clean.out	(revision 5095)
@@ -0,0 +1,17 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/post-commit.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/post-lock.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/post-revprop-change.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/post-unlock.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/pre-commit.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/pre-lock.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/pre-revprop-change.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/pre-unlock.tmpl
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/hooks/start-commit.tmpl
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-2.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-2.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-2.out	(revision 5095)
@@ -0,0 +1,6 @@
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/commit.conf <- ^/commit.conf
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/svnperms.conf <- ^/svnperms.conf
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-site-override-2.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-site-override-2.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-site-override-2.out	(revision 5095)
@@ -0,0 +1,6 @@
+YYYY-mm-ddTHH:MM:SSZ: copy PWD/svn-hooks/foo/commit.conf to PWD/svn-repos/foo/hooks/commit.conf
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/svnperms.conf <- ^/svnperms.conf
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-site-override.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-site-override.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf-site-override.out	(revision 5095)
@@ -0,0 +1,10 @@
+YYYY-mm-ddTHH:MM:SSZ: copy PWD/svn-hooks/foo/commit.conf to PWD/svn-repos/foo/hooks/commit.conf
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/svnperms.conf <- ^/svnperms.conf
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/commit-conf.out	(revision 5095)
@@ -0,0 +1,10 @@
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/commit.conf <- ^/commit.conf
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/svnperms.conf <- ^/svnperms.conf
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/new-2.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/new-2.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/new-2.out	(revision 5095)
@@ -0,0 +1,4 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/new.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/new.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/new.out	(revision 5095)
@@ -0,0 +1,8 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/svnperms-conf-2.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/svnperms-conf-2.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/svnperms-conf-2.out	(revision 5095)
@@ -0,0 +1,5 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/svnperms.conf <- ^/svnperms.conf
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/svnperms-conf.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/svnperms-conf.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/00-basic/svnperms-conf.out	(revision 5095)
@@ -0,0 +1,9 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_REAL_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: install PWD/svn-repos/foo/hooks/svnperms.conf <- ^/svnperms.conf
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log.t	(revision 5095)
@@ -0,0 +1,191 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests housekeep hook logs functionalities provided by "fcm-install-svn-hook".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+if ! which svnadmin 1>/dev/null 2>/dev/null; then
+    skip_all 'svnadmin not available'
+fi
+tests 14
+#-------------------------------------------------------------------------------
+FCM_REAL_HOME=$(readlink -f "$FCM_HOME")
+TODAY=$(date -u +%Y%m%d)
+mkdir -p conf/ svn-repos/
+export FCM_CONF_PATH="$PWD/conf"
+cat >conf/admin.cfg <<__CONF__
+svn_group=
+svn_live_dir=$PWD/svn-repos
+svn_project_suffix=
+__CONF__
+#-------------------------------------------------------------------------------
+# Newly created logs
+svnadmin create svn-repos/bar
+svnadmin create svn-repos/foo
+
+# 1st run, create logs
+KEY="0-cmd0"
+TEST_KEY="$TEST_KEY_BASE-$KEY"
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+date2datefmt "$TEST_KEY.out" >"$TEST_KEY.out.parsed"
+m4 -DFCM_HOME="$FCM_REAL_HOME" -DPWD="$PWD" -DTODAY="$TODAY" \
+    "$TEST_SOURCE_DIR/$TEST_KEY_BASE/$KEY.out" >"$TEST_KEY.out.exp"
+diff -u "$TEST_KEY.out.parsed" "$TEST_KEY.out.exp" >&2
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out.parsed" "$TEST_KEY.out.exp"
+
+# Add something to logs between runs
+for FILE in svn-repos/{foo,bar}/log/*.log; do
+    echo "$FILE: time passes, and contents were written to me." >"$FILE"
+done
+sha1sum svn-repos/{foo,bar}/log/*.log >logs.shalsum
+#-------------------------------------------------------------------------------
+# 2nd run on same day, should leave logs alone
+KEY="0-cmd1"
+TEST_KEY="$TEST_KEY_BASE-$KEY"
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+date2datefmt "$TEST_KEY.out" >"$TEST_KEY.out.parsed"
+m4 -DFCM_HOME="$FCM_REAL_HOME" -DPWD="$PWD" -DTODAY="$TODAY" \
+    "$TEST_SOURCE_DIR/$TEST_KEY_BASE/$KEY.out" >"$TEST_KEY.out.exp"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out.parsed" "$TEST_KEY.out.exp"
+# Logs should not be modified
+run_pass "$TEST_KEY.sha1sum" sha1sum -c logs.shalsum
+#-------------------------------------------------------------------------------
+# Pretend that logs were created 3 days ago
+TEST_KEY="$TEST_KEY_BASE-3"
+DATE_P3D=$(date --date='3 days ago' +%Y%m%d)
+
+# Add something to logs
+# Pretend that they were created 3 days ago
+for FILE in svn-repos/{foo,bar}/log/*.log; do
+    echo "$FILE: time flies in the world of testing." >>"$FILE"
+    NAME=$(readlink "$FILE")
+    mv "$(dirname $FILE)/$NAME" "$FILE.$DATE_P3D"
+    ln -f -s "$(basename $FILE).$DATE_P3D" "$FILE"
+done
+sha1sum svn-repos/{foo,bar}/log/*.log >logs.shalsum
+
+# Run with logs created 3 days ago
+# STDOUT should be identical to "0-cmd1".
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+date2datefmt "$TEST_KEY.out" >"$TEST_KEY.out.parsed"
+m4 -DFCM_HOME="$FCM_REAL_HOME" -DPWD="$PWD" -DTODAY="$TODAY" \
+    "$TEST_SOURCE_DIR/$TEST_KEY_BASE/0-cmd1.out" >"$TEST_KEY.out.exp"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out.parsed" "$TEST_KEY.out.exp"
+
+# Logs should not be modified
+run_pass "$TEST_KEY.sha1sum" sha1sum -c logs.shalsum
+#-------------------------------------------------------------------------------
+# Pretend that logs were created 7 days ago
+TEST_KEY="$TEST_KEY_BASE-7"
+DATE_P7D=$(date --date='7 days ago' +%Y%m%d)
+
+# Add something to logs
+# Pretend that they were created 7 days ago
+for FILE in svn-repos/{foo,bar}/log/*.log; do
+    echo "$FILE: time continues to fly in the world of testing." >>"$FILE"
+    NAME=$(readlink "$FILE")
+    mv "$(dirname $FILE)/$NAME" "$FILE.$DATE_P7D"
+    ln -f -s "$(basename $FILE).$DATE_P7D" "$FILE"
+done
+sha1sum svn-repos/{foo,bar}/log/*.log >logs.shalsum
+
+# Run with logs created 7 days ago, should gzip old logs and create new ones
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+date2datefmt "$TEST_KEY.out" >"$TEST_KEY.out.parsed"
+m4 -DFCM_HOME="$FCM_REAL_HOME" -DPWD="$PWD" -DTODAY="$TODAY" \
+    -DDATE_P7D="$DATE_P7D" \
+    "$TEST_SOURCE_DIR/$TEST_KEY_BASE/7-cmd.out" >"$TEST_KEY.out.exp"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out.parsed" "$TEST_KEY.out.exp"
+
+# Unzip old logs and check that their contents are unchanged
+mkdir -p old-logs/svn-repos/{foo,bar}/log
+for FILE in svn-repos/{foo,bar}/log/*.log.*.gz; do
+    gunzip -c "$FILE" >old-logs/${FILE%.$DATE_P7D.gz}
+done
+cd old-logs
+run_pass "$TEST_KEY.sha1sum" sha1sum -c ../logs.shalsum
+cd "$OLDPWD"
+#-------------------------------------------------------------------------------
+# Pretend that logs were created between 7 to 28 days ago
+TEST_KEY="$TEST_KEY_BASE-28"
+DATE_P14D=$(date --date='14 days ago' +%Y%m%d)
+DATE_P21D=$(date --date='21 days ago' +%Y%m%d)
+DATE_P28D=$(date --date='28 days ago' +%Y%m%d)
+
+# Create fake logs
+rm -f svn-repos/{foo,bar}/log/*.log*
+for FILE in svn-repos/{foo,bar}/log/{pre,post}-commit.log; do
+    for DATE in $DATE_P14D $DATE_P21D $DATE_P28D; do
+        echo "$FILE $DATE whatever" >"$FILE.$DATE"
+        gzip "$FILE.$DATE"
+    done
+    echo "$FILE $DATE_P7D whatever" >"$FILE.$DATE_P7D"
+    ln -s $(basename "$FILE.$DATE_P7D") "$FILE"
+done
+for FILE in svn-repos/{foo,bar}/log/{pre,post}-revprop-change.log; do
+    echo "$FILE $DATE_P7D whatever" >"$FILE.$DATE_P7D"
+    ln -s $(basename "$FILE.$DATE_P7D") "$FILE"
+done
+
+# Run with logs created 7 to 28 days ago.
+# Should remove oldest and empty ones, gzip old ones and create new ones
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+date2datefmt "$TEST_KEY.out" >"$TEST_KEY.out.parsed"
+m4 -DFCM_HOME="$FCM_REAL_HOME" -DPWD="$PWD" -DTODAY="$TODAY" \
+    -DDATE_P7D="$DATE_P7D" -DDATE_P28D="$DATE_P28D" \
+    "$TEST_SOURCE_DIR/$TEST_KEY_BASE/28-cmd.out" >"$TEST_KEY.out.exp"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out.parsed" "$TEST_KEY.out.exp"
+ls svn-repos/{foo,bar}/log/*.log* | sort >"$TEST_KEY.ls.out"
+file_cmp "$TEST_KEY.ls" "$TEST_KEY.ls.out" <<__LIST__
+svn-repos/bar/log/post-commit.log
+svn-repos/bar/log/post-commit.log.$DATE_P21D.gz
+svn-repos/bar/log/post-commit.log.$DATE_P14D.gz
+svn-repos/bar/log/post-commit.log.$DATE_P7D.gz
+svn-repos/bar/log/post-commit.log.$TODAY
+svn-repos/bar/log/post-revprop-change.log
+svn-repos/bar/log/post-revprop-change.log.$DATE_P7D.gz
+svn-repos/bar/log/post-revprop-change.log.$TODAY
+svn-repos/bar/log/pre-commit.log
+svn-repos/bar/log/pre-commit.log.$DATE_P21D.gz
+svn-repos/bar/log/pre-commit.log.$DATE_P14D.gz
+svn-repos/bar/log/pre-commit.log.$DATE_P7D.gz
+svn-repos/bar/log/pre-commit.log.$TODAY
+svn-repos/bar/log/pre-revprop-change.log
+svn-repos/bar/log/pre-revprop-change.log.$DATE_P7D.gz
+svn-repos/bar/log/pre-revprop-change.log.$TODAY
+svn-repos/foo/log/post-commit.log
+svn-repos/foo/log/post-commit.log.$DATE_P21D.gz
+svn-repos/foo/log/post-commit.log.$DATE_P14D.gz
+svn-repos/foo/log/post-commit.log.$DATE_P7D.gz
+svn-repos/foo/log/post-commit.log.$TODAY
+svn-repos/foo/log/post-revprop-change.log
+svn-repos/foo/log/post-revprop-change.log.$DATE_P7D.gz
+svn-repos/foo/log/post-revprop-change.log.$TODAY
+svn-repos/foo/log/pre-commit.log
+svn-repos/foo/log/pre-commit.log.$DATE_P21D.gz
+svn-repos/foo/log/pre-commit.log.$DATE_P14D.gz
+svn-repos/foo/log/pre-commit.log.$DATE_P7D.gz
+svn-repos/foo/log/pre-commit.log.$TODAY
+svn-repos/foo/log/pre-revprop-change.log
+svn-repos/foo/log/pre-revprop-change.log.$DATE_P7D.gz
+svn-repos/foo/log/pre-revprop-change.log.$TODAY
+__LIST__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/0-cmd0.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/0-cmd0.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/0-cmd0.out	(revision 5095)
@@ -0,0 +1,16 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/bar/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/bar/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/bar/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/bar/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/bar/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/bar/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/bar/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/bar/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/0-cmd1.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/0-cmd1.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/0-cmd1.out	(revision 5095)
@@ -0,0 +1,8 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/bar/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/bar/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/bar/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/bar/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/28-cmd.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/28-cmd.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/28-cmd.out	(revision 5095)
@@ -0,0 +1,36 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/bar/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/bar/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/bar/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/bar/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/bar/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/post-commit.log.DATE_P28D.gz
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/post-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/bar/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/post-revprop-change.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/bar/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/pre-commit.log.DATE_P28D.gz
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/pre-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/bar/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/pre-revprop-change.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/post-commit.log.DATE_P28D.gz
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/post-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/post-revprop-change.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/pre-commit.log.DATE_P28D.gz
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/pre-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/pre-revprop-change.log.DATE_P7D
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/7-cmd.out
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/7-cmd.out	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/01-housekeep-log/7-cmd.out	(revision 5095)
@@ -0,0 +1,32 @@
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/bar/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/bar/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/bar/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/bar/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/bar/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/post-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/bar/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/post-revprop-change.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/bar/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/pre-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/bar/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/bar/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/bar/log/pre-revprop-change.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-commit to PWD/svn-repos/foo/hooks/post-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/post-revprop-change to PWD/svn-repos/foo/hooks/post-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-commit to PWD/svn-repos/foo/hooks/pre-commit
+YYYY-mm-ddTHH:MM:SSZ: copy FCM_HOME/etc/svn-hooks/pre-revprop-change to PWD/svn-repos/foo/hooks/pre-revprop-change
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-commit.log.TODAY -> PWD/svn-repos/foo/log/post-commit.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/post-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: post-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/post-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/post-revprop-change.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-commit.log.TODAY -> PWD/svn-repos/foo/log/pre-commit.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/pre-commit.log.DATE_P7D
+YYYY-mm-ddTHH:MM:SSZ: removing PWD/svn-repos/foo/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: creating symlink: pre-revprop-change.log.TODAY -> PWD/svn-repos/foo/log/pre-revprop-change.log
+YYYY-mm-ddTHH:MM:SSZ: gzip PWD/svn-repos/foo/log/pre-revprop-change.log.DATE_P7D
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/02-env.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/02-env.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/02-env.t	(revision 5095)
@@ -0,0 +1,58 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test "fcm-install-svn-hook", "hooks-env" installation.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+if ! which svnadmin 1>/dev/null 2>/dev/null; then
+    skip_all 'svnadmin not available'
+fi
+tests 2
+FCM_REAL_HOME=$(readlink -f "$FCM_HOME")
+mkdir conf svn-repos
+export FCM_CONF_PATH="$PWD/conf"
+cat >conf/admin.cfg <<__CONF__
+admin_email=robert.fitzroy@metoffice.gov.uk
+notification_from=notifications@localhost
+svn_dump_dir=$PWD/svn/dumps
+svn_group=
+svn_hook_path_env=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+svn_live_dir=$PWD/svn-repos
+svn_project_suffix=.svn
+trac_live_dir=$PWD/trac
+__CONF__
+svnadmin create svn-repos/foo.svn
+cat >hooks-env <<__CONF__
+[default]
+FCM_HOME=$FCM_REAL_HOME
+FCM_SVN_HOOK_ADMIN_EMAIL=robert.fitzroy@metoffice.gov.uk
+FCM_SVN_HOOK_COMMIT_DUMP_DIR=$PWD/svn/dumps
+FCM_SVN_HOOK_NOTIFICATION_FROM=notifications@localhost
+FCM_SVN_HOOK_REPOS_SUFFIX=.svn
+FCM_SVN_HOOK_TRAC_ROOT_DIR=$PWD/trac
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+TZ=UTC
+__CONF__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-install-svn-hook"
+file_cmp "$TEST_KEY.foo.hooks-env" svn-repos/foo.svn/conf/hooks-env hooks-env
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-install-svn-hook/test_header	(revision 5095)
@@ -0,0 +1,38 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+. $(dirname $0)/../lib/bash/test_header
+
+function file_cmp() {
+    local TEST_KEY=$1
+    local FILE_ACTUAL=$2
+    local FILE_EXPECT=${3:--}
+    if diff -u $FILE_EXPECT $FILE_ACTUAL >&2; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
+
+# Convert a string like "2014-05-01T09:30:45Z" to "YYYY-mm-ddTHH:MM:SSZ".
+# This allows output files to compare.
+date2datefmt() {
+    perl -p -e 's/\d+-\d\d-\d\dT\d\d:\d\d:\d\dZ/YYYY-mm-ddTHH:MM:SSZ/' "$@"
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-keyword-print/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-keyword-print/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-keyword-print/00-simple.t	(revision 5095)
@@ -0,0 +1,67 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm keyword-print".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+svnadmin create plants
+URL="file://$PWD/plants"
+svn mkdir --parents -q -m 'test' $URL/{daisy,ivy,holly}/trunk
+mkdir -p conf
+cat >conf/keyword.cfg <<__CFG__
+location{primary}[daisy]=$URL/daisy/
+location{primary}[ivy]=$URL/ivy//
+location{primary}[holly]=$URL/holly
+__CFG__
+export FCM_CONF_PATH=$PWD/conf
+#-------------------------------------------------------------------------------
+tests 21
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE" # no argument
+run_pass "$TEST_KEY" fcm kp
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+location{primary}[daisy] = $URL/daisy
+location{primary}[holly] = $URL/holly
+location{primary}[ivy] = $URL/ivy
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+for NS in daisy ivy holly; do
+    TEST_KEY="$TEST_KEY_BASE-$NS" # normal mode
+    run_pass "$TEST_KEY" fcm kp fcm:$NS
+    file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+location{primary}[$NS] = $URL/$NS
+__OUT__
+    file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+
+    TEST_KEY="$TEST_KEY_BASE-v-$NS" # verbose mode
+    run_pass "$TEST_KEY" fcm kp -v fcm:$NS
+    file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+location{primary}[$NS] = $URL/$NS
+location[${NS}-br] = $URL/$NS/branches
+location[${NS}-tg] = $URL/$NS/tags
+location[${NS}-tr] = $URL/$NS/trunk
+location[${NS}_br] = $URL/$NS/branches
+location[${NS}_tg] = $URL/$NS/tags
+location[${NS}_tr] = $URL/$NS/trunk
+__OUT__
+    file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+done
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-keyword-print/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-keyword-print/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-keyword-print/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-loc-layout/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-loc-layout/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-loc-layout/00-simple.t	(revision 5095)
@@ -0,0 +1,163 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm status".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 24
+#-------------------------------------------------------------------------------
+setup
+unset TEST_PROJECT
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, no project, default setup
+TEST_KEY=$TEST_KEY_BASE-no-project-default
+run_pass "$TEST_KEY" fcm loc-layout
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: .
+url: $ROOT_URL/trunk@9
+root: $REPOS_URL
+path: /trunk
+peg_rev: 9
+project: 
+branch: trunk
+branch_category: trunk
+sub_tree: 
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, no project, default setup, cd to subdirectory
+TEST_KEY=$TEST_KEY_BASE-no-project-subtree
+cd module
+run_pass "$TEST_KEY" fcm loc-layout
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: .
+url: $ROOT_URL/trunk/module@9
+root: $REPOS_URL
+path: /trunk/module
+peg_rev: 9
+project: 
+branch: trunk
+branch_category: trunk
+sub_tree: module
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+cd ..
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, no project, default setup, target subdirectory
+TEST_KEY=$TEST_KEY_BASE-no-project-target-subtree
+run_pass "$TEST_KEY" fcm loc-layout module
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: module
+url: $ROOT_URL/trunk/module@9
+root: $REPOS_URL
+path: /trunk/module
+peg_rev: 9
+project: 
+branch: trunk
+branch_category: trunk
+sub_tree: module
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, no project, default setup, target subdirectory
+TEST_KEY=$TEST_KEY_BASE-no-project-target-repos
+run_pass "$TEST_KEY" fcm loc-layout $REPOS_URL
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: $REPOS_URL
+url: $ROOT_URL@9
+root: $REPOS_URL
+path: 
+peg_rev: 9
+project: 
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
+setup
+init_repos_layout_roses
+svn checkout -q $ROOT_URL/a/a/0/0/0/trunk $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, 'roses' 5-level project
+TEST_KEY=$TEST_KEY_BASE-roses-default
+run_pass "$TEST_KEY" fcm loc-layout
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: .
+url: $ROOT_URL/a/a/0/0/0/trunk@3
+root: $REPOS_URL
+path: /a/a/0/0/0/trunk
+peg_rev: 3
+project: a/a/0/0/0
+branch: trunk
+branch_category: trunk
+sub_tree: 
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, 'roses' 5-level project
+TEST_KEY=$TEST_KEY_BASE-roses-subtree
+cd module
+run_pass "$TEST_KEY" fcm loc-layout
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: .
+url: $ROOT_URL/a/a/0/0/0/trunk/module@3
+root: $REPOS_URL
+path: /a/a/0/0/0/trunk/module
+peg_rev: 3
+project: a/a/0/0/0
+branch: trunk
+branch_category: trunk
+sub_tree: module
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+cd ..
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, 'roses' 5-level project
+TEST_KEY=$TEST_KEY_BASE-roses-target-subtree
+run_pass "$TEST_KEY" fcm loc-layout module
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: module
+url: $ROOT_URL/a/a/0/0/0/trunk/module@3
+root: $REPOS_URL
+path: /a/a/0/0/0/trunk/module
+peg_rev: 3
+project: a/a/0/0/0
+branch: trunk
+branch_category: trunk
+sub_tree: module
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm loc-layout, no project, default setup, target subdirectory
+TEST_KEY=$TEST_KEY_BASE-roses-target-repos
+run_pass "$TEST_KEY" fcm loc-layout $REPOS_URL
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+target: $REPOS_URL
+url: $ROOT_URL@3
+root: $REPOS_URL
+path: 
+peg_rev: 3
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-loc-layout/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-loc-layout/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-loc-layout/test_header	(revision 5095)
@@ -0,0 +1,58 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Optional enviroment variables:
+#   TEST_REMOTE_HOST (tests using svn+ssh repositories located on given host)
+# ------------------------------------------------------------------------------
+
+. $(dirname $0)/../lib/bash/svn_test_header
+
+function init_repos_layout_roses() {
+    if [[ -n ${TEST_REMOTE_HOST:-} ]]; then
+        TEST_REMOTE_DIR=$(ssh $TEST_REMOTE_HOST "mktemp -d")
+        ssh $TEST_REMOTE_HOST "svnadmin create --fs-type fsfs $TEST_REMOTE_DIR"
+        REPOS_URL="svn+ssh://${TEST_REMOTE_HOST}$TEST_REMOTE_DIR"
+    else
+        svnadmin create --fs-type fsfs $TEST_DIR/test_repos
+        REPOS_URL="file://$TEST_DIR/test_repos"
+    fi
+    svn mkdir -q -m " " --parents $REPOS_URL/a/a/0/0/0/trunk
+    svn import -q $TEST_SOURCE_DIR/../etc/repo_files \
+        $REPOS_URL/a/a/0/0/0/trunk -m "initial trunk import"
+    TMPFILE=$(mktemp)
+    cat >$TMPFILE <<__LAYOUT__
+depth-project = 5
+depth-branch = 1
+depth-tag = 1
+dir-trunk = trunk
+dir-branch =
+dir-tag =
+level-owner-branch =
+level-owner-tag =
+template-branch =
+template-tag =
+__LAYOUT__
+    TMPDIR=$(mktemp -d)
+    svn checkout -q $REPOS_URL $TMPDIR
+    svn propset -q --file=$TMPFILE fcm:layout $TMPDIR
+    svn commit -q -m " " $TMPDIR
+    rm -f $TMPFILE
+    rm -rf $TMPDIR
+    ROOT_URL=$REPOS_URL
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic.t	(revision 5095)
@@ -0,0 +1,86 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "fcm make".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 18
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+find .fcm-make build -type f | sed 's/^\(\.fcm-make\/log\).*$/\1/' \
+    | sort >"$TEST_KEY.find"
+sort >"${TEST_KEY}.find.expected" <<'__OUT__'
+.fcm-make/config-as-parsed.cfg
+.fcm-make/config-on-success.cfg
+.fcm-make/ctx.gz
+.fcm-make/log
+build/bin/hello.exe
+build/include/world.mod
+build/o/hello.o
+build/o/world.o
+__OUT__
+file_cmp "$TEST_KEY.find" "$TEST_KEY.find" "${TEST_KEY}.find.expected"
+file_test "$TEST_KEY.log" fcm-make.log
+file_test "$TEST_KEY.fcm-make-as-parsed.cfg" fcm-make-as-parsed.cfg
+file_test "$TEST_KEY.fcm-make-on-success.cfg" fcm-make-on-success.cfg
+readlink fcm-make.log >"$TEST_KEY.log.readlink"
+file_cmp "$TEST_KEY.log.readlink" "$TEST_KEY.log.readlink" <<<'.fcm-make/log'
+sed '/^\[info\] \(source->target\|target\|required-target\) /!d' \
+    .fcm-make/log >"$TEST_KEY.log.sed"
+file_cmp "$TEST_KEY.log.sed" "$TEST_KEY.log.sed" <<'__LOG__'
+[info] source->target / -> (archive) lib/ libo.a
+[info] source->target hello.f90 -> (link) bin/ hello.exe
+[info] source->target hello.f90 -> (install) include/ hello.f90
+[info] source->target hello.f90 -> (compile) o/ hello.o
+[info] source->target world.f90 -> (install) include/ world.f90
+[info] source->target world.f90 -> (compile+) include/ world.mod
+[info] source->target world.f90 -> (compile) o/ world.o
+[info] target hello.exe
+[info] target  - hello.o
+[info] target  -  - world.mod
+[info] target  -  -  - world.o
+[info] target  - world.o
+__LOG__
+file_test "$TEST_KEY-as-parsed.cfg" fcm-make-as-parsed.cfg
+readlink fcm-make-as-parsed.cfg >"$TEST_KEY-as-parsed.cfg.out"
+file_cmp "$TEST_KEY-as-parsed.cfg.out" "$TEST_KEY-as-parsed.cfg.out" \
+    <<<'.fcm-make/config-as-parsed.cfg'
+file_test "$TEST_KEY-on-success.cfg" fcm-make-on-success.cfg 
+readlink fcm-make-on-success.cfg >"$TEST_KEY-on-success.cfg.out"
+file_cmp "$TEST_KEY-on-success.cfg.out" "$TEST_KEY-on-success.cfg.out" \
+    <<<'.fcm-make/config-on-success.cfg'
+run_pass "$TEST_KEY.exe" $PWD/build/bin/hello.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY.exe.out" <<<'Hello Earth'
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr"
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr-fail"
+echo 'build.target=foo' >>fcm-make.cfg
+run_fail "$TEST_KEY" fcm make
+run_fail "$TEST_KEY.config-on-success" test -e .fcm-make/config-on-success
+run_fail "$TEST_KEY.fcm-make-on-success.cfg" test -e fcm-make-on-success.cfg
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/bin/my-ld
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/bin/my-ld	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/bin/my-ld	(revision 5095)
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+echo "$@" >$(basename $0).out
+exec ${FCM_TEST_FC:-gfortran} "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+description = There is nothing like a good test
+steps = build
+build.source = $HERE/src
+build.target{task} = link
+#build.prop{keep-lib-o} = true
+#build.prop{ld} = ld
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/src/hello.f90	(revision 5095)
@@ -0,0 +1,4 @@
+program hello
+use world, only: get_world
+WRITE(*, '(A,A)') 'Hello ', trim(get_world())
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/src/world.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/src/world.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/00-build-basic/src/world.f90	(revision 5095)
@@ -0,0 +1,8 @@
+module world
+character(*), parameter :: world1 = 'Earth'
+contains
+elemental function get_world() result(w)
+character(len=len(world1)) :: w
+w = world1
+end function get_world
+end module world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/01-build-link-opts
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/01-build-link-opts	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/01-build-link-opts	(revision 5095)
@@ -0,0 +1,1 @@
+link 00-build-basic
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/01-build-link-opts.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/01-build-link-opts.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/01-build-link-opts.t	(revision 5095)
@@ -0,0 +1,94 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests some linker options for "fcm make".
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+#-------------------------------------------------------------------------------
+tests 14
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* .
+PATH="${PWD}/bin:${PATH}"
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-ld-link-without-ar-new"
+run_pass "${TEST_KEY}" fcm make --new 'build.prop{link-without-ar}=true'
+find 'build' -type f | sort >"${TEST_KEY}.find"
+file_cmp "${TEST_KEY}.find" "${TEST_KEY}.find" <<'__OUT__'
+build/bin/hello.exe
+build/include/world.mod
+build/o/hello.o
+build/o/world.o
+__OUT__
+sed -n 's/^.*\(gfortran -obin.*\)$/\1/p' 'fcm-make.log' >"${TEST_KEY}.log.edited"
+file_cmp "${TEST_KEY}.log.edited" "${TEST_KEY}.log.edited" <<'__OUT__'
+gfortran -obin/hello.exe o/hello.o o/world.o
+__OUT__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-keep-lib-o-incr"
+fcm make -q --new
+echo 'build.prop{keep-lib-o} = true' >>fcm-make.cfg
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+if cmp -s "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"; then
+    fail "$TEST_KEY.mtime"
+else
+    pass "$TEST_KEY.mtime"
+fi
+file_grep "$TEST_KEY.mtime.grep" 'lib/libhello[.]a' "$TEST_KEY.mtime"
+sed -i '/hello[.]exe/d' "$TEST_KEY.mtime.old"
+sed -i '/libhello[.]a/d; /hello[.]exe/d' "$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime.old" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-keep-lib-o-new"
+# echo 'build.prop{keep-lib-o} = true' >>fcm-make.cfg # already done above
+run_pass "$TEST_KEY" fcm make --new
+find build -type f | sort >"$TEST_KEY.find"
+file_cmp "$TEST_KEY.find" "$TEST_KEY.find" <<'__OUT__'
+build/bin/hello.exe
+build/include/world.mod
+build/lib/libhello.a
+build/o/hello.o
+build/o/world.o
+__OUT__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-ld-incr"
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/fcm-make.cfg .
+fcm make -q --new
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+echo 'build.prop{ld} = my-ld' >>fcm-make.cfg
+run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_grep "$TEST_KEY.mtime.grep" 'build/my-ld[.]out' "$TEST_KEY.mtime"
+sed -i '/hello[.]exe/d' "$TEST_KEY.mtime.old"
+sed -i '/hello[.]exe/d; /my-ld[.]out/d' "$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime.old" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-ld-new"
+# echo 'build.prop{ld} = my-ld' >>fcm-make.cfg # already done above
+run_pass "$TEST_KEY" fcm make --new
+find build -type f | sort >"$TEST_KEY.find"
+file_cmp "$TEST_KEY.find" "$TEST_KEY.find" <<'__OUT__'
+build/bin/hello.exe
+build/include/world.mod
+build/my-ld.out
+build/o/hello.o
+build/o/world.o
+__OUT__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface.t	(revision 5095)
@@ -0,0 +1,46 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests build ext-iface for "fcm make".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 4
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+# Normal operation. Lots of examples in a single source file.
+TEST_KEY="$TEST_KEY_BASE-t1"
+TARGETS=t1.interface run_pass "$TEST_KEY" fcm make --new
+file_cmp "$TEST_KEY.interface" build/include/t1.interface expected/t1.interface
+#-------------------------------------------------------------------------------
+# Bad syntax 1: missing close bracket ) in a local declaration statement.
+# Hang at FCM-2-3-1.
+# We can ignore this problem, as it does not add to the interface
+TEST_KEY="$TEST_KEY_BASE-t2"
+TARGETS=t2.interface run_fail "$TEST_KEY" fcm make --new
+# Time may not be 0.0 on a very very slow computer
+sed -i '2s/ [0-9][0-9]*\.[0-9][0-9]* / ?.? /' "$TEST_KEY.err"
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<__ERR__
+[FAIL] $PWD/src/t2.f90(2): syntax error
+[FAIL] ext-iface  ?.? ! t2.interface         <- t2.f90
+[FAIL] ! t2.interface        : update task failed
+
+__ERR__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/expected/t1.interface
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/expected/t1.interface	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/expected/t1.interface	(revision 5095)
@@ -0,0 +1,76 @@
+interface
+logical function func_simple()
+end function func_simple
+logical function func_simple_1()
+end function
+logical function func_simple_2()
+end
+pure logical function func_simple_pure()
+end function func_simple_pure
+recursive pure integer function func_simple_recursive_pure(i)
+integer, intent(in) :: i
+end function func_simple_recursive_pure
+elemental logical function func_simple_elemental()
+end function func_simple_elemental
+integer(selected_int_kind(0)) function func_with_use_and_args(egg, ham)
+use foo
+use bar, only:&
+ & i_am_dim
+integer, intent(in) :: egg(i_am_dim)
+integer, intent(in) :: ham(i_am_dim, 2)
+end function func_with_use_and_args
+character(20) function func_with_parameters(egg, ham)
+character*(*), parameter :: x_param = '01234567890'
+character(*), parameter :: &
+ y_param &
+ = '!&!&!&!&!&!'
+character(len(x_param)), intent(in) :: egg
+character(len(y_param)), intent(in) :: ham
+end function func_with_parameters
+function func_with_parameters_1(egg, ham) result(r)
+integer, parameter :: x_param = 10
+integer z_param
+parameter(z_param = 2)
+real, intent(in), dimension(x_param) :: egg
+integer, intent(in) :: ham
+logical :: r(z_param)
+end function func_with_parameters_1
+character(10) function func_with_contains(mushroom, tomoato)
+character(5) mushroom
+character(5) tomoato
+end function func_with_contains
+Function func_mix_local_and_result(egg, ham, bacon) Result(Breakfast)
+Integer, Intent(in) :: egg, ham
+Real, Intent(in) :: bacon
+Real :: tomato, breakfast
+End Function func_mix_local_and_result
+subroutine sub_simple()
+end subroutine sub_simple
+subroutine sub_simple_1()
+end subroutine
+subroutine sub_simple_2()
+end
+subroutine sub_simple_3()
+end sub&
+&routine&
+& sub_simple_3
+subroutine sub_with_contains(foo)
+character*(len('!"&''&"!')) &
+ foo
+end subroutine sub_with_contains
+subroutine sub_with_renamed_import(i_am_dim)
+integer, parameter :: d = 2
+complex :: i_am_dim(d)
+end subroutine sub_with_renamed_import
+subroutine sub_with_external(proc)
+external proc
+end subroutine sub_with_external
+subroutine sub_with_external2(PROC)
+external proc
+end subroutine sub_with_external2
+subroutine sub_with_external3(proc)
+external PROC
+end subroutine sub_with_external3
+subroutine sub_with_end()
+end subroutine sub_with_end
+end interface
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/expected/t2.interface
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/expected/t2.interface	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/expected/t2.interface	(revision 5095)
@@ -0,0 +1,4 @@
+interface
+subroutine t2()
+end subroutine t2
+end interface
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+$TARGETS{?}=t1.interface
+build.target=$TARGETS
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/m1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/m1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/m1.f90	(revision 5095)
@@ -0,0 +1,26 @@
+! A module with nonsense
+module bar
+type food
+integer :: cooking_method
+end type food
+type organic
+integer :: growing_method
+end type organic
+integer, parameter :: i_am_dim = 10
+end module bar
+
+! A module with more nonsense
+module foo
+use bar, only: FOOD
+integer :: foo_int
+contains
+subroutine foo_sub(egg)
+integer, parameter :: egg_dim = 10
+type(Food), intent(in) :: egg
+write(*, *) egg
+end subroutine foo_sub
+elemental function foo_func() result(f)
+integer :: f
+f = 0
+end function
+end module foo
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/t1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/t1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/t1.f90	(revision 5095)
@@ -0,0 +1,177 @@
+! A simple function
+logical function func_simple()
+func_simple = .true.
+end function func_simple
+
+! A simple function, but with less friendly end
+logical function func_simple_1()
+func_simple_1 = .true.
+end function
+
+! A simple function, but with even less friendly end
+logical function func_simple_2()
+func_simple_2 = .true.
+end
+
+! A pure simple function
+pure logical function func_simple_pure()
+func_simple_pure = .true.
+end function func_simple_pure
+
+! A pure recursive function
+recursive pure integer function func_simple_recursive_pure(i)
+integer, intent(in) :: i
+if (i <= 0) then
+    func_simple_recursive_pure = i
+else
+    func_simple_recursive_pure = i + func_simple_recursive_pure(i - 1)
+end if
+end function func_simple_recursive_pure
+
+! An elemental simple function
+elemental logical function func_simple_elemental()
+func_simple_elemental = .true.
+end function func_simple_elemental
+
+! An function with arguments and module imports
+integer(selected_int_kind(0)) function func_with_use_and_args(egg, ham)
+use foo
+! Deliberate trailing spaces in next line
+use bar, only : organic,     i_am_dim   
+implicit none
+integer, intent(in) :: egg(i_am_dim)
+integer, intent(in) :: ham(i_am_dim, 2)
+real bacon
+! Deliberate trailing spaces in next line
+type(   organic   ) :: tomato   
+func_with_use_and_args = egg(1) + ham(1, 1)
+end function func_with_use_and_args
+
+! A function with some parameters
+character(20) function func_with_parameters(egg, ham)
+implicit none
+character*(*), parameter :: x_param = '01234567890'
+character(*), parameter :: & ! throw in some comments
+    y_param                &
+    = '!&!&!&!&!&!'          ! how to make life interesting
+integer, parameter :: z = 20
+character(len(x_param)), intent(in) :: egg
+character(len(y_param)), intent(in) :: ham
+func_with_parameters = egg // ham
+end function func_with_parameters
+
+! A function with some parameters, with a result
+function func_with_parameters_1(egg, ham) result(r)
+implicit none
+integer, parameter :: x_param = 10
+integer z_param
+parameter(z_param = 2)
+real, intent(in), dimension(x_param) :: egg
+integer, intent(in) :: ham
+logical :: r(z_param)
+r(1) = int(egg(1)) + ham > 0
+r(2) = .false.
+end function func_with_parameters_1
+
+! A function with a contains
+character(10) function func_with_contains(mushroom, tomoato)
+character(5) mushroom
+character(5) tomoato
+func_with_contains = func_with_contains_1()
+contains
+character(10) function func_with_contains_1()
+func_with_contains_1 = mushroom // tomoato
+end function func_with_contains_1
+end function func_with_contains
+
+! A function with its result declared after a local in the same statement
+Function func_mix_local_and_result(egg, ham, bacon) Result(Breakfast)
+Integer, Intent(in) :: egg, ham
+Real, Intent(in) :: bacon
+Real :: tomato, breakfast
+Breakfast = real(egg) + real(ham) + bacon
+End Function func_mix_local_and_result
+
+! A simple subroutine
+subroutine sub_simple()
+end subroutine sub_simple
+
+! A simple subroutine, with not so friendly end
+subroutine sub_simple_1()
+end subroutine
+
+! A simple subroutine, with even less friendly end
+subroutine sub_simple_2()
+end
+
+! A simple subroutine, with funny continuation
+subroutine sub_simple_3()
+end sub&
+&routine&
+& sub_simple_3
+
+! A subroutine with a few contains
+subroutine sub_with_contains(foo) ! " &
+! Deliberate trailing spaces in next line
+use Bar, only: i_am_dim    
+character*(len('!"&''&"!')) & ! what a mess!
+    foo
+call sub_with_contains_first()
+call sub_with_contains_second()
+call sub_with_contains_third()
+print*, foo
+contains
+subroutine sub_with_contains_first()
+interface
+integer function x()
+end function x
+end interface
+end subroutine sub_with_contains_first
+subroutine sub_with_contains_second()
+end subroutine
+subroutine sub_with_contains_third()
+end subroutine
+end subroutine sub_with_contains
+
+! A subroutine with a renamed module import
+subroutine sub_with_renamed_import(i_am_dim)
+use bar, only: i_am_not_dim => i_am_dim
+integer, parameter :: d = 2
+complex :: i_am_dim(d)
+print*, i_am_dim
+end subroutine sub_with_renamed_import
+
+! A subroutine with an external argument
+subroutine sub_with_external(proc)
+external proc
+call proc()
+end subroutine sub_with_external
+
+! A subroutine with an external argument, 2
+! https://github.com/metomi/fcm/issues/175
+subroutine sub_with_external2(PROC)
+external proc
+call proc()
+end subroutine sub_with_external2
+
+! A subroutine with an external argument, 3
+! https://github.com/metomi/fcm/issues/175
+subroutine sub_with_external3(proc)
+external PROC
+call proc()
+end subroutine sub_with_external3
+
+! A subroutine with a variable named "end"
+subroutine sub_with_end()
+integer :: end
+end = 0
+end subroutine sub_with_end
+
+! A module with nonsense
+module stuff
+character(*), parameter :: stuffing = 'yummy'
+contains
+subroutine matter()
+print*, 'Matter is not dark'
+end subroutine matter
+end module stuff
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/t2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/t2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/02-build-ext-iface/src/t2.f90	(revision 5095)
@@ -0,0 +1,3 @@
+subroutine t2()
+integer :: c( ! bad syntax
+end subroutine t2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths.t	(revision 5095)
@@ -0,0 +1,69 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "build.prop{fc.include-paths}".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+
+function get_compiler_log() {
+    sed '/^\[info\] shell(0.*) gfortran/!d;
+         /hello\.exe/d;
+         s/^\[info\] shell(0.*) //' .fcm-make/log
+}
+#-------------------------------------------------------------------------------
+tests 11
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-control"
+run_fail "$TEST_KEY" fcm make
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+FCM_TEST_FC_INCLUDE_PATHS="$PWD/include/world1 $PWD/include/world2" \
+    run_pass "$TEST_KEY" fcm make
+$PWD/build/bin/hello.exe >"$TEST_KEY.command.out"
+file_cmp "$TEST_KEY.command.out" "$TEST_KEY.command.out" <<<'Hello Earth'
+get_compiler_log >"$TEST_KEY.gfortran.log"
+file_cmp "$TEST_KEY.gfortran.log" "$TEST_KEY.gfortran.log" <<__LOG__
+gfortran -oo/world.o -c -I./include -I$PWD/include/world1 -I$PWD/include/world2 $PWD/src/world.f90
+gfortran -oo/hello.o -c -I./include -I$PWD/include/world1 -I$PWD/include/world2 $PWD/src/hello.f90
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr0"
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+FCM_TEST_FC_INCLUDE_PATHS="$PWD/include/world1 $PWD/include/world2" \
+    run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+$PWD/build/bin/hello.exe >"$TEST_KEY.command.out"
+file_cmp "$TEST_KEY.command.out" "$TEST_KEY.command.out" <<<'Hello Earth'
+get_compiler_log >"$TEST_KEY.gfortran.log"
+file_cmp "$TEST_KEY.gfortran.log" "$TEST_KEY.gfortran.log" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr1"
+FCM_TEST_FC_INCLUDE_PATHS="$PWD/include/world2 $PWD/include/world1" \
+    run_pass "$TEST_KEY" fcm make
+$PWD/build/bin/hello.exe >"$TEST_KEY.command.out"
+file_cmp "$TEST_KEY.command.out" "$TEST_KEY.command.out" <<<'Hello Moon'
+get_compiler_log >"$TEST_KEY.gfortran.log"
+file_cmp "$TEST_KEY.gfortran.log" "$TEST_KEY.gfortran.log" <<__LOG__
+gfortran -oo/world.o -c -I./include -I$PWD/include/world2 -I$PWD/include/world1 $PWD/src/world.f90
+gfortran -oo/hello.o -c -I./include -I$PWD/include/world2 -I$PWD/include/world1 $PWD/src/hello.f90
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{no-dep.include}=worldx.f90
+$FCM_TEST_FC_INCLUDE_PATHS{?}=
+build.prop{fc.include-paths}=$FCM_TEST_FC_INCLUDE_PATHS
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/include/world1/worldx.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/include/world1/worldx.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/include/world1/worldx.f90	(revision 5095)
@@ -0,0 +1,1 @@
+character(*), parameter :: world1 = 'Earth'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/include/world2/worldx.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/include/world2/worldx.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/include/world2/worldx.f90	(revision 5095)
@@ -0,0 +1,1 @@
+character(*), parameter :: world1 = 'Moon'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/src/hello.f90	(revision 5095)
@@ -0,0 +1,4 @@
+program hello
+use world, only: get_world
+WRITE(*, '(A,A)') 'Hello ', trim(get_world())
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/src/world.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/src/world.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/03-build-include-paths/src/world.f90	(revision 5095)
@@ -0,0 +1,8 @@
+module world
+include 'worldx.f90'
+contains
+elemental function get_world() result(w)
+character(len=len(world1)) :: w
+w = world1
+end function get_world
+end module world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs.t	(revision 5095)
@@ -0,0 +1,71 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "build.prop{fc.lib-paths}" and "build.prop{fc.libs}".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+
+function get_linker_log() {
+    sed '/^\[info\] shell(0.*) gfortran/!d;
+         s/^\[info\] shell(0.*) //' .fcm-make/log
+}
+#-------------------------------------------------------------------------------
+tests 11
+set -e
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+gfortran -c src-lib/*
+mkdir -p greet/lib
+ar rs greet/lib/libgreet.a greet.o 2>/dev/null
+ar rs greet/lib/libearth.a earth.o 2>/dev/null
+ar rs greet/lib/libmoon.a moon.o 2>/dev/null
+rm *.o
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-control"
+run_fail "$TEST_KEY" fcm make
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+FCM_TEST_FC_LIBS='greet earth' run_pass "$TEST_KEY" fcm make
+$PWD/build/bin/hello.exe >"$TEST_KEY.command.out"
+file_cmp "$TEST_KEY.command.out" "$TEST_KEY.command.out" <<<'Hello Earth'
+get_linker_log >"$TEST_KEY.gfortran.log"
+file_cmp "$TEST_KEY.gfortran.log" "$TEST_KEY.gfortran.log" <<__LOG__
+gfortran -obin/hello.exe o/hello.o -L$PWD/greet/lib -lgreet -learth
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr0"
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+FCM_TEST_FC_LIBS='greet earth' run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+$PWD/build/bin/hello.exe >"$TEST_KEY.command.out"
+file_cmp "$TEST_KEY.command.out" "$TEST_KEY.command.out" <<<'Hello Earth'
+get_linker_log >"$TEST_KEY.gfortran.log"
+file_cmp "$TEST_KEY.gfortran.log" "$TEST_KEY.gfortran.log" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr1"
+FCM_TEST_FC_LIBS='greet moon' run_pass "$TEST_KEY" fcm make
+$PWD/build/bin/hello.exe >"$TEST_KEY.command.out"
+file_cmp "$TEST_KEY.command.out" "$TEST_KEY.command.out" <<<'Hello Moon'
+get_linker_log >"$TEST_KEY.gfortran.log"
+file_cmp "$TEST_KEY.gfortran.log" "$TEST_KEY.gfortran.log" <<__LOG__
+gfortran -obin/hello.exe o/hello.o -L$PWD/greet/lib -lgreet -lmoon
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+$FCM_TEST_FC_LIBS{?}=
+build.prop{fc.lib-paths}=$HERE/greet/lib
+build.prop{fc.libs}=$FCM_TEST_FC_LIBS
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/earth.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/earth.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/earth.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine world(w)
+character(*), intent(out) :: w
+w = 'Earth'
+end subroutine world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/greet.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine greet(hello, world)
+character(*), intent(in) :: hello, world
+write(*, '(A,1X,A)') trim(hello), trim(world)
+end subroutine greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/moon.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/moon.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src-lib/moon.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine world(w)
+character(*), intent(out) :: w
+w = 'Moon'
+end subroutine world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/04-build-libs/src/hello.f90	(revision 5095)
@@ -0,0 +1,13 @@
+program hello
+character(5) :: w
+interface
+subroutine greet(hi, world)
+character(*), intent(in) :: hi, world
+end subroutine greet
+subroutine world(w)
+character(*), intent(out) :: w
+end subroutine world
+end interface
+call world(w)
+call greet('Hello', w)
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic.t	(revision 5095)
@@ -0,0 +1,85 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "fcm make" C and C++ source.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+
+function get_compiler_log() {
+    sed '/^\[info\] shell(0.*) gcc\|g++/!d;
+         s/^\[info\] shell(0.*) //' .fcm-make/log
+}
+#-------------------------------------------------------------------------------
+tests 14
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+find .fcm-make build -type f | sed 's/^\(\.fcm-make\/log\).*$/\1/' \
+    | sort >"$TEST_KEY.find"
+sort >"${TEST_KEY}.find.expected" <<'__OUT__'
+.fcm-make/config-as-parsed.cfg
+.fcm-make/config-on-success.cfg
+.fcm-make/ctx.gz
+.fcm-make/log
+build/bin/chello
+build/bin/cxxhello
+build/o/chello.o
+build/o/cxxhello.o
+__OUT__
+file_cmp "$TEST_KEY.find" "$TEST_KEY.find" "${TEST_KEY}.find.expected"
+run_pass "$TEST_KEY.chello" $PWD/build/bin/chello
+file_cmp "$TEST_KEY.chello.out" "$TEST_KEY.chello.out" <<<'Hello C'
+run_pass "$TEST_KEY.cxxhello" $PWD/build/bin/cxxhello
+file_cmp "$TEST_KEY.cxxhello.out" "$TEST_KEY.cxxhello.out" <<<'Hello C++'
+get_compiler_log >"$TEST_KEY.compiler.log"
+file_cmp "$TEST_KEY.compiler.log" "$TEST_KEY.compiler.log" <<__LOG__
+gcc -oo/chello.o -c -I./include $PWD/src/chello.c
+gcc -obin/chello o/chello.o
+g++ -oo/cxxhello.o -c -I./include $PWD/src/cxxhello.cxx
+g++ -obin/cxxhello o/cxxhello.o
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr-0"
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+get_compiler_log >"$TEST_KEY.compiler.log"
+file_cmp "$TEST_KEY.compiler.log" "$TEST_KEY.compiler.log" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr-1"
+export CCFLAGS=-O2
+run_pass "$TEST_KEY" fcm make
+get_compiler_log >"$TEST_KEY.compiler.log"
+file_cmp "$TEST_KEY.compiler.log" "$TEST_KEY.compiler.log" <<__LOG__
+gcc -oo/chello.o -c -I./include -O2 $PWD/src/chello.c
+gcc -obin/chello o/chello.o
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr-2"
+export CXXFLAGS=-O3
+run_pass "$TEST_KEY" fcm make
+get_compiler_log >"$TEST_KEY.compiler.log"
+file_cmp "$TEST_KEY.compiler.log" "$TEST_KEY.compiler.log" <<__LOG__
+g++ -oo/cxxhello.o -c -I./include -O3 $PWD/src/cxxhello.cxx
+g++ -obin/cxxhello o/cxxhello.o
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{file-ext.bin}=
+$CCFLAGS{?}=
+build.prop{cc.flags}=$CCFLAGS
+$CXXFLAGS{?}=
+build.prop{cxx.flags}=$CXXFLAGS
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/src/chello.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/src/chello.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/src/chello.c	(revision 5095)
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main() {
+    printf("Hello C\n");
+    return 0;
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/src/cxxhello.cxx
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/src/cxxhello.cxx	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/05-build-c-cxx-basic/src/cxxhello.cxx	(revision 5095)
@@ -0,0 +1,6 @@
+#include <iostream>
+
+int main() {
+    std::cout << "Hello C++\n";
+    return 0;
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/06-extract-ssh.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/06-extract-ssh.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/06-extract-ssh.t	(revision 5095)
@@ -0,0 +1,108 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "extract" from SSH location.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+N_TESTS=6
+tests $N_TESTS
+#-------------------------------------------------------------------------------
+# Get a remote host for testing
+T_HOST=
+for FILE in $HOME/.metomi/fcm/t.cfg $FCM_HOME/etc/fcm/t.cfg; do
+    if [[ ! -f $FILE || ! -r $FILE ]]; then
+        continue
+    fi
+    T_HOST=$(fcm cfg $FILE | sed '/^ *host *=/!d; s/^ *host *= *//' | tail -1)
+    if [[ -n $T_HOST ]]; then
+        break
+    fi
+done
+if [[ -z $T_HOST ]]; then
+    skip $N_TESTS 'fcm/t.cfg: "host" not defined'
+    exit 0
+fi
+#-------------------------------------------------------------------------------
+# Create a source tree on the remote host
+mkdir -p hello/{greet,hello,hi,.secret}
+for NAME in mercury venus earth mars; do
+    echo "Greet $NAME" >hello/greet/greet_${NAME}.txt
+    echo "Hello $NAME" >hello/hello/hello_${NAME}.txt
+    echo "[Alien-speak] $NAME" >hello/.secret/hello_${NAME}.txt
+    echo "Hi $NAME" >hello/hi/hi_${NAME}.txt
+done
+T_HOST_WORK_DIR=$(ssh -oBatchMode=yes $T_HOST mktemp -d)
+rsync -a hello $T_HOST:$T_HOST_WORK_DIR
+rm -r hello
+#-------------------------------------------------------------------------------
+# Create a fcm-make.cfg
+cat >fcm-make.cfg <<__FCM_MAKE_CFG__
+steps=extract
+extract.ns=hello
+extract.location[hello]=$T_HOST:$T_HOST_WORK_DIR/hello
+__FCM_MAKE_CFG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+grep -e '\[info\] location hello: 0' -e '\[info\] AU hello:0' fcm-make.log \
+    >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+[info] location hello: 0: $T_HOST:$T_HOST_WORK_DIR/hello
+[info] AU hello:0      hi/hi_mars.txt
+[info] AU hello:0      hello/hello_venus.txt
+[info] AU hello:0      hello/hello_mercury.txt
+[info] AU hello:0      greet/greet_venus.txt
+[info] AU hello:0      greet/greet_mars.txt
+[info] AU hello:0      hi/hi_mercury.txt
+[info] AU hello:0      greet/greet_earth.txt
+[info] AU hello:0      greet/greet_mercury.txt
+[info] AU hello:0      hi/hi_earth.txt
+[info] AU hello:0      hello/hello_earth.txt
+[info] AU hello:0      hello/hello_mars.txt
+[info] AU hello:0      hi/hi_venus.txt
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr0"
+run_pass "$TEST_KEY" fcm make
+grep -e '\[info\]   dest:' -e '\[info\] source:' fcm-make.log \
+    >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+[info]   dest:   12 [U unchanged]
+[info] source:   12 [U from base]
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr1"
+echo 'Hello Martians' \
+    | ssh -oBatchMode=yes $T_HOST "cat >$T_HOST_WORK_DIR/hello/hello/hello_mars.txt"
+run_pass "$TEST_KEY" fcm make
+grep \
+    -e '\[info\]   dest:' \
+    -e '\[info\] source:' \
+    -e '\[info\] MU hello:0' \
+    fcm-make.log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+[info] MU hello:0      hello/hello_mars.txt
+[info]   dest:    1 [M modified]
+[info]   dest:   11 [U unchanged]
+[info] source:   12 [U from base]
+__LOG__
+#-------------------------------------------------------------------------------
+ssh -oBatchMode=yes $T_HOST rm -r $T_HOST_WORK_DIR
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep.t	(revision 5095)
@@ -0,0 +1,68 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "build.prop{ns-dep.o}".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 6
+set -e
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-bad-1"
+run_fail "$TEST_KEY" fcm make
+tail -2 .fcm-make/log >"$TEST_KEY.log" 2>/dev/null
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[FAIL] foo: bad or missing dependency (type=ns-dep.o)
+[FAIL]     required by: hello.exe
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-bad-2"
+mkdir src/foo # An empty directory
+run_fail "$TEST_KEY" fcm make
+tail -2 .fcm-make/log >"$TEST_KEY.log" 2>/dev/null
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[FAIL] foo: bad or missing dependency (type=ns-dep.o)
+[FAIL]     required by: hello.exe
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+FCM_TEST_NS_DEP_O=lib run_pass "$TEST_KEY" fcm make
+sed '/^\[info\] \(source->target\|target\) /!d' .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] source->target / -> (archive) lib/ libo.a
+[info] source->target lib -> (archive) lib/ lib/libo.a
+[info] source->target lib/earth.f90 -> (install) include/ earth.f90
+[info] source->target lib/earth.f90 -> (ext-iface) include/ earth.interface
+[info] source->target lib/earth.f90 -> (compile) o/ world.o
+[info] source->target lib/greet.f90 -> (install) include/ greet.f90
+[info] source->target lib/greet.f90 -> (ext-iface) include/ greet.interface
+[info] source->target lib/greet.f90 -> (compile) o/ greet.o
+[info] source->target main -> (archive) lib/ main/libo.a
+[info] source->target main/hello.f90 -> (link) bin/ hello.exe
+[info] source->target main/hello.f90 -> (install) include/ hello.f90
+[info] source->target main/hello.f90 -> (compile) o/ hello.o
+[info] target hello.exe
+[info] target  - greet.o
+[info] target  - hello.o
+[info] target  - world.o
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+$FCM_TEST_NS_DEP_O{?}=foo
+build.prop{ns-dep.o}=$FCM_TEST_NS_DEP_O
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/lib/earth.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/lib/earth.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/lib/earth.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine world(w)
+character(*), intent(out) :: w
+w = 'Earth'
+end subroutine world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/lib/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/lib/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/lib/greet.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine greet(hello, world)
+character(*), intent(in) :: hello, world
+write(*, '(A,1X,A)') trim(hello), trim(world)
+end subroutine greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/main/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/main/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/07-build-ns-dep/src/main/hello.f90	(revision 5095)
@@ -0,0 +1,13 @@
+program hello
+character(5) :: w
+interface
+subroutine greet(hello, world)
+character(*), intent(in) :: hello, world
+end subroutine greet
+subroutine world(w)
+character(*), intent(out) :: w
+end subroutine world
+end interface
+call world(w)
+call greet('Hello', w)
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep.t	(revision 5095)
@@ -0,0 +1,60 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "build.prop{ns-dep.o}".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 4
+set -e
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-bad"
+run_fail "$TEST_KEY" fcm make
+tail -2 .fcm-make/log >"$TEST_KEY.log" 2>/dev/null
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[FAIL] world.o: same target from [lib/earth.f90, lib/moon.f90]
+[FAIL]     required by: hello.exe
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+rm src/lib/moon.f90
+run_pass "$TEST_KEY" fcm make
+sed '/^\[info\] \(source->target\|target\) /!d' .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] source->target / -> (archive) lib/ libo.a
+[info] source->target lib -> (archive) lib/ lib/libo.a
+[info] source->target lib/earth.f90 -> (install) include/ earth.f90
+[info] source->target lib/earth.f90 -> (ext-iface) include/ earth.interface
+[info] source->target lib/earth.f90 -> (compile) o/ world.o
+[info] source->target lib/greet.f90 -> (install) include/ greet.f90
+[info] source->target lib/greet.f90 -> (ext-iface) include/ greet.interface
+[info] source->target lib/greet.f90 -> (compile) o/ greet.o
+[info] source->target main -> (archive) lib/ main/libo.a
+[info] source->target main/hello.f90 -> (link) bin/ hello.exe
+[info] source->target main/hello.f90 -> (install) include/ hello.f90
+[info] source->target main/hello.f90 -> (compile) o/ hello.o
+[info] target hello.exe
+[info] target  - greet.o
+[info] target  - hello.o
+[info] target  - world.o
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{ns-dep.o}=lib
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/earth.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/earth.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/earth.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine world(w)
+character(*), intent(out) :: w
+w = 'Earth'
+end subroutine world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/greet.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine greet(hello, world)
+character(*), intent(in) :: hello, world
+write(*, '(A,1X,A)') trim(hello), trim(world)
+end subroutine greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/moon.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/moon.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/lib/moon.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine world(w)
+character(*), intent(out) :: w
+w = 'Moon'
+end subroutine world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/main/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/main/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/08-build-dup-dep/src/main/hello.f90	(revision 5095)
@@ -0,0 +1,13 @@
+program hello
+character(5) :: w
+interface
+subroutine greet(hello, world)
+character(*), intent(in) :: hello, world
+end subroutine greet
+subroutine world(w)
+character(*), intent(out) :: w
+end subroutine world
+end interface
+call world(w)
+call greet('Hello', w)
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o.t	(revision 5095)
@@ -0,0 +1,64 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "build.prop{dep.o}" top namespace.
+# (Cyclic dependency bug in 2013-09.)
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 2
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+sed '/^\[info\] \(source->target\|target\) /!d' .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] source->target / -> (archive) lib/ libo.a
+[info] source->target lib -> (archive) lib/ lib/libo.a
+[info] source->target lib/earth.f90 -> (install) include/ earth.f90
+[info] source->target lib/earth.f90 -> (ext-iface) include/ earth.interface
+[info] source->target lib/earth.f90 -> (compile) o/ world.o
+[info] source->target lib/greet.f90 -> (install) include/ greet.f90
+[info] source->target lib/greet.f90 -> (ext-iface) include/ greet.interface
+[info] source->target lib/greet.f90 -> (compile) o/ greet.o
+[info] source->target lib/greet_fmt_mod.f90 -> (install) include/ greet_fmt_mod.f90
+[info] source->target lib/greet_fmt_mod.f90 -> (compile+) include/ greet_fmt_mod.mod
+[info] source->target lib/greet_fmt_mod.f90 -> (compile) o/ greet_fmt_mod.o
+[info] source->target main -> (archive) lib/ main/libo.a
+[info] source->target main/hello.f90 -> (link) bin/ hello.exe
+[info] source->target main/hello.f90 -> (install) include/ hello.f90
+[info] source->target main/hello.f90 -> (compile) o/ hello.o
+[info] source->target main/hi.f90 -> (link) bin/ hi.exe
+[info] source->target main/hi.f90 -> (install) include/ hi.f90
+[info] source->target main/hi.f90 -> (compile) o/ hi.o
+[info] target hi.exe
+[info] target  - greet.o
+[info] target  -  - greet_fmt_mod.mod
+[info] target  -  -  - greet_fmt_mod.o
+[info] target  - greet_fmt_mod.o
+[info] target  - hi.o
+[info] target  - world.o
+[info] target hello.exe
+[info] target  - greet.o (n-deps=1)
+[info] target  - greet_fmt_mod.o
+[info] target  - hello.o
+[info] target  - world.o
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{dep.o}=world.o greet.o
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/earth.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/earth.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/earth.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine world(w)
+character(*), intent(out) :: w
+w = 'Earth'
+end subroutine world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/greet.f90	(revision 5095)
@@ -0,0 +1,5 @@
+subroutine greet(hello, world)
+use greet_fmt_mod, only: greet_fmt
+character(*), intent(in) :: hello, world
+write(*, greet_fmt) trim(hello), trim(world)
+end subroutine greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/greet_fmt_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/greet_fmt_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/lib/greet_fmt_mod.f90	(revision 5095)
@@ -0,0 +1,3 @@
+MODULE greet_fmt_mod
+CHARACTER(*), PARAMETER :: greet_fmt='(A,1X,A)'
+END MODULE greet_fmt_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/main/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/main/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/main/hello.f90	(revision 5095)
@@ -0,0 +1,13 @@
+program hello
+character(5) :: w
+interface
+subroutine greet(hi, world)
+character(*), intent(in) :: hi, world
+end subroutine greet
+subroutine world(w)
+character(*), intent(out) :: w
+end subroutine world
+end interface
+call world(w)
+call greet('Hello', w)
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/main/hi.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/main/hi.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/09-build-dep-o/src/main/hi.f90	(revision 5095)
@@ -0,0 +1,13 @@
+program hi
+character(5) :: w
+interface
+subroutine greet(hello, world)
+character(*), intent(in) :: hello, world
+end subroutine greet
+subroutine world(w)
+character(*), intent(out) :: w
+end subroutine world
+end interface
+call world(w)
+call greet('Hello', w)
+end program hi
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/10-log
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/10-log	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/10-log	(revision 5095)
@@ -0,0 +1,1 @@
+link 00-build-basic
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/10-log.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/10-log.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/10-log.t	(revision 5095)
@@ -0,0 +1,66 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", generation of log files.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 12
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+if [[ -d $FCM_HOME/.git ]]; then
+    VERSION="FCM $(git --git-dir=$FCM_HOME/.git describe)"
+else
+    VERSION=$(sed '/FCM\.VERSION/!d; s/^.*="\(.*\)";$/\1/' \
+        $FCM_HOME/doc/etc/fcm-version.js)
+    VERSION="FCM $VERSION"
+fi
+file_grep "${TEST_KEY}.log.version" "\\[info\\] ${VERSION}" '.fcm-make/log'
+file_grep "${TEST_KEY}.log.mode" '\[info\] mode=new' '.fcm-make/log'
+file_grep "${TEST_KEY}.log.description" \
+    '\[info\] description=There is nothing like a good test' '.fcm-make/log'
+if [[ $(ls .fcm-make/log-* | wc -l) == 1 ]]; then
+    pass "$TEST_KEY-n-logs"
+else
+    fail "$TEST_KEY-n-logs"
+fi
+run_pass "$TEST_KEY-symlink-1" \
+    test "$(readlink 'fcm-make.log')" '=' '.fcm-make/log'
+run_pass "$TEST_KEY-symlink-2" \
+    test "$(readlink '.fcm-make/log')" \
+    '=' "$(cd '.fcm-make'; ls 'log-'* | sort | tail -1)"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr"
+sleep 1
+run_pass "$TEST_KEY" fcm make
+file_grep "$TEST_KEY.log.mode" '\[info\] mode=incremental' .fcm-make/log
+if [[ $(ls .fcm-make/log-* | wc -l) == 2 ]]; then
+    pass "$TEST_KEY-n-logs"
+else
+    fail "$TEST_KEY-n-logs"
+fi
+run_pass "$TEST_KEY-symlink-1" \
+    test "$(readlink 'fcm-make.log')" '=' '.fcm-make/log'
+run_pass "$TEST_KEY-symlink-2" \
+    test "$(readlink '.fcm-make/log')" \
+    '=' "$(cd '.fcm-make'; ls 'log-'* | sort | tail -1)"
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path.t	(revision 5095)
@@ -0,0 +1,59 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "preprocess.prop{fpp.include-paths}".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+
+function get_cpp_log() {
+    sed '/^\[info\] shell(0.*) cpp/!d; s/^\[info\] shell(0.*) //' .fcm-make/log
+}
+#-------------------------------------------------------------------------------
+tests 11
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-control"
+run_fail "$TEST_KEY" fcm make
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+FCM_TEST_FPP_INCLUDE_PATHS="$PWD/include/world1" run_pass "$TEST_KEY" fcm make
+file_grep "$TEST_KEY.world.F90" "world1 = 'Earth'" $PWD/preprocess/src/world.F90
+get_cpp_log >"$TEST_KEY.cpp.log"
+file_cmp "$TEST_KEY.cpp.log" "$TEST_KEY.cpp.log" <<__LOG__
+cpp -P -traditional -I./include -I$PWD/include/world1 $PWD/src/world.F90
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr0"
+find preprocess -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+FCM_TEST_FPP_INCLUDE_PATHS="$PWD/include/world1" run_pass "$TEST_KEY" fcm make
+find preprocess -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+file_grep "$TEST_KEY.world.F90" "world1 = 'Earth'" $PWD/preprocess/src/world.F90
+get_cpp_log >"$TEST_KEY.cpp.log"
+file_cmp "$TEST_KEY.cpp.log" "$TEST_KEY.cpp.log" </dev/null
+##-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr1"
+FCM_TEST_FPP_INCLUDE_PATHS="$PWD/include/world2" run_pass "$TEST_KEY" fcm make
+file_grep "$TEST_KEY.world.F90" "world1 = 'Moon'" $PWD/preprocess/src/world.F90
+get_cpp_log >"$TEST_KEY.cpp.log"
+file_cmp "$TEST_KEY.cpp.log" "$TEST_KEY.cpp.log" <<__LOG__
+cpp -P -traditional -I./include -I$PWD/include/world2 $PWD/src/world.F90
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=preprocess
+preprocess.source=$HERE/src
+$FCM_TEST_FPP_INCLUDE_PATHS{?}=
+preprocess.prop{fpp.include-paths}=$FCM_TEST_FPP_INCLUDE_PATHS
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/include/world1/worldx.h
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/include/world1/worldx.h	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/include/world1/worldx.h	(revision 5095)
@@ -0,0 +1,1 @@
+character(*), parameter :: world1 = 'Earth'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/include/world2/worldx.h
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/include/world2/worldx.h	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/include/world2/worldx.h	(revision 5095)
@@ -0,0 +1,1 @@
+character(*), parameter :: world1 = 'Moon'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/src/world.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/src/world.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/11-preprocess-include-path/src/world.F90	(revision 5095)
@@ -0,0 +1,8 @@
+module world
+#include <worldx.h>
+contains
+elemental function get_world() result(w)
+character(len=len(world1)) :: w
+w = world1
+end function get_world
+end module world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop.t	(revision 5095)
@@ -0,0 +1,69 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", *.prop{class,*}.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 6
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+PATH=$PWD/bin:$PATH
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+find build* -type f | sort >"$TEST_KEY.find"
+sort >"${TEST_KEY}.find.expected" <<'__FIND__'
+build/bin/hello.bin
+build/o/hello.o
+build_house/bin/hello_house
+build_house/o/hello_house.o
+build_office/bin/hello_office
+build_office/o/hello_office.o
+build_road/bin/hello_road
+build_road/o/hello_road.o
+__FIND__
+file_cmp "$TEST_KEY.find" "$TEST_KEY.find" "${TEST_KEY}.find.expected"
+sed '/^\[info\] shell(0.*) \(my-fc\|gfortran\)/!d; s/^\[info\] shell(0.*) //' \
+    .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+my-fc -oo/hello.o -c -I./include $PWD/src/hello.f90
+my-fc -obin/hello.bin o/hello.o
+my-fc -oo/hello_house.o -c -I./include $PWD/src/hello_house.f90
+my-fc -obin/hello_house o/hello_house.o
+my-fc -oo/hello_office.o -c -I./include $PWD/src/hello_office.f90
+my-fc -obin/hello_office o/hello_office.o
+gfortran -oo/hello_road.o -c -I./include $PWD/src/hello_road.f90
+gfortran -obin/hello_road o/hello_road.o
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr"
+find build* -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+run_pass "$TEST_KEY" fcm make
+find build* -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+sed '/^\[info\] \(compile\|link\)   targets:/!d; s/total-time=.*$//' \
+    .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] compile   targets: modified=0, unchanged=1, failed=0, 
+[info] compile   targets: modified=0, unchanged=1, failed=0, 
+[info] compile   targets: modified=0, unchanged=1, failed=0, 
+[info] compile   targets: modified=0, unchanged=1, failed=0, 
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/bin/my-fc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/bin/my-fc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/bin/my-fc	(revision 5095)
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec gfortran "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,14 @@
+step.class[build_house build_office build_road]=build
+steps=build build_house build_office build_road
+build.prop{class,fc}=my-fc
+build.prop{class,file-ext.bin}=
+build.source=$HERE/src
+build.target=hello.bin
+build.prop{file-ext.bin}=.bin
+build_house.source=$HERE/src
+build_house.target=hello_house
+build_office.source=$HERE/src
+build_office.target=hello_office
+build_road.source=$HERE/src
+build_road.target=hello_road
+build_road.prop{fc}=gfortran
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello
+WRITE(*, '(A,1X,A)') 'Hello', 'World'
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_house.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_house.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_house.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello_house
+WRITE(*, '(A,1X,A)') 'Hello', 'House'
+end program hello_house
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_office.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_office.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_office.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello_office
+WRITE(*, '(A,1X,A)') 'Hello', 'Office'
+end program hello_office
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_road.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_road.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/12-build-class-prop/src/hello_road.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello_road
+WRITE(*, '(A,1X,A)') 'Hello', 'Road'
+end program hello_road
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop.t	(revision 5095)
@@ -0,0 +1,53 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test "fcm make", build.prop{*}[target].
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 5
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+PATH=$PWD/bin:$PATH
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+find build -type f | sort >"$TEST_KEY.find"
+file_cmp "$TEST_KEY.find" "$TEST_KEY.find" <<'__OUT__'
+build/bin/hello.exe
+build/include/world.mod
+build/o/hello.o
+build/o/world.o
+__OUT__
+sed '
+    /\[info\] shell(0.*) \(gfortran\|my-fc\)/!d;
+    /hello\.exe/d;
+    s/^\[info\] shell(0.*) //
+' .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+my-fc -oo/world.o -c -I./include $PWD/src/world.f90
+gfortran -oo/hello.o -c -I./include $PWD/src/hello.f90
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr"
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/bin/my-fc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/bin/my-fc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/bin/my-fc	(revision 5095)
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+exec gfortran "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps = build
+build.source = $HERE/src
+build.target{task} = link
+build.prop{fc}[world.o] = my-fc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/src/hello.f90	(revision 5095)
@@ -0,0 +1,4 @@
+program hello
+use world, only: get_world
+WRITE(*, '(A,A)') 'Hello ', trim(get_world())
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/src/world.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/src/world.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/13-build-target-prop/src/world.f90	(revision 5095)
@@ -0,0 +1,8 @@
+module world
+character(*), parameter :: world1 = 'Earth'
+contains
+elemental function get_world() result(w)
+character(len=len(world1)) :: w
+w = world1
+end function get_world
+end module world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc.t	(revision 5095)
@@ -0,0 +1,60 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test "fcm make", build etc files, broken at 2013-11 due to:
+# build.prop{class,file-she.script} = #!
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 5
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+PATH=$PWD/bin:$PATH
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+find build -type f | sort >"$TEST_KEY.find"
+file_cmp "$TEST_KEY.find" "$TEST_KEY.find" <<'__OUT__'
+build/bin/foo
+build/etc/.etc
+build/etc/hello.txt
+build/etc/hi/.etc
+build/etc/hi/hi-earth.txt
+build/etc/hi/hi-mars.txt
+__OUT__
+sed '
+    /\[info\] install/!d;
+    /\[info\] install  *targets:/d;
+    s/^\[info\] install *[^ ]* M //
+' .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+foo                  <- foo
+hello.txt            <- hello.txt
+hi/hi-earth.txt      <- hi/hi-earth.txt
+hi/hi-mars.txt       <- hi/hi-mars.txt
+hi/.etc              <- hi
+.etc                 <- 
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-incr"
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime.old"
+run_pass "$TEST_KEY" fcm make
+find build -type f -exec stat -c'%Y %n' {} \; | sort >"$TEST_KEY.mtime"
+file_cmp "$TEST_KEY.mtime" "$TEST_KEY.mtime.old" "$TEST_KEY.mtime"
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=install
+build.prop{class,file-she.script} = #!
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/foo
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/foo	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/foo	(revision 5095)
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+echo FOO
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hello.txt
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hello.txt	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hello.txt	(revision 5095)
@@ -0,0 +1,1 @@
+Hello World
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hi/hi-earth.txt
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hi/hi-earth.txt	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hi/hi-earth.txt	(revision 5095)
@@ -0,0 +1,1 @@
+Hi Earth
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hi/hi-mars.txt
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hi/hi-mars.txt	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/14-build-etc/src/hi/hi-mars.txt	(revision 5095)
@@ -0,0 +1,1 @@
+Hi Mars
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/15-extract-loc-reset.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/15-extract-loc-reset.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/15-extract-loc-reset.t	(revision 5095)
@@ -0,0 +1,182 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "extract", location reset and base eq diff cases.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+N_TESTS=34
+tests $N_TESTS
+#-------------------------------------------------------------------------------
+svnadmin create repos
+T_REPOS=file://$PWD/repos
+mkdir t
+echo "Hello World" >t/hello.txt
+echo "Hi World" >t/hi.txt
+svn import -q -m'Test' t/hello.txt $T_REPOS/trunk/hello.txt
+svn import -q -m'Test' t $T_REPOS/branch
+rm t/hello.txt t/hi.txt
+rmdir t
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg.0 <<__FCM_MAKE_CFG__
+steps=extract
+extract.ns=t
+extract.location{primary}[t]=$T_REPOS
+__FCM_MAKE_CFG__
+#-------------------------------------------------------------------------------
+base_tests() {
+    run_pass "$TEST_KEY" fcm make --new
+    find extract -type f >"$TEST_KEY.find"
+    file_cmp "$TEST_KEY.find" "$TEST_KEY.find" <<<'extract/t/hello.txt'
+    sed '/^\[info\] location     t: /!d; s/^\[info\] location     t: //' \
+        .fcm-make/log > "$TEST_KEY.log"
+    file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+0: $T_REPOS/trunk@2 (1)
+__LOG__
+}
+#-------------------------------------------------------------------------------
+diff_tests() {
+    run_pass "$TEST_KEY" fcm make --new
+    find extract -type f | sort >"$TEST_KEY.find"
+    file_cmp "$TEST_KEY.find" "$TEST_KEY.find" <<'__FIND__'
+extract/t/hello.txt
+extract/t/hi.txt
+__FIND__
+    sed '/^\[info\] location     t: /!d; s/^\[info\] location     t: //' \
+        .fcm-make/log > "$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+0: $T_REPOS/trunk@2 (1)
+1: $T_REPOS/branch@2 (2)
+__LOG__
+}
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-primary"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location{primary}[t]="
+    echo "extract.location[t]=$T_REPOS/trunk"
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-base-0"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location[t]="
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-base-0-with-eq-diff"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location{diff}[t]=$T_REPOS/trunk"
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-base"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location[t]=$T_REPOS/trunk"
+    echo "extract.location[t]="
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-base-with-eq-diff"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location[t]=$T_REPOS/trunk"
+    echo "extract.location{diff}[t]=$T_REPOS/trunk"
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-base-with-diff"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location[t]=$T_REPOS/trunk"
+    echo "extract.location{diff}[t]=$T_REPOS/branch"
+    echo "extract.location[t]="
+} >fcm-make.cfg
+diff_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-base-with-1-eq-diff"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location[t]=$T_REPOS/trunk"
+    echo "extract.location{diff}[t]=$T_REPOS/trunk $T_REPOS/branch"
+    echo "extract.location[t]="
+} >fcm-make.cfg
+diff_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-diff-0"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location{diff}[t]="
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-diff"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location{diff}[t]=$T_REPOS/branch"
+    echo "extract.location{diff}[t]="
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-diff-with-base"
+{
+    cat fcm-make.cfg.0
+    echo "extract.location{diff}[t]=$T_REPOS/branch"
+    echo "extract.location[t]=$T_REPOS/trunk"
+    echo "extract.location{diff}[t]="
+} >fcm-make.cfg
+base_tests
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-inherit-base-eq-my-diff"
+mkdir -p i0 i1
+cat fcm-make.cfg.0 >i0/fcm-make.cfg
+fcm make --new -q -C i0
+{
+    echo 'use=$HERE/../i0'
+    echo "extract.location{diff}[t]=$T_REPOS/trunk"
+} >i1/fcm-make.cfg
+run_pass "$TEST_KEY" fcm make --new -C i1
+sed '/^\[info\] location     t: /!d; s/^\[info\] location     t: //' \
+    i1/.fcm-make/log > "$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+0: $T_REPOS/trunk@2 (1)
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-inherit-base-eq-1-of-my-diff"
+# N.B. The 3 lines below have been done in the test above.
+#      Uncomment them if the above test is removed.
+#mkdir -p i0 i1
+#cat fcm-make.cfg.0 >i0/fcm-make.cfg
+#fcm make --new -q -C i0
+{
+    echo 'use=$HERE/../i0'
+    echo "extract.location{diff}[t]=$T_REPOS/branch $T_REPOS/trunk"
+} >i1/fcm-make.cfg
+run_pass "$TEST_KEY" fcm make --new -C i1
+sed '/^\[info\] location     t: /!d; s/^\[info\] location     t: //' \
+    i1/.fcm-make/log > "$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+0: $T_REPOS/trunk@2 (1)
+1: $T_REPOS/branch@2 (2)
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2.t	(revision 5095)
@@ -0,0 +1,50 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", "build.prop{dep.o}" top namespace, complicated by a module.
+# See also "09-build.dep-o.t".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 2
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+sed '/^\[info\] \(source->target\|target\) /!d' .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] source->target / -> (archive) lib/ libo.a
+[info] source->target hello.f90 -> (link) bin/ hello.exe
+[info] source->target hello.f90 -> (install) include/ hello.f90
+[info] source->target hello.f90 -> (compile) o/ hello.o
+[info] source->target hello_mod.f90 -> (install) include/ hello_mod.f90
+[info] source->target hello_mod.f90 -> (compile+) include/ hello_mod.mod
+[info] source->target hello_mod.f90 -> (compile) o/ hello_mod.o
+[info] source->target hello_sub.f90 -> (install) include/ hello_sub.f90
+[info] source->target hello_sub.f90 -> (ext-iface) include/ hello_sub.interface
+[info] source->target hello_sub.f90 -> (compile) o/ hello_sub.o
+[info] target hello.exe
+[info] target  - hello.o
+[info] target  - hello_mod.o
+[info] target  - hello_sub.o
+[info] target  -  - hello_mod.mod
+[info] target  -  -  - hello_mod.o
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{dep.o}=hello_sub.o
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello
+call hello_sub()
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello_mod.f90	(revision 5095)
@@ -0,0 +1,3 @@
+module hello_mod
+character(*), parameter :: greet='Hello'
+end module hello_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello_sub.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello_sub.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/16-build-dep-o-2/src/hello_sub.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine hello_sub
+use hello_mod, only: greet
+write(*, '(a,1x,a)') greet, 'world'
+end subroutine hello_sub
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic.t	(revision 5095)
@@ -0,0 +1,44 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", self cyclic dependency.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 2
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_fail "$TEST_KEY" fcm make
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERR__'
+[FAIL] baz.mod: target depends on itself
+[FAIL]     required by: bar.o
+[FAIL]     required by: bar.mod
+[FAIL]     required by: baz.o
+[FAIL]     required by: baz.mod
+[FAIL]     required by: foo.o
+[FAIL]     required by: foo.mod
+[FAIL]     required by: meow.o
+[FAIL]     required by: meow.mod
+[FAIL]     required by: hello.o
+[FAIL]     required by: hello.exe
+
+__ERR__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/bar.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/bar.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/bar.f90	(revision 5095)
@@ -0,0 +1,9 @@
+module bar
+use quack,  only: quack_type
+use baz, only: baz_type
+implicit none
+private
+
+type, public, abstract, extends(quack_type) :: bar_type
+end type
+end module bar
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/baz.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/baz.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/baz.f90	(revision 5095)
@@ -0,0 +1,10 @@
+module baz
+use quack, only: quack_type
+use bar, only: bar_type
+implicit none
+private
+
+type, public, abstract :: baz_type
+end type
+
+end module baz
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/foo.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/foo.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/foo.f90	(revision 5095)
@@ -0,0 +1,10 @@
+module foo
+use bar, only: bar_type
+use baz, only: baz_type
+implicit none
+private
+
+type, public, extends(bar_type) :: foo_type
+end type
+
+end module foo
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/hello.f90	(revision 5095)
@@ -0,0 +1,7 @@
+program hello 
+  use foo, only: foo_type
+  use meow, only: meow_type
+  use baz, only: baz_type
+  implicit none
+  write(*, '(A)') 'Hello'
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/meow.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/meow.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/meow.f90	(revision 5095)
@@ -0,0 +1,11 @@
+module meow
+use quack,  only: quack_type
+use baz, only: baz_type
+use foo,      only: foo_type
+implicit none
+private
+
+type, public, extends(baz_type) :: meow_type
+end type
+
+end module meow
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/quack.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/quack.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/17-build-cyclic/src/quack.f90	(revision 5095)
@@ -0,0 +1,9 @@
+module quack
+implicit none
+private
+
+type, public, abstract :: quack_type
+  private
+end type
+
+end module quack
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic.t	(revision 5095)
@@ -0,0 +1,60 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", build, Fortran source file, "use, intrinsic" statement.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 7
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+sed '/^\[info\] target /!d' .fcm-make/log >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] target greet.exe
+[info] target  - greet.o
+[info] target  -  - hello.interface
+[info] target  -  - hi.interface
+[info] target  - hello.o
+[info] target  - hi.o
+__LOG__
+file_cmp "$TEST_KEY.hello.interface" "build/include/hello.interface" \
+    <<'__INTERFACE__'
+interface
+subroutine hello()
+end subroutine hello
+end interface
+__INTERFACE__
+file_cmp "$TEST_KEY.hi.interface" "build/include/hi.interface" \
+    <<'__INTERFACE__'
+interface
+subroutine hi()
+use, intrinsic :: iso_fortran_env
+end subroutine hi
+end interface
+__INTERFACE__
+run_pass "$TEST_KEY.exe" ./build/bin/greet.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY.exe.out" <<'__OUT__'
+Hello
+Hi
+__OUT__
+file_cmp "$TEST_KEY.exe.err" "$TEST_KEY.exe.err" </dev/null
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/greet.f90	(revision 5095)
@@ -0,0 +1,14 @@
+program greet
+implicit none
+include 'hello.interface'
+include 'hi.interface'
+abstract interface
+subroutine abstract_greet()
+end subroutine abstract_greet
+end interface
+procedure(abstract_greet), pointer :: say
+say => hello
+call say()
+say => hi
+call say()
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/hello.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine hello()
+use, intrinsic :: iso_fortran_env, only: output_unit
+write(output_unit, '(a)') 'Hello'
+end subroutine hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/hi.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/hi.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/18-build-use-intrinsic/src/hi.f90	(revision 5095)
@@ -0,0 +1,4 @@
+subroutine hi()
+use, intrinsic :: iso_fortran_env !, only: output_unit
+write(output_unit, '(a)') 'Hi'
+end subroutine hi
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop.t	(revision 5095)
@@ -0,0 +1,50 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", ensure that properties can be declared before or after use=
+# declaration.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 2
+set -e
+mkdir -p i0 i1 i2
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* i0
+fcm make -q -C i0
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i1"
+cat >i1/fcm-make.cfg <<'__FCM_MAKE_CFG__'
+use=$HERE/../i0
+build.prop{fc.defs}=WORLD='"Mars"'
+__FCM_MAKE_CFG__
+fcm make -q -C i1
+$PWD/i1/build/bin/hello.exe >"$TEST_KEY.exe.out"
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY.exe.out" <<<'Hello Mars'
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i2"
+cat >i2/fcm-make.cfg <<'__FCM_MAKE_CFG__'
+build.prop{fc.defs}=WORLD='"Venus"'
+use=$HERE/../i0
+__FCM_MAKE_CFG__
+fcm make -q -C i2
+$PWD/i2/build/bin/hello.exe >"$TEST_KEY.exe.out"
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY.exe.out" <<<'Hello Venus'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{fc.defs}=WORLD='"Earth"'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop/src/hello.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop/src/hello.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/19-build-inherit-prop/src/hello.F90	(revision 5095)
@@ -0,0 +1,6 @@
+program hello
+#ifndef WORLD
+#define WORLD "World"
+#endif
+write(*, '(a,1x,a)') 'Hello', WORLD
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args.t	(revision 5095)
@@ -0,0 +1,50 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", CLI arguments.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 6
+set -e
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-hello"
+run_pass "$TEST_KEY" fcm make 'build.prop{file-ext.bin}=' 'build.target=hello'
+grep '^\[info\] required-target:' .fcm-make/log >$TEST_KEY.log
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] required-target: link      bin     hello
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-greet"
+run_pass "$TEST_KEY" fcm make 'build.target=greet.exe'
+grep '^\[info\] required-target:' .fcm-make/log >$TEST_KEY.log
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] required-target: link      bin     greet.exe
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-new-greet"
+run_pass "$TEST_KEY" fcm make --new 'build.target=greet.exe'
+grep '^\[info\] required-target:' .fcm-make/log >$TEST_KEY.log
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<'__LOG__'
+[info] required-target: link      bin     greet.exe
+__LOG__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+steps=build
+build.source=$HERE/src
+build.target=hello.exe
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/src/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/src/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/src/greet.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program greet
+write(*, '(a)') 'Greetings'
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/20-args/src/hello.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello
+write(*, '(a)') 'Hello'
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps.t	(revision 5095)
@@ -0,0 +1,68 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", ensure that steps can be declared before or after use=
+# declaration.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 3
+set -e
+mkdir -p i0 i1 i2 i3
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* i0
+fcm make -q -C i0
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i1"
+cat >i1/fcm-make.cfg <<'__FCM_MAKE_CFG__'
+use=$HERE/../i0
+__FCM_MAKE_CFG__
+fcm make -q -C i1
+find i1/*/bin -type f | sort >"$TEST_KEY.ls"
+file_cmp "$TEST_KEY.ls" "$TEST_KEY.ls" <<'__LIST__'
+i1/build1/bin/hello.exe
+i1/build2/bin/salute.exe
+i1/build3/bin/greet.exe
+__LIST__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i2"
+cat >i2/fcm-make.cfg <<'__FCM_MAKE_CFG__'
+step.class[build1 build2]=build
+steps=build1 build2
+use=$HERE/../i0
+__FCM_MAKE_CFG__
+fcm make -q -C i2
+find i2/*/bin -type f | sort >"$TEST_KEY.ls"
+file_cmp "$TEST_KEY.ls" "$TEST_KEY.ls" <<'__LIST__'
+i2/build1/bin/hello.exe
+i2/build2/bin/salute.exe
+__LIST__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i3"
+cat >i3/fcm-make.cfg <<'__FCM_MAKE_CFG__'
+use=$HERE/../i0
+steps=build3
+__FCM_MAKE_CFG__
+fcm make -q -C i3
+find i3/*/bin -type f | sort >"$TEST_KEY.ls"
+file_cmp "$TEST_KEY.ls" "$TEST_KEY.ls" <<'__LIST__'
+i3/build3/bin/greet.exe
+__LIST__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+step.class[build1 build2 build3]=build
+steps=build1 build2 build3
+build1.source=$HERE/src1
+build1.target{task}=link
+build2.source=$HERE/src2
+build2.target{task}=link
+build3.source=$HERE/src3
+build3.target{task}=link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src1/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src1/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src1/hello.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello
+write(*, '(a)') 'Hello!'
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src2/salute.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src2/salute.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src2/salute.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program salute
+write(*, '(a)') 'Salute!'
+end program salute
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src3/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src3/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/21-inherit-steps/src3/greet.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program greet
+write(*, '(a)') 'Greeting!'
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit.t	(revision 5095)
@@ -0,0 +1,59 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", inherit build correctness. metomi/fcm#110
+# * 2 source files with bad syntax override.
+# * Build fails on first source file.
+# * Fix first source file.
+# * Build should fail on second source file.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 7
+set -e
+mkdir -p i0 i1
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/{fcm-make.cfg,src} i0
+fcm make -q -C i0
+set +e
+#-------------------------------------------------------------------------------
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/src-i i1/src
+cat >i1/fcm-make.cfg <<'__CFG__'
+use=$HERE/../i0
+build.source=$HERE/src
+__CFG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i1-1"
+run_fail "$TEST_KEY" fcm make -q -C i1
+file_grep "$TEST_KEY.err" '\[FAIL\].*i1/src/m1.f90' "$TEST_KEY.err"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i1-2"
+sed 's/^writ/write/' $TEST_SOURCE_DIR/$TEST_KEY_BASE/src-i/m1.f90 >i1/src/m1.f90
+run_fail "$TEST_KEY" fcm make -q -C i1
+file_grep "$TEST_KEY.err" '\[FAIL\].*i1/src/m2.f90' "$TEST_KEY.err"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-i1-3"
+sed 's/^writ/write/' $TEST_SOURCE_DIR/$TEST_KEY_BASE/src-i/m2.f90 >i1/src/m2.f90
+run_pass "$TEST_KEY" fcm make -q -C i1 --new
+run_pass "$TEST_KEY.exe" $PWD/i1/build/bin/p1.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY.exe.out" <<'__OUT__'
+Greet from m1-s1!
+Greet from m2-s2!
+__OUT__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+steps=build
+build.source=$HERE/src
+build.target=p1.exe
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src-i/m1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src-i/m1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src-i/m1.f90	(revision 5095)
@@ -0,0 +1,6 @@
+module m1
+contains
+subroutine s1()
+writ(*, '(a)') 'Greet from m1-s1!'
+end subroutine s1
+end module m1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src-i/m2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src-i/m2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src-i/m2.f90	(revision 5095)
@@ -0,0 +1,6 @@
+module m2
+contains
+subroutine s2()
+writ(*, '(a)') 'Greet from m2-s2!'
+end subroutine s2
+end module m2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/m1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/m1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/m1.f90	(revision 5095)
@@ -0,0 +1,6 @@
+module m1
+contains
+subroutine s1()
+write(*, '(a)') 'Hello from m1:s1!'
+end subroutine s1
+end module m1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/m2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/m2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/m2.f90	(revision 5095)
@@ -0,0 +1,6 @@
+module m2
+contains
+subroutine s2()
+write(*, '(a)') 'Hello from m2:s2!'
+end subroutine s2
+end module m2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/p1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/p1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/22-build-2-bad-mod-over-inherit/src/p1.f90	(revision 5095)
@@ -0,0 +1,6 @@
+program p1
+use m1, only: s1
+use m2, only: s2
+call s1()
+call s2()
+end program p1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp.t	(revision 5095)
@@ -0,0 +1,68 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", build detects dependencies in OMP sentinels.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 16
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+printf '6.0\n%.0s' {1..100} >"$TEST_KEY_BASE.exe.on.out"
+printf '1.0\n%.0s' {1..100} >"$TEST_KEY_BASE.exe.off.out"
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-on # fc.flag-omp on in new mode
+run_pass "$TEST_KEY" fcm make
+grep ' !\$' fcm-make.log | sort >"$TEST_KEY.log.deps"
+sort >"${TEST_KEY}.log.deps.expected" <<'__LOG__'
+[info]              -> (  include) !$i1.f90
+[info]              -> (  include) !$i2.f90
+[info]              -> ( f.module) !$m1
+[info]              -> ( f.module) !$m2
+__LOG__
+file_cmp "${TEST_KEY}.log.deps" \
+    "${TEST_KEY}.log.deps" "${TEST_KEY}.log.deps.expected"
+run_pass "$TEST_KEY.exe" $PWD/build/bin/p1.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY_BASE.exe.on.out" "$TEST_KEY.exe.out"
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-on-off # fc.flag-omp on->off in incremental mode
+echo 'build.prop{fc.flag-omp}=' >>fcm-make.cfg
+run_pass "$TEST_KEY" fcm make
+run_pass "$TEST_KEY.exe" $PWD/build/bin/p1.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY_BASE.exe.off.out" "$TEST_KEY.exe.out"
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-on-off-on # fc.flag-omp on->off->on in incremental mode
+echo 'build.prop{fc.flag-omp}=-fopenmp' >>fcm-make.cfg
+run_pass "$TEST_KEY" fcm make
+run_pass "$TEST_KEY.exe" $PWD/build/bin/p1.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY_BASE.exe.on.out" "$TEST_KEY.exe.out"
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-off # fc.flag-omp off in new mode
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/fcm-make.cfg .
+echo 'build.prop{fc.flag-omp}=' >>fcm-make.cfg
+run_pass "$TEST_KEY" fcm make --new
+run_pass "$TEST_KEY.exe" $PWD/build/bin/p1.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY_BASE.exe.off.out" "$TEST_KEY.exe.out"
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-off-on # fc.flag-omp off->on in incremental mode
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/fcm-make.cfg .
+run_pass "$TEST_KEY" fcm make
+run_pass "$TEST_KEY.exe" $PWD/build/bin/p1.exe
+file_cmp "$TEST_KEY.exe.out" "$TEST_KEY_BASE.exe.on.out" "$TEST_KEY.exe.out"
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target=p1.exe
+build.prop{fc.flag-omp}=-fopenmp
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/i1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/i1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/i1.f90	(revision 5095)
@@ -0,0 +1,1 @@
+call s1(n, y, x)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/i2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/i2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/i2.f90	(revision 5095)
@@ -0,0 +1,1 @@
+call s2(n, z, y)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/m1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/m1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/m1.f90	(revision 5095)
@@ -0,0 +1,14 @@
+module m1
+contains
+subroutine s1(n, y, x)
+integer, intent(in) :: n
+real, intent(out) :: y(:)
+real, intent(in) :: x(:)
+integer :: i
+!$omp parallel do shared(y)
+do i = 1, n
+    y(i) = x(i) * 2.0
+end do
+!$omp end parallel do
+end subroutine s1
+end module m1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/m2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/m2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/m2.f90	(revision 5095)
@@ -0,0 +1,14 @@
+module m2
+contains
+subroutine s2(n, z, y)
+integer, intent(in) :: n
+real, intent(out) :: z(:)
+real, intent(in) :: y(:)
+integer :: i
+!$omp parallel do shared(z)
+do i = 1, n
+    z(i) = y(i) * 3.0
+end do
+!$omp end parallel do
+end subroutine s2
+end module m2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/p1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/p1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/p1.f90	(revision 5095)
@@ -0,0 +1,21 @@
+program p1
+
+!$ use omp_lib
+!$ use m1, only: s1
+
+integer, parameter :: n=100
+integer :: i
+real :: x(n), y(n), z(n)
+
+include 's3.interface'
+
+x(:) = 1.0
+y(:) = 1.0
+z(:) = 1.0
+!$ include "i1.f90"
+call s3(n, z, y)
+do i = 1, n
+    write(*, '(f3.1)') z(i)
+end do
+
+end program p1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/s3.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/s3.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/23-build-omp/src/s3.f90	(revision 5095)
@@ -0,0 +1,7 @@
+subroutine s3(n, z, y)
+!$ use m2, only: s2
+integer, intent(in) :: n
+real, intent(out) :: z(:)
+real, intent(in) :: y(:)
+!$ include "i2.f90"
+end subroutine s3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel.t	(revision 5095)
@@ -0,0 +1,36 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test build C source file with mixed case name and has main function.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 3
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY" fcm make
+grep '^\[info\] target ' fcm-make.log >"$TEST_KEY.target.log"
+file_cmp "$TEST_KEY.target.log" "$TEST_KEY.target.log" <<'__LOG__'
+[info] target Hello
+[info] target  - hello.o
+__LOG__
+run_pass "$TEST_KEY.chello" $PWD/build/bin/Hello
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{file-ext.bin}=
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel/src/Hello.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel/src/Hello.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/24-build-c-main-camel/src/Hello.c	(revision 5095)
@@ -0,0 +1,5 @@
+#include <stdio.h>
+int main(void) {
+    printf("Hello World\n");
+    return 0;
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2.t	(revision 5095)
@@ -0,0 +1,40 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", adjacent cyclic dependency.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 2
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_fail "$TEST_KEY" fcm make
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERR__'
+[FAIL] m1.mod: target depends on itself
+[FAIL]     required by: m2.o
+[FAIL]     required by: m2.mod
+[FAIL]     required by: m1.o
+[FAIL]     required by: m1.mod
+[FAIL]     required by: foo.o
+[FAIL]     required by: foo.exe
+
+__ERR__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/foo.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/foo.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/foo.f90	(revision 5095)
@@ -0,0 +1,4 @@
+program foo
+use m1, only s1
+call s1()
+end program foo
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/m1.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/m1.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/m1.f90	(revision 5095)
@@ -0,0 +1,12 @@
+module m1
+character(*), parameter :: WHATEVER
+contains
+subroutine s1()
+write(*, '(a)') f1() // ' from s1'
+end subroutine s1
+function f1()
+use m2, only: HELLO
+character(len=len(HELLO)) :: f1
+f1 = hello
+end function f1
+end module m1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/m2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/m2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/25-build-cyclic-2/src/m2.f90	(revision 5095)
@@ -0,0 +1,8 @@
+module m2
+use m1, only: WHATEVER
+character(*), parameter :: HELLO='Hello'
+contains
+subroutine s2()
+write(*, '(a)') HELLO // ' from s2'
+end subroutine s2
+end module m2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/26-no-config.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/26-no-config.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/26-no-config.t	(revision 5095)
@@ -0,0 +1,33 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", no configuration and no arguments.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 2
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_fail "$TEST_KEY" fcm make
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERR__'
+[FAIL] no configuration specified or found
+
+__ERR__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/27-args-only.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/27-args-only.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/27-args-only.t	(revision 5095)
@@ -0,0 +1,41 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", no configuration files, only CLI arguments.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 3
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+run_pass "$TEST_KEY" fcm make \
+    'steps=build' "build.source=$PWD/src" 'build.target=hello.exe'
+file_test "$TEST_KEY.hello.exe" $PWD/build/bin/hello.exe
+$PWD/build/bin/hello.exe >"$TEST_KEY.hello.exe.out"
+file_cmp "$TEST_KEY.hello.exe.out" "$TEST_KEY.hello.exe.out" <<'__OUT__'
+Hello World!
+__OUT__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/28-bad-arg.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/28-bad-arg.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/28-bad-arg.t	(revision 5095)
@@ -0,0 +1,41 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", bad arguments.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 4
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_fail "$TEST_KEY" fcm make 'foo'
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERROR__'
+[FAIL] arg 0 (foo): invalid config declaration
+
+__ERROR__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-cfg"
+run_fail "$TEST_KEY" fcm make 'foo.cfg'
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERROR__'
+[FAIL] arg 0 (foo.cfg): invalid config declaration
+[FAIL] did you mean "-f foo.cfg"?
+
+__ERROR__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/29-relative-cfg.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/29-relative-cfg.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/29-relative-cfg.t	(revision 5095)
@@ -0,0 +1,85 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", config as relative paths
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+
+clean() {
+    rm -fr \
+        .fcm-make \
+        build \
+        fcm-make-as-parsed.cfg \
+        fcm-make-on-success.cfg \
+        fcm-make.log
+}
+#-------------------------------------------------------------------------------
+tests 11
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+
+mkdir etc
+cat >etc/fcm-make.cfg <<'__CFG__'
+steps=build
+build.source=$HERE/../src
+build.target=hello.exe
+__CFG__
+
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+
+#-------------------------------------------------------------------------------
+clean
+run_fail "$TEST_KEY-control" fcm make
+file_cmp "$TEST_KEY-control.err" "$TEST_KEY-control.err" <<'__ERROR__'
+[FAIL] no configuration specified or found
+
+__ERROR__
+#-------------------------------------------------------------------------------
+clean
+run_pass "$TEST_KEY-pwd" fcm make -f etc/fcm-make.cfg
+file_test "$TEST_KEY.hello.exe" $PWD/build/bin/hello.exe
+$PWD/build/bin/hello.exe >"$TEST_KEY.hello.exe.out"
+file_cmp "$TEST_KEY.hello.exe.out" "$TEST_KEY.hello.exe.out" <<'__OUT__'
+Hello World!
+__OUT__
+#-------------------------------------------------------------------------------
+clean
+run_pass "$TEST_KEY-path" fcm make -F $PWD/etc
+file_test "$TEST_KEY.hello.exe" $PWD/build/bin/hello.exe
+$PWD/build/bin/hello.exe >"$TEST_KEY.hello.exe.out"
+file_cmp "$TEST_KEY.hello.exe.out" "$TEST_KEY.hello.exe.out" <<'__OUT__'
+Hello World!
+__OUT__
+#-------------------------------------------------------------------------------
+clean
+cd src
+run_pass "$TEST_KEY-path" fcm make -C .. -f etc/fcm-make.cfg
+file_test "$TEST_KEY.hello.exe" ../build/bin/hello.exe
+../build/bin/hello.exe >"$TEST_KEY.hello.exe.out"
+file_cmp "$TEST_KEY.hello.exe.out" "$TEST_KEY.hello.exe.out" <<'__OUT__'
+Hello World!
+__OUT__
+cd ..
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/30-relative-cfg-in-svn.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/30-relative-cfg-in-svn.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/30-relative-cfg-in-svn.t	(revision 5095)
@@ -0,0 +1,52 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", relative config in a Subversion repository
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 9
+#-------------------------------------------------------------------------------
+mkdir etc
+cat >etc/fcm-make.cfg <<'__CFG__'
+steps=build
+build.source=src
+build.target=hello.exe
+__CFG__
+
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+
+svnadmin create svn-repos
+svn import -m 'test stuff' etc file://$PWD/svn-repos/etc
+rm -fr etc
+
+#-------------------------------------------------------------------------------
+fcm_make_build_hello_tests "$TEST_KEY_BASE" \
+    '.exe' -F "file://$PWD/svn-repos" -f 'etc/fcm-make.cfg'
+fcm_make_build_hello_tests "$TEST_KEY_BASE-1" \
+    '.exe' -F "file://$PWD/svn-repos@1" -f 'etc/fcm-make.cfg'
+fcm_make_build_hello_tests "$TEST_KEY_BASE-HEAD" \
+    '.exe' -F "file://$PWD/svn-repos@HEAD" -f 'etc/fcm-make.cfg'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/31-relative-cfg-in-ssh.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/31-relative-cfg-in-ssh.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/31-relative-cfg-in-ssh.t	(revision 5095)
@@ -0,0 +1,69 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", relative config in a remote host accessible via SSH
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+# Get a remote host for testing
+T_HOST=
+for FILE in $HOME/.metomi/fcm/t.cfg $FCM_HOME/etc/fcm/t.cfg; do
+    if [[ ! -f $FILE || ! -r $FILE ]]; then
+        continue
+    fi
+    T_HOST=$(fcm cfg $FILE | sed '/^ *host *=/!d; s/^ *host *= *//' | tail -1)
+    if [[ -n $T_HOST ]]; then
+        break
+    fi
+done
+if [[ -z $T_HOST ]]; then
+    skip_all 'fcm/t.cfg: "host" not defined'
+fi
+#-------------------------------------------------------------------------------
+tests 3
+#-------------------------------------------------------------------------------
+mkdir etc
+cat >etc/fcm-make.cfg <<'__CFG__'
+steps=build
+build.source=src
+build.target=hello.exe
+__CFG__
+
+T_HOST_WORK_DIR=$(ssh -oBatchMode=yes $T_HOST mktemp -d)
+rsync -a etc $T_HOST:$T_HOST_WORK_DIR
+rm -r etc
+
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_pass "$TEST_KEY-pwd" fcm make -F $T_HOST:$T_HOST_WORK_DIR/etc
+file_test "$TEST_KEY.hello.exe" $PWD/build/bin/hello.exe
+$PWD/build/bin/hello.exe >"$TEST_KEY.hello.exe.out"
+file_cmp "$TEST_KEY.hello.exe.out" "$TEST_KEY.hello.exe.out" <<'__OUT__'
+Hello World!
+__OUT__
+#-------------------------------------------------------------------------------
+ssh -oBatchMode=yes $T_HOST rm -r $T_HOST_WORK_DIR
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/32-include-relative-cfg.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/32-include-relative-cfg.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/32-include-relative-cfg.t	(revision 5095)
@@ -0,0 +1,54 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", include relative config
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 6
+#-------------------------------------------------------------------------------
+mkdir etc
+cat >etc/fcm-make-build.cfg <<'__CFG__'
+steps=build
+build.source=src
+build.target=hello.exe
+__CFG__
+
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include = fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-config-file-path" '.exe' -F $PWD/etc
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include-path=$HERE/etc
+include=fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-include-path" '.exe'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/33-include-relative-cfg-in-svn.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/33-include-relative-cfg-in-svn.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/33-include-relative-cfg-in-svn.t	(revision 5095)
@@ -0,0 +1,73 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", include relative config in a Subversion repository
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 12
+#-------------------------------------------------------------------------------
+mkdir etc
+cat >etc/fcm-make-build.cfg <<'__CFG__'
+steps=build
+build.source=src
+build.target=hello.exe
+__CFG__
+
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+
+svnadmin create svn-repos
+svn import -m 'test stuff' etc file://$PWD/svn-repos/etc
+rm -fr etc
+
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include = fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests \
+    "$TEST_KEY_BASE-config-file-path" '.exe' -F "file://$PWD/svn-repos/etc"
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include = fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests\
+    "$TEST_KEY_BASE-config-file-path-1" '.exe' -F "file://$PWD/svn-repos/etc@1"
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include-path=file://$PWD/svn-repos/etc
+include=fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-include-paths" '.exe'
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include-path=file://$PWD/svn-repos/etc@1
+include=fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-include-path-1" '.exe'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/34-include-relative-cfg-in-ssh.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/34-include-relative-cfg-in-ssh.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/34-include-relative-cfg-in-ssh.t	(revision 5095)
@@ -0,0 +1,75 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", include relative config in a remote host accessible via SSH
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+# Get a remote host for testing
+T_HOST=
+for FILE in $HOME/.metomi/fcm/t.cfg $FCM_HOME/etc/fcm/t.cfg; do
+    if [[ ! -f $FILE || ! -r $FILE ]]; then
+        continue
+    fi
+    T_HOST=$(fcm cfg $FILE | sed '/^ *host *=/!d; s/^ *host *= *//' | tail -1)
+    if [[ -n $T_HOST ]]; then
+        break
+    fi
+done
+if [[ -z $T_HOST ]]; then
+    skip_all 'fcm/t.cfg: "host" not defined'
+fi
+#-------------------------------------------------------------------------------
+tests 6
+#-------------------------------------------------------------------------------
+mkdir etc
+cat >etc/fcm-make-build.cfg <<'__CFG__'
+steps=build
+build.source=src
+build.target=hello.exe
+__CFG__
+
+T_HOST_WORK_DIR=$(ssh -oBatchMode=yes $T_HOST mktemp -d)
+rsync -a etc $T_HOST:$T_HOST_WORK_DIR
+rm -r etc
+
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include = fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-config-file-path" '.exe' \
+    -F "$T_HOST:$T_HOST_WORK_DIR/etc"
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<__CFG__
+include-path=$T_HOST:$T_HOST_WORK_DIR/etc
+include=fcm-make-build.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-include-paths" '.exe'
+#-------------------------------------------------------------------------------
+ssh -oBatchMode=yes $T_HOST rm -r $T_HOST_WORK_DIR
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/35-include-relative-cfg-in-2-dirs.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/35-include-relative-cfg-in-2-dirs.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/35-include-relative-cfg-in-2-dirs.t	(revision 5095)
@@ -0,0 +1,62 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", include relative config
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 6
+#-------------------------------------------------------------------------------
+mkdir cfg1 cfg2
+cat >cfg1/fcm-make-head.cfg <<'__CFG__'
+steps=build
+build.source=src
+__CFG__
+cat >cfg1/fcm-make-tail.cfg <<'__CFG__'
+build.target=hello.exe
+__CFG__
+cat >cfg2/fcm-make-tail.cfg <<'__CFG__'
+build.target=hello
+build.prop{file-ext.bin}=
+__CFG__
+
+mkdir src
+cat >src/hello.f90 <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include-path = $HERE/cfg1 $HERE/cfg2
+include = fcm-make-head.cfg fcm-make-tail.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-1" '.exe'
+#-------------------------------------------------------------------------------
+cat >fcm-make.cfg <<'__CFG__'
+include-path = $HERE/cfg2
+include-path{+} = $HERE/cfg1
+include = fcm-make-head.cfg fcm-make-tail.cfg
+__CFG__
+
+fcm_make_build_hello_tests "$TEST_KEY_BASE-2" ''
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic.t	(revision 5095)
@@ -0,0 +1,132 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", build, continue on failure.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+
+task_lines_from_log() {
+    sed '
+        /\[\(info\|FAIL\)\] \(compile\+\?\|ext-iface\|install\|link\)  *\(----\|[0-9][0-9]*\.[0-9][0-9]*\) /!d
+        s/  [0-9][0-9]*\.[0-9][0-9]* / ???? /
+    ' fcm-make.log
+}
+
+fail_lines_from_log() {
+    sed '/\[FAIL\] !/!d' fcm-make.log
+}
+
+#-------------------------------------------------------------------------------
+tests 12
+#-------------------------------------------------------------------------------
+cp -r $TEST_SOURCE_DIR/$TEST_KEY_BASE/* .
+#-------------------------------------------------------------------------------
+# Break hello1 and hello2
+TEST_KEY="$TEST_KEY_BASE-1-2-bad"
+sed -i 's/implicit none/implicit non/' src/greet_mod.f90  # introduce typo
+run_fail "$TEST_KEY" fcm make --new
+task_lines_from_log >"$TEST_KEY-log-tasks"
+file_cmp "$TEST_KEY-log-tasks" "$TEST_KEY-log-tasks" <<'__LOG__'
+[info] compile   ???? M world_mod.o          <- world_mod.f90
+[FAIL] compile   ???? ! greet_mod.o          <- greet_mod.f90
+[FAIL] compile   ---- ! hello2.o             <- hello2.f90
+[info] compile   ???? M hello_sub.o          <- hello_sub.f90
+[info] ext-iface ???? M hello_sub.interface  <- hello_sub.f90
+[info] compile   ???? M hello3.o             <- hello3.f90
+[info] link      ???? M hello3               <- hello3.f90
+[info] compile   ???? M hello4.o             <- hello4.f90
+[info] link      ???? M hello4               <- hello4.f90
+__LOG__
+fail_lines_from_log >"$TEST_KEY-log-fails"
+file_cmp "$TEST_KEY-log-fails" "$TEST_KEY-log-fails" <<'__LOG__'
+[FAIL] ! greet_mod.mod       : depends on failed target: greet_mod.o
+[FAIL] ! greet_mod.o         : update task failed
+[FAIL] ! hello2              : depends on failed target: hello2.o
+[FAIL] ! hello2.o            : depends on failed target: greet_mod.mod
+__LOG__
+
+TEST_KEY="$TEST_KEY_BASE-1-2-fix"
+sed -i 's/implicit non/implicit none/' src/greet_mod.f90  # fix typo
+run_pass "$TEST_KEY" fcm make
+task_lines_from_log >"$TEST_KEY-log-tasks"
+file_cmp "$TEST_KEY-log-tasks" "$TEST_KEY-log-tasks" <<'__LOG__'
+[info] compile   ---- U world_mod.o          <- world_mod.f90
+[info] compile   ???? M greet_mod.o          <- greet_mod.f90
+[info] compile   ???? M hello.o              <- hello.f90
+[info] link      ???? M hello                <- hello.f90
+[info] compile   ???? M hello2.o             <- hello2.f90
+[info] link      ???? M hello2               <- hello2.f90
+[info] compile   ---- U hello_sub.o          <- hello_sub.f90
+[info] ext-iface ---- U hello_sub.interface  <- hello_sub.f90
+[info] compile   ---- U hello3.o             <- hello3.f90
+[info] link      ---- U hello3               <- hello3.f90
+[info] compile   ---- U hello4.o             <- hello4.f90
+[info] link      ---- U hello4               <- hello4.f90
+__LOG__
+fail_lines_from_log >"$TEST_KEY-log-fails"
+file_cmp "$TEST_KEY-log-fails" "$TEST_KEY-log-fails" </dev/null
+#-------------------------------------------------------------------------------
+# Break hello3 and hello4
+TEST_KEY="$TEST_KEY_BASE-3-4-bad"
+sed -i 's/implicit none/implicit non/' src/hello_sub.f90  # introduce typo
+run_fail "$TEST_KEY" fcm make --new
+task_lines_from_log >"$TEST_KEY-log-tasks"
+file_cmp "$TEST_KEY-log-tasks" "$TEST_KEY-log-tasks" <<'__LOG__'
+[info] compile   ???? M world_mod.o          <- world_mod.f90
+[info] compile   ???? M greet_mod.o          <- greet_mod.f90
+[info] compile   ???? M hello.o              <- hello.f90
+[info] link      ???? M hello                <- hello.f90
+[info] compile   ???? M hello2.o             <- hello2.f90
+[info] link      ???? M hello2               <- hello2.f90
+[FAIL] compile   ???? ! hello_sub.o          <- hello_sub.f90
+[info] ext-iface ???? M hello_sub.interface  <- hello_sub.f90
+[info] compile   ???? M hello3.o             <- hello3.f90
+[FAIL] link      ---- ! hello3               <- hello3.f90
+[info] compile   ???? M hello4.o             <- hello4.f90
+[FAIL] link      ---- ! hello4               <- hello4.f90
+__LOG__
+fail_lines_from_log >"$TEST_KEY-log-fails"
+file_cmp "$TEST_KEY-log-fails" "$TEST_KEY-log-fails" <<'__LOG__'
+[FAIL] ! hello3              : depends on failed target: hello_sub.o
+[FAIL] ! hello4              : depends on failed target: hello_sub.o
+[FAIL] ! hello_sub.o         : update task failed
+__LOG__
+
+TEST_KEY="$TEST_KEY_BASE-3-4-fix"
+sed -i 's/implicit non/implicit none/' src/hello_sub.f90  # fix typo
+run_pass "$TEST_KEY" fcm make
+task_lines_from_log >"$TEST_KEY-log-tasks"
+file_cmp "$TEST_KEY-log-tasks" "$TEST_KEY-log-tasks" <<'__LOG__'
+[info] compile   ---- U world_mod.o          <- world_mod.f90
+[info] compile   ---- U greet_mod.o          <- greet_mod.f90
+[info] compile   ---- U hello.o              <- hello.f90
+[info] link      ---- U hello                <- hello.f90
+[info] compile   ---- U hello2.o             <- hello2.f90
+[info] link      ---- U hello2               <- hello2.f90
+[info] compile   ???? M hello_sub.o          <- hello_sub.f90
+[info] ext-iface ???? U hello_sub.interface  <- hello_sub.f90
+[info] compile   ---- U hello3.o             <- hello3.f90
+[info] link      ???? M hello3               <- hello3.f90
+[info] compile   ---- U hello4.o             <- hello4.f90
+[info] link      ???? M hello4               <- hello4.f90
+__LOG__
+fail_lines_from_log >"$TEST_KEY-log-fails"
+file_cmp "$TEST_KEY-log-fails" "$TEST_KEY-log-fails" </dev/null
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{file-ext.bin}=
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/greet_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/greet_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/greet_mod.f90	(revision 5095)
@@ -0,0 +1,9 @@
+module greet_mod
+implicit none
+character(*), parameter :: greet_word = 'Hello'
+contains
+subroutine greet(world)
+character(*), intent(in) :: world
+write(*, '(a,1x,a)') greet_word, world
+end subroutine greet
+end module greet_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello.f90	(revision 5095)
@@ -0,0 +1,6 @@
+program hello
+use greet_mod, only: greet
+use world_mod, only: world
+implicit none
+call greet(world)
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello2.f90	(revision 5095)
@@ -0,0 +1,6 @@
+program hello2
+use greet_mod, only: greet
+use world_mod, only: world
+implicit none
+call greet(trim(world))
+end program hello2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello3.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello3.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello3.f90	(revision 5095)
@@ -0,0 +1,6 @@
+program hello3
+use world_mod, only: world
+implicit none
+include 'hello_sub.interface'
+call hello_sub(world)
+end program hello3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello4.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello4.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello4.f90	(revision 5095)
@@ -0,0 +1,5 @@
+program hello4
+implicit none
+include 'hello_sub.interface'
+call hello_sub('Earth')
+end program hello4
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello_sub.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello_sub.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/hello_sub.f90	(revision 5095)
@@ -0,0 +1,5 @@
+subroutine hello_sub(world)
+implicit none
+character(*), intent(in) :: world
+write(*, '(a)'), 'Hello ' // trim(world)
+end subroutine hello_sub
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/world_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/world_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/36-build-fail-cont-basic/src/world_mod.f90	(revision 5095)
@@ -0,0 +1,4 @@
+module world_mod
+implicit none
+character(*), parameter :: world = 'Earth'
+end module world_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/37-no-such-config-file.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/37-no-such-config-file.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/37-no-such-config-file.t	(revision 5095)
@@ -0,0 +1,33 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", specified bad configuration file.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 2
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+run_fail "$TEST_KEY" fcm make -f bad-name.cfg
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<'__ERR__'
+[FAIL] bad-name.cfg: no such configuration file
+
+__ERR__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/38-extract-inherit-set-primary.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/38-extract-inherit-set-primary.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/38-extract-inherit-set-primary.t	(revision 5095)
@@ -0,0 +1,79 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make", "extract", location{primary} reset in inheritance.
+#-------------------------------------------------------------------------------
+. "$(dirname $0)/test_header"
+#-------------------------------------------------------------------------------
+tests 5
+#-------------------------------------------------------------------------------
+set -e
+svnadmin create 'repos'
+T_REPOS="file://${PWD}/repos"
+mkdir 't'
+echo "Hello World" >'t/hello.txt'
+echo "Hi World" >'t/hi.txt'
+svn import --no-auth-cache -q -m'Test' \
+    't/hello.txt' "${T_REPOS}/trunk/hello.txt"
+rm -fr 't'
+mkdir 'etc' 'i0' 'i1' 'junk'
+export FCM_CONF_PATH="${PWD}/etc"
+echo "location{primary}[t]=${T_REPOS}" >"${PWD}/etc/keyword.cfg"
+cat >'i0/fcm-make.cfg' <<__FCM_MAKE_CFG__
+steps=extract
+extract.ns=t
+extract.location{primary}[t]=${T_REPOS}
+__FCM_MAKE_CFG__
+fcm make -q -C 'i0'
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-reset"
+cat >'i1/fcm-make.cfg' <<__FCM_MAKE_CFG__
+use=${PWD}/i0
+extract.location{primary}[t]=${PWD}/junk
+__FCM_MAKE_CFG__
+run_fail "${TEST_KEY}" fcm make --new -C 'i1'
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <<__ERR__
+[FAIL] extract.location{primary}[t] = ${PWD}/junk: cannot modify, value is inherited
+[FAIL] config-file=${PWD}/i1/fcm-make.cfg:1
+
+__ERR__
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-set-identical"
+cat >'i1/fcm-make.cfg' <<__FCM_MAKE_CFG__
+use=${PWD}/i0
+extract.location{primary}[t]=${T_REPOS}
+__FCM_MAKE_CFG__
+run_pass "${TEST_KEY}" fcm make --new -C 'i1'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-set-same-1"
+cat >'i1/fcm-make.cfg' <<__FCM_MAKE_CFG__
+use=${PWD}/i0
+extract.location{primary}[t]=${T_REPOS}/
+__FCM_MAKE_CFG__
+run_pass "${TEST_KEY}" fcm make --new -C 'i1'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-set-same-2"
+cat >'i1/fcm-make.cfg' <<__FCM_MAKE_CFG__
+use=${PWD}/i0
+extract.location{primary}[t]=fcm:t
+__FCM_MAKE_CFG__
+run_pass "${TEST_KEY}" fcm make --new -C 'i1'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/39-build-source-with-dot.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/39-build-source-with-dot.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/39-build-source-with-dot.t	(revision 5095)
@@ -0,0 +1,54 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", build, source path is under a hidden directory.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 5
+#-------------------------------------------------------------------------------
+mkdir -p 'src/.singularity'
+cat >'src/.singularity/hello.f90' <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'No information!'
+end program hello
+__FORTRAN__
+#-------------------------------------------------------------------------------
+cat >'fcm-make.cfg' <<'__CFG__'
+steps=build
+build.source=$HERE/src
+build.prop{file-ext.bin}=.bin
+build.target=hello.bin
+__CFG__
+run_fail "${TEST_KEY_BASE}-tail" fcm make --new
+run_fail "${TEST_KEY_BASE}-tail.bin" './build/bin/hello.bin'
+
+cat >'fcm-make.cfg' <<'__CFG__'
+steps=build
+build.source=$HERE/src/.singularity
+build.prop{file-ext.bin}=.bin
+build.target=hello.bin
+__CFG__
+run_pass "${TEST_KEY_BASE}-head" fcm make --new
+run_pass "${TEST_KEY_BASE}-head.bin" './build/bin/hello.bin'
+file_cmp "${TEST_KEY_BASE}-head.bin.out" \
+    "${TEST_KEY_BASE}-head.bin.out" <<'__OUT__'
+No information!
+__OUT__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/40-extract-fs-source-with-dot.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/40-extract-fs-source-with-dot.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/40-extract-fs-source-with-dot.t	(revision 5095)
@@ -0,0 +1,59 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", extract, FS source path is under a hidden directory.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 5
+#-------------------------------------------------------------------------------
+# dot path name under source tree
+mkdir -p 'bubble/.com'
+cat >'bubble/.com/burst.f90' <<'__FORTRAN__'
+program burst
+write(*, '(a)') 'Burst!'
+end program burst
+__FORTRAN__
+cat >'fcm-make.cfg' <<'__CFG__'
+steps=extract
+extract.ns = bubble
+extract.location[bubble]=$HERE/bubble
+__CFG__
+run_pass "${TEST_KEY_BASE}-tail" fcm make --new
+run_fail "${TEST_KEY_BASE}-tail-find" find 'extract' -type f
+#-------------------------------------------------------------------------------
+# dot path name above source tree
+mkdir -p '.com/bubble'
+cat >'.com/bubble/burst.f90' <<'__FORTRAN__'
+program burst
+write(*, '(a)') 'Burst!'
+end program burst
+__FORTRAN__
+cat >'fcm-make.cfg' <<'__CFG__'
+steps=extract
+extract.ns = bubble
+extract.location[bubble]=$HERE/.com/bubble
+__CFG__
+run_pass "${TEST_KEY_BASE}-head" fcm make --new
+run_pass "${TEST_KEY_BASE}-head-find" find 'extract' -type f
+file_cmp "${TEST_KEY_BASE}-head-find.out" \
+    "${TEST_KEY_BASE}-head-find.out" <<'__OUT__'
+extract/bubble/burst.f90
+__OUT__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/41-ctx-name.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/41-ctx-name.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/41-ctx-name.t	(revision 5095)
@@ -0,0 +1,184 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", context name.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+
+file_cmp_sorted() {
+    sort - >"${1}.expected"
+    file_cmp "$1" "${2}" "${1}.expected"
+}
+
+find_fcm_make_files() {
+    find . "$@" -type f \
+        '(' -path '*/build/*' -o \
+            -path '*/extract/*' -o \
+            -path '*/.fcm-make*/c*' ')' \
+        | sort
+}
+
+tests 17
+
+mkdir -p 'src/a' 'src/b'
+cat >'src/a/a.f90' <<'__FORTRAN__'
+program a
+write(*, '(a)') 'I am program A.'
+end program a
+__FORTRAN__
+cat >'src/b/b.f90' <<'__FORTRAN__'
+program b
+write(*, '(a)') 'I am program B.'
+end program b
+__FORTRAN__
+
+cat >'fcm-make.cfg' <<'__CFG__'
+steps=extract
+extract.ns = a
+extract.location[a]=$HERE/src/a
+__CFG__
+
+cat >'fcm-make2.cfg' <<'__CFG__'
+name=2
+steps=build
+build.source=extract src/b
+build.target{task}=link
+__CFG__
+#-------------------------------------------------------------------------------
+run_pass "${TEST_KEY_BASE}-1" fcm make
+find_fcm_make_files >"${TEST_KEY_BASE}-1.find.new"
+file_cmp_sorted \
+    "${TEST_KEY_BASE}-1.find.new" "${TEST_KEY_BASE}-1.find.new" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+./extract/a/a.f90
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-2" fcm make --name=2
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-2.find.old"
+file_cmp "${TEST_KEY_BASE}-2.find.old" \
+    "${TEST_KEY_BASE}-2.find.old" "${TEST_KEY_BASE}-1.find.new"
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-2.find.new"
+file_cmp_sorted \
+    "${TEST_KEY_BASE}-2.find.new" "${TEST_KEY_BASE}-2.find.new" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+./build/bin/a.exe
+./build/bin/b.exe
+./build/o/a.o
+./build/o/b.o
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-1-incr" fcm make
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-1-incr.find.old"
+file_cmp_sorted "${TEST_KEY_BASE}-1-incr.find.old" \
+    "${TEST_KEY_BASE}-1-incr.find.old" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+./build/bin/a.exe
+./build/bin/b.exe
+./build/o/a.o
+./build/o/b.o
+./extract/a/a.f90
+__FIND__
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-1-incr.find.new"
+file_cmp_sorted "${TEST_KEY_BASE}-1-incr.find.new" \
+    "${TEST_KEY_BASE}-1-incr.find.new" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-2-incr" fcm make --name=2
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-2-incr.find.old"
+file_cmp_sorted "${TEST_KEY_BASE}-2-incr.find.old" \
+    "${TEST_KEY_BASE}-2-incr.find.old" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+./build/bin/a.exe
+./build/bin/b.exe
+./build/o/a.o
+./build/o/b.o
+./extract/a/a.f90
+__FIND__
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-2-incr.find.new"
+file_cmp_sorted "${TEST_KEY_BASE}-2-incr.find.new" \
+    "${TEST_KEY_BASE}-2-incr.find.new" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-1-new" fcm make --new
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-1-new.find.old"
+file_cmp_sorted "${TEST_KEY_BASE}-1-new.find.old" \
+    "${TEST_KEY_BASE}-1-new.find.old" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+./build/bin/a.exe
+./build/bin/b.exe
+./build/o/a.o
+./build/o/b.o
+__FIND__
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-1-new.find.new"
+file_cmp_sorted "${TEST_KEY_BASE}-1-new.find.new" \
+    "${TEST_KEY_BASE}-1-new.find.new" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+./extract/a/a.f90
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-2-new" fcm make --name=2 --new
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-2-new.find.old"
+file_cmp_sorted "${TEST_KEY_BASE}-2-new.find.old" \
+    "${TEST_KEY_BASE}-2-new.find.old" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+./extract/a/a.f90
+__FIND__
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-2-new.find.new"
+file_cmp_sorted "${TEST_KEY_BASE}-2-new.find.new" \
+    "${TEST_KEY_BASE}-2-new.find.new" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+./build/bin/a.exe
+./build/bin/b.exe
+./build/o/a.o
+./build/o/b.o
+__FIND__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/42-make-mirror-make2.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/42-make-mirror-make2.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/42-make-mirror-make2.t	(revision 5095)
@@ -0,0 +1,132 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make" with mirror to same location, then "fcm make --name=2".
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+
+file_cmp_sorted() {
+    sort - >"${1}.expected"
+    file_cmp "$1" "${2}" "${1}.expected"
+}
+
+find_fcm_make_files() {
+    find . "$@" -type f \
+        '(' -path '*/build/*' -o \
+            -path '*/extract/*' -o \
+            -path '*/mirror/*' -o \
+            -path '*/.fcm-make*/c*' ')' \
+        | sort
+}
+
+tests 11
+
+mkdir -p 'src/hello'
+cat >'src/hello/hello.f90' <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello!'
+end program hello
+__FORTRAN__
+
+cat >'fcm-make.cfg' <<'__CFG__'
+steps=extract mirror
+extract.ns = hello
+extract.location[hello]=$HERE/src/hello
+mirror.target=$HERE
+mirror.prop{config-file.name}=2
+mirror.prop{config-file.steps}=build
+build.target{task}=link
+__CFG__
+
+#-------------------------------------------------------------------------------
+run_pass "${TEST_KEY_BASE}-1" fcm make
+find_fcm_make_files >"${TEST_KEY_BASE}-1.find.new"
+file_cmp_sorted \
+    "${TEST_KEY_BASE}-1.find.new" "${TEST_KEY_BASE}-1.find.new" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+./extract/hello/hello.f90
+./mirror/fcm-make2.cfg
+./mirror/fcm-make2.cfg.orig
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-2" fcm make -f fcm-make2.cfg
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-2.find.old"
+file_cmp "${TEST_KEY_BASE}-2.find.old" \
+    "${TEST_KEY_BASE}-2.find.old" "${TEST_KEY_BASE}-1.find.new"
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-2.find.new"
+file_cmp_sorted \
+    "${TEST_KEY_BASE}-2.find.new" "${TEST_KEY_BASE}-2.find.new" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+./build/bin/hello.exe
+./build/o/hello.o
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-1-incr" fcm make
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-1-incr.find.old"
+file_cmp_sorted "${TEST_KEY_BASE}-1-incr.find.old" \
+    "${TEST_KEY_BASE}-1-incr.find.old" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+./build/bin/hello.exe
+./build/o/hello.o
+./extract/hello/hello.f90
+__FIND__
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-1-incr.find.new"
+file_cmp_sorted "${TEST_KEY_BASE}-1-incr.find.new" \
+    "${TEST_KEY_BASE}-1-incr.find.new" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+./mirror/fcm-make2.cfg
+./mirror/fcm-make2.cfg.orig
+__FIND__
+
+touch 'marker'
+sleep 1
+run_pass "${TEST_KEY_BASE}-2-incr" fcm make -f fcm-make2.cfg
+find_fcm_make_files '!' -newer 'marker' >"${TEST_KEY_BASE}-2-incr.find.old"
+file_cmp_sorted "${TEST_KEY_BASE}-2-incr.find.old" \
+    "${TEST_KEY_BASE}-2-incr.find.old" <<'__FIND__'
+./.fcm-make/config-as-parsed.cfg
+./.fcm-make/config-on-success.cfg
+./.fcm-make/ctx.gz
+./build/bin/hello.exe
+./build/o/hello.o
+./extract/hello/hello.f90
+./mirror/fcm-make2.cfg
+./mirror/fcm-make2.cfg.orig
+__FIND__
+find_fcm_make_files -newer 'marker' >"${TEST_KEY_BASE}-2-incr.find.new"
+file_cmp_sorted "${TEST_KEY_BASE}-2-incr.find.new" \
+    "${TEST_KEY_BASE}-2-incr.find.new" <<'__FIND__'
+./.fcm-make2/config-as-parsed.cfg
+./.fcm-make2/config-on-success.cfg
+./.fcm-make2/ctx.gz
+__FIND__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/43-ctx-name-inherit.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/43-ctx-name-inherit.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/43-ctx-name-inherit.t	(revision 5095)
@@ -0,0 +1,104 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", inherit, with context name.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+
+file_cmp_sorted() {
+    sort - >"${1}.expected"
+    file_cmp "$1" "${2}" "${1}.expected"
+}
+
+find_fcm_make_files() {
+    find . "$@" -type f \
+        '(' -path '*/build/*' -o \
+            -path '*/extract/*' -o \
+            -path '*/.fcm-make*/c*' ')' \
+        | sort
+}
+
+tests 5
+
+#-------------------------------------------------------------------------------
+mkdir -p 'hello/src'
+cat >'hello/fcm-make-friend.cfg' <<'__CFG__'
+name=-friend
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+__CFG__
+cat >'hello/src/friend.f90' <<'__FORTRAN__'
+module friend
+character(*), parameter :: name = 'friend'
+end module friend
+__FORTRAN__
+cat >'hello/src/hello.f90' <<'__FORTRAN__'
+program hello
+use friend, only: name
+write(*, '(a,1x,a)') 'Hello', name
+end program hello
+__FORTRAN__
+
+run_pass "${TEST_KEY_BASE}-hello" fcm make -C "${PWD}/hello" -n '-friend'
+#-------------------------------------------------------------------------------
+mkdir -p 'greet/src'
+cat >'greet/fcm-make-friend.cfg' <<'__CFG__'
+use=$HERE/../hello
+name=-friend
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+__CFG__
+cat >'greet/src/greet.f90' <<'__FORTRAN__'
+program greet
+use friend, only: name
+write(*, '(a,1x,a)') 'Greet', name
+end program greet
+__FORTRAN__
+
+run_pass "${TEST_KEY_BASE}-greet" fcm make -C "${PWD}/greet" -n '-friend'
+(cd 'greet' && find_fcm_make_files) >"${TEST_KEY_BASE}-greet.find"
+file_cmp_sorted \
+    "${TEST_KEY_BASE}-greet.find" "${TEST_KEY_BASE}-greet.find" <<'__FIND__'
+./.fcm-make-friend/config-as-parsed.cfg
+./.fcm-make-friend/config-on-success.cfg
+./.fcm-make-friend/ctx.gz
+./build/bin/greet.exe
+./build/bin/hello.exe
+./build/o/greet.o
+__FIND__
+#-------------------------------------------------------------------------------
+mkdir -p 'snub/src'
+cat >'snub/fcm-make-no-friend.cfg' <<'__CFG__'
+use=$HERE/../hello
+name=-no-friend
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+__CFG__
+run_fail "${TEST_KEY_BASE}-snub" fcm make -C "${PWD}/snub" -n '-no-friend'
+sed -i '3q' "${TEST_KEY_BASE}-snub.err"  # 3 lines only
+file_cmp "${TEST_KEY_BASE}-snub.err" "${TEST_KEY_BASE}-snub.err" <<__ERR__
+[FAIL] use = ${PWD}/hello: incorrect value in declaration
+[FAIL] config-file=${PWD}/snub/fcm-make-no-friend.cfg:1
+[FAIL] ${PWD}/hello/.fcm-make/ctx.gz: cannot retrieve cache
+__ERR__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/44-ctx-name-inherit-compat.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/44-ctx-name-inherit-compat.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/44-ctx-name-inherit-compat.t	(revision 5095)
@@ -0,0 +1,97 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", inherit, with context name, back compat.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+
+file_cmp_sorted() {
+    sort - >"${1}.expected"
+    file_cmp "$1" "${2}" "${1}.expected"
+}
+
+find_fcm_make_files() {
+    find . "$@" -type f \
+        '(' -path '*/build/*' -o \
+            -path '*/extract/*' -o \
+            -path '*/.fcm-make*/c*' ')' \
+        | sort
+}
+
+tests 2
+
+#-------------------------------------------------------------------------------
+mkdir -p 'hello/src'
+cat >'hello/fcm-make.cfg' <<'__CFG__'
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+__CFG__
+cat >'hello/src/friend.f90' <<'__FORTRAN__'
+module friend
+character(*), parameter :: name = 'friend'
+end module friend
+__FORTRAN__
+cat >'hello/src/hello.f90' <<'__FORTRAN__'
+program hello
+use friend, only: name
+write(*, '(a,1x,a)') 'Hello', name
+end program hello
+__FORTRAN__
+
+fcm make -q -C "${PWD}/hello"
+
+# Remove "name" from make ctx to make it look like a make ctx generated from an
+# old version of "fcm make".
+gunzip "${PWD}/hello/.fcm-make/ctx.gz"
+perl - "${PWD}/hello/.fcm-make/ctx" <<'__PERL__'
+use Storable qw{nstore retrieve};
+my $m_ctx = retrieve($ARGV[0]);
+delete $m_ctx->{'name'};
+nstore($m_ctx, $ARGV[0]);
+__PERL__
+gzip "${PWD}/hello/.fcm-make/ctx"
+#-------------------------------------------------------------------------------
+mkdir -p 'greet/src'
+cat >'greet/fcm-make-friend.cfg' <<'__CFG__'
+use=$HERE/../hello
+name=-friend
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+__CFG__
+cat >'greet/src/greet.f90' <<'__FORTRAN__'
+program greet
+use friend, only: name
+write(*, '(a,1x,a)') 'Greet', name
+end program greet
+__FORTRAN__
+
+run_pass "${TEST_KEY_BASE}" fcm make -C "${PWD}/greet" -n '-friend'
+(cd 'greet' && find_fcm_make_files) >"${TEST_KEY_BASE}.find"
+file_cmp_sorted "${TEST_KEY_BASE}.find" "${TEST_KEY_BASE}.find" <<'__FIND__'
+./.fcm-make-friend/config-as-parsed.cfg
+./.fcm-make-friend/config-on-success.cfg
+./.fcm-make-friend/ctx.gz
+./build/bin/greet.exe
+./build/bin/hello.exe
+./build/o/greet.o
+__FIND__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/45-dest-mv.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/45-dest-mv.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/45-dest-mv.t	(revision 5095)
@@ -0,0 +1,85 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests "fcm make", destination moved.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+
+tests 9
+
+mkdir -p 'src/hello'
+cat >'src/hello/hello.f90' <<'__FORTRAN__'
+program hello
+use greet_mod, only: greet
+use world_mod, only: world
+write(*, '(a,1x,a)') greet, world
+end program hello
+__FORTRAN__
+cat >'src/hello/world_mod.f90' <<'__FORTRAN__'
+module world_mod
+character(*), parameter :: world = 'Earth'
+end module world_mod
+__FORTRAN__
+cat >'src/hello/greet_mod.f90' <<'__FORTRAN__'
+module greet_mod
+character(*), parameter :: greet = 'Hello'
+end module greet_mod
+__FORTRAN__
+
+svnadmin create 'hello.svn'
+URL="file://${PWD}/hello.svn/hello"
+svn import --no-auth-cache -m'initial import' 'src' "${URL}/trunk/src"
+
+mkdir -p 'loc1'
+cat >'loc1/fcm-make.cfg' <<__CFG__
+steps=extract build
+extract.ns=hello
+extract.location{primary}[hello]=${URL}
+build.target{task}=link
+build.prop{file-ext.bin}=
+__CFG__
+#-------------------------------------------------------------------------------
+run_pass "${TEST_KEY_BASE}-1" fcm make -C "${PWD}/loc1"
+mv "${PWD}/loc1" "${PWD}/loc2"
+#-------------------------------------------------------------------------------
+# Inherit
+mkdir -p 'loc3'
+cat >'loc3/fcm-make.cfg' <<__CFG__
+use=${PWD}/loc2
+__CFG__
+run_pass "${TEST_KEY_BASE}-3" fcm make -C "${PWD}/loc3"
+LOG="${PWD}/loc3/fcm-make.log"
+file_grep "${TEST_KEY_BASE}-3-log-1" \
+    '\[info\]   dest:    3 \[U unchanged\]' "${LOG}"
+file_grep "${TEST_KEY_BASE}-3-log-2" \
+    '\[info\] sources: total=3, analysed=0' "${LOG}"
+file_grep "${TEST_KEY_BASE}-3-log-3" \
+    '\[info\] TOTAL     targets: modified=0, unchanged=6, failed=0' "${LOG}"
+#-------------------------------------------------------------------------------
+# Incremental
+run_pass "${TEST_KEY_BASE}-2" fcm make -C "${PWD}/loc2"
+LOG="${PWD}/loc2/fcm-make.log"
+file_grep "${TEST_KEY_BASE}-2-log-1" \
+    '\[info\]   dest:    3 \[U unchanged\]' "${LOG}"
+file_grep "${TEST_KEY_BASE}-2-log-2" \
+    '\[info\] sources: total=3, analysed=0' "${LOG}"
+file_grep "${TEST_KEY_BASE}-2-log-3" \
+    '\[info\] TOTAL     targets: modified=0, unchanged=6, failed=0' "${LOG}"
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/46-archive-mode.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/46-archive-mode.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/46-archive-mode.t	(revision 5095)
@@ -0,0 +1,117 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Tests for "fcm make --archive"
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+
+file_cmp_sorted() {
+    sort - >"${1}.expected"
+    file_cmp "$1" "${2}" "${1}.expected"
+}
+
+tests 11
+#-------------------------------------------------------------------------------
+# Create a repository to extract
+svnadmin create 'repos'
+T_REPOS="file://${PWD}/repos"
+mkdir 't'
+cat >'t/hello.f90' <<'__FORTRAN__'
+program hello
+use world_mod, only: world
+write(*, '(a,1x,a)') 'Hello', world
+end program hello
+__FORTRAN__
+cat >'t/world_mod.f90' <<'__FORTRAN__'
+module world_mod
+character(*), parameter :: world = 'Earth'
+end module world_mod
+__FORTRAN__
+svn import --no-auth-cache -q -m'Test' t "${T_REPOS}/hello/trunk"
+rm -r 't'
+
+# Create a fcm-make.cfg to do some extract and build
+cat >'fcm-make.cfg' <<__CFG__
+steps = extract build
+extract.ns = hello
+extract.location{primary}[hello] = ${T_REPOS}/hello
+build.target{task} = link
+build.prop{file-ext.bin} =
+__CFG__
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-on-new"
+run_pass "${TEST_KEY}" fcm make -a
+find '.fcm-make/cache' 'build' -type f | sort >"${TEST_KEY}.find"
+file_cmp_sorted "${TEST_KEY}.find" "${TEST_KEY}.find" <<'__FIND__'
+.fcm-make/cache/extract.tar.gz
+build/bin/hello
+build/include.tar.gz
+build/o.tar.gz
+__FIND__
+
+touch 'new'
+sleep 1
+
+TEST_KEY="${TEST_KEY_BASE}-on-incr"
+run_pass "${TEST_KEY}" fcm make -a
+find '.fcm-make/cache' 'build' -type f -newer 'new' \
+    | sort >"${TEST_KEY}.find.new"
+file_cmp_sorted "${TEST_KEY}.find.new" "${TEST_KEY}.find.new" <<'__FIND__'
+.fcm-make/cache/extract.tar.gz
+build/include.tar.gz
+build/o.tar.gz
+__FIND__
+find '.fcm-make/cache' 'build' -type f '!' -newer 'new' \
+    | sort >"${TEST_KEY}.find.old"
+file_cmp_sorted "${TEST_KEY}.find.old" "${TEST_KEY}.find.old" <<'__FIND__'
+build/bin/hello
+__FIND__
+
+TEST_KEY="${TEST_KEY_BASE}-on-incr-build-o"
+run_pass "${TEST_KEY}" \
+    fcm make -a 'build.prop{archive-ok-target-category}=o'
+find '.fcm-make/cache' 'build' -type f -newer 'new' \
+    | sort >"${TEST_KEY}.find.new"
+file_cmp_sorted "${TEST_KEY}.find.new" "${TEST_KEY}.find.new" <<'__FIND__'
+.fcm-make/cache/extract.tar.gz
+build/o.tar.gz
+__FIND__
+find '.fcm-make/cache' 'build' -type f '!' -newer 'new' \
+    | sort >"${TEST_KEY}.find.old"
+file_cmp_sorted "${TEST_KEY}.find.old" "${TEST_KEY}.find.old" <<'__FIND__'
+build/bin/hello
+build/include/world_mod.mod
+__FIND__
+
+run_pass "${TEST_KEY_BASE}-off" fcm make
+find '.fcm-make/cache' 'build' -type f -newer 'new' \
+    | sort >"${TEST_KEY}.find.new"
+file_cmp_sorted "${TEST_KEY}.find.new" "${TEST_KEY}.find.new" <'/dev/null'
+find '.fcm-make/cache' 'build' -type f '!' -newer 'new' \
+    | sort >"${TEST_KEY}.find.old"
+file_cmp_sorted "${TEST_KEY}.find.old" "${TEST_KEY}.find.old" <<'__FIND__'
+.fcm-make/cache/extract/hello/0/hello.f90
+.fcm-make/cache/extract/hello/0/world_mod.f90
+build/bin/hello
+build/include/world_mod.mod
+build/o/hello.o
+build/o/world_mod.o
+__FIND__
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns.t	(revision 5095)
@@ -0,0 +1,93 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test "fcm make", build.target{category} and build.target{task} with namespace.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+#-------------------------------------------------------------------------------
+tests 12
+mkdir 'i0'
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* 'i0'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+run_pass "${TEST_KEY}" fcm make -C 'i0'
+
+grep -F 'build.target{category}' 'i0/.fcm-make/config-on-success.cfg' \
+    >'edited-config-on-success.cfg'
+file_cmp "${TEST_KEY}-edited-config-on-success.cfg" \
+    'edited-config-on-success.cfg' <<'__CFG__'
+build.target{category}[hello] = bin
+__CFG__
+
+find 'i0/build' -type f | sort >'find-i0-build.out'
+file_cmp "${TEST_KEY}-find-i0-build.out" 'find-i0-build.out' <<'__FIND__'
+i0/build/bin/hello
+i0/build/o/hello.o
+__FIND__
+
+"${PWD}/i0/build/bin/hello" <<<'&world_nl /' >'hello.out'
+file_cmp "${TEST_KEY}-hello.out" 'hello.out' <<<'Hello World'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-inherit"
+run_pass "${TEST_KEY}" fcm make -C 'i1' \
+    "use=${PWD}/i0" \
+    'build.target{category}[greet] = etc'
+
+grep -F 'build.target{category}' 'i1/.fcm-make/config-on-success.cfg' \
+    >'edited-config-on-success.cfg'
+file_cmp "${TEST_KEY}-edited-config-on-success.cfg" \
+    'edited-config-on-success.cfg' <<'__CFG__'
+build.target{category}[greet] = etc
+build.target{category}[hello] = bin
+__CFG__
+
+find 'i1/build' -type f | sort >'find-i1-build.out'
+file_cmp "${TEST_KEY}-find-i1-build.out" 'find-i1-build.out' <<'__FIND__'
+i1/build/bin/hello
+i1/build/etc/greet/.etc
+i1/build/etc/greet/world.nl
+__FIND__
+
+"${PWD}/i1/build/bin/hello" <'i1/build/etc/greet/world.nl' >'hello.out'
+file_cmp "${TEST_KEY}-hello.out" 'hello.out' <<<'Hello Earth'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-inherit-incr"
+touch 'before'
+run_pass "${TEST_KEY}" fcm make -C 'i1' \
+    "use=${PWD}/i0" \
+    'build.target{category}[greet] = bin etc'
+
+grep -F 'build.target{category}' 'i1/.fcm-make/config-on-success.cfg' \
+    >'edited-config-on-success.cfg'
+file_cmp "${TEST_KEY}-edited-config-on-success.cfg" \
+    'edited-config-on-success.cfg' <<'__CFG__'
+build.target{category}[greet] = bin etc
+build.target{category}[hello] = bin
+__CFG__
+
+find 'i1/build' -type f -newer 'before' | sort >'find-i1-build.out'
+file_cmp "${TEST_KEY}-find-i1-build.out" 'find-i1-build.out' <<'__FIND__'
+i1/build/bin/greet
+i1/build/o/greet.o
+__FIND__
+
+"${PWD}/i1/build/bin/greet" <'i1/build/etc/greet/world.nl' >'greet.out'
+file_cmp "${TEST_KEY}-greet.out" 'greet.out' <<<'Greet Earth'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.prop{file-ext.bin}=
+build.target{category}[hello] = bin
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/greet/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/greet/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/greet/greet.f90	(revision 5095)
@@ -0,0 +1,6 @@
+program greet
+character(31) :: world = 'World'
+namelist /world_nl/ world
+read(*, nml=world_nl)
+write(*, '(a,1x,a)') 'Greet', trim(world)
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/greet/world.nl
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/greet/world.nl	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/greet/world.nl	(revision 5095)
@@ -0,0 +1,3 @@
+&world_nl
+world='Earth',
+/
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/hello/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/hello/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/hello/hello.f90	(revision 5095)
@@ -0,0 +1,6 @@
+program hello
+character(31) :: world = 'World'
+namelist /world_nl/ world
+read(*, nml=world_nl)
+write(*, '(a,1x,a)') 'Hello', trim(world)
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/hello/world.nl
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/hello/world.nl	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/47-build-target-modifier-ns/src/hello/world.nl	(revision 5095)
@@ -0,0 +1,3 @@
+&world_nl
+world='Jupiter',
+/
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1.t	(revision 5095)
@@ -0,0 +1,65 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test "fcm make", build, using SHA1 checksum to determine changes.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+
+get-hello-checksum() {
+    PERL5LIB="${FCM_HOME}/lib" perl - <<'__PERL__'
+use strict;
+use warnings;
+use IO::Uncompress::Gunzip qw{gunzip};
+use Storable;
+gunzip('.fcm-make/ctx.gz', 'ctx');
+my $m_ctx = retrieve('ctx');
+printf(
+    "%s  %s\n",
+    $m_ctx->{'ctx_of'}{'build'}{'source_of'}{'hello.f90'}{'checksum'},
+    'src/hello.f90',
+);
+unlink('ctx')
+__PERL__
+}
+#-------------------------------------------------------------------------------
+tests 8
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* .
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+run_pass "${TEST_KEY}" fcm make
+run_pass "${TEST_KEY}-sha1sum-check" sha1sum -c - <<<"$(get-hello-checksum)"
+HELLO_O_MTIME="$(stat -c '%Y' 'build/o/hello.o')"
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-incr"
+run_pass "${TEST_KEY}" fcm make
+run_pass "${TEST_KEY}-sha1sum-check" sha1sum -c - <<<"$(get-hello-checksum)"
+HELLO_O_MTIME_INCR="$(stat -c '%Y' 'build/o/hello.o')"
+run_pass "${TEST_KEY}-mtime-of-hello.o" \
+    test "${HELLO_O_MTIME_INCR}" -eq "${HELLO_O_MTIME}"
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-incr-2"
+sed -i 's/Hello/Greet/' 'src/hello.f90'
+sleep 1  # In case computer is very fast, when everything can happen within 1s.
+run_pass "${TEST_KEY}" fcm make
+run_pass "${TEST_KEY}-sha1sum-check" sha1sum -c - <<<"$(get-hello-checksum)"
+HELLO_O_MTIME_INCR="$(stat -c '%Y' 'build/o/hello.o')"
+run_pass "${TEST_KEY}-mtime-of-hello.o" \
+    test "${HELLO_O_MTIME_INCR}" -gt "${HELLO_O_MTIME}"
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+steps=build
+build.source=$HERE/src
+build.prop{checksum-method} = sha1
+build.prop{file-ext.bin}=
+build.target{task} = link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1/src/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1/src/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/48-build-sha1/src/hello.f90	(revision 5095)
@@ -0,0 +1,3 @@
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more.t	(revision 5095)
@@ -0,0 +1,50 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test build C source file with mixed case name and has main function.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+#-------------------------------------------------------------------------------
+tests 8
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* '.'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+run_pass "${TEST_KEY}" fcm make
+grep '^\[info\] target ' fcm-make.log >"${TEST_KEY}.target.log"
+file_cmp "${TEST_KEY}.target.log" "${TEST_KEY}.target.log" <<'__LOG__'
+[info] target hi
+[info] target  - hi.o
+[info] target hello
+[info] target  - hello.o
+[info] target greet
+[info] target  - greet.o
+__LOG__
+
+"${PWD}/build/bin/greet" >"${TEST_KEY}.greet.out"
+run_pass "${TEST_KEY}.greet.rc" test $? -eq 12
+file_cmp "${TEST_KEY}.greet.out" "${TEST_KEY}.greet.out" <<<'Greet World'
+
+run_pass "${TEST_KEY}.hello" "${PWD}/build/bin/hello"
+file_cmp "${TEST_KEY}.hello.out" "${TEST_KEY}.hello.out" <<<'Hello World'
+
+"${PWD}/build/bin/hi" >"${TEST_KEY}.hi.out"
+run_pass "${TEST_KEY}.hi.rc" test $? -eq 9
+file_cmp "${TEST_KEY}.hi.out" "${TEST_KEY}.hi.out" <<<'Hi World'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{file-ext.bin}=
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/greet.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/greet.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/greet.c	(revision 5095)
@@ -0,0 +1,4 @@
+#include <stdio.h>
+void main(void) {
+    printf("Greet World\n");
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/hello.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/hello.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/hello.c	(revision 5095)
@@ -0,0 +1,5 @@
+#include <stdio.h>
+int main(void) {
+    printf("Hello World\n");
+    return 0;
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/hi.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/hi.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/49-build-c-more/src/hi.c	(revision 5095)
@@ -0,0 +1,5 @@
+#include <stdio.h>
+void
+main(void) {
+    printf("Hi World\n");
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep.t	(revision 5095)
@@ -0,0 +1,46 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test build.prop{dep.o}[target], etc.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 7
+#-------------------------------------------------------------------------------
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* '.'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+
+run_fail "${TEST_KEY}-1" fcm make
+run_pass "${TEST_KEY}-1-log-grep" \
+    grep -F '[FAIL] ! greet.bin           : update task failed' 'fcm-make.log'
+
+# Explicitly add dependency to target
+run_pass "${TEST_KEY}-2" fcm make 'build.prop{dep.o}[greet.bin]=greet_world.o'
+grep '^\[info\] target ' 'fcm-make.log' >"${TEST_KEY}.target.log"
+file_cmp "${TEST_KEY}-2.target.log" "${TEST_KEY}.target.log" <<'__LOG__'
+[info] target greet.bin
+[info] target  - greet.o
+[info] target  - greet_world.o
+__LOG__
+
+run_pass "${TEST_KEY}.greet" "${PWD}/build/bin/greet.bin"
+file_cmp "${TEST_KEY}.greet.out" "${TEST_KEY}.greet.out" <<<'Greet World'
+file_cmp "${TEST_KEY}.greet.err" "${TEST_KEY}.greet.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{file-ext.bin}=.bin
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/src/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/src/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/src/greet.f90	(revision 5095)
@@ -0,0 +1,7 @@
+program greet
+interface
+subroutine greet_world()
+end subroutine greet_world
+end interface
+call greet_world()
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/src/greet_world.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/src/greet_world.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/50-build-target-dep/src/greet_world.f90	(revision 5095)
@@ -0,0 +1,3 @@
+subroutine greet_world()
+write(*, '(a)') 'Greet World'
+end subroutine greet_world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep.t	(revision 5095)
@@ -0,0 +1,59 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test build.prop{dep.o}[target], etc.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 7
+#-------------------------------------------------------------------------------
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* '.'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+
+run_fail "${TEST_KEY}-1" fcm make
+sed -n '/bad or missing/p; /required by/p' 'fcm-make.log' >'fcm-make.log.edited'
+file_cmp "${TEST_KEY}-1-log-edited" 'fcm-make.log.edited' <<'__LOG__'
+[FAIL] hello_mod.mod: bad or missing dependency (type=1.include)
+[FAIL]     required by: greet_mod.o
+[FAIL]     required by: greet_mod.mod
+[FAIL]     required by: greet.o
+[FAIL]     required by: greet.bin
+__LOG__
+
+# Remove dependency from target
+mkdir 'hello'
+(cd 'hello'; gfortran -c '../src2/hello_mod.f90')
+(cd 'hello'; ar rs 'libhello.a' 'hello_mod.o' 2>'/dev/null')
+run_pass "${TEST_KEY}-2" fcm make \
+    'build.prop{no-dep.include}[greet_mod.o]=hello_mod.mod' \
+    'build.prop{no-dep.o}[greet_mod.mod]=hello_mod.o'
+grep '^\[info\] target ' 'fcm-make.log' >"${TEST_KEY}.target.log"
+file_cmp "${TEST_KEY}-2.target.log" "${TEST_KEY}.target.log" <<'__LOG__'
+[info] target greet.bin
+[info] target  - greet.o
+[info] target  -  - greet_mod.mod
+[info] target  -  -  - greet_mod.o
+[info] target  - greet_mod.o
+__LOG__
+
+run_pass "${TEST_KEY}.greet" "${PWD}/build/bin/greet.bin"
+file_cmp "${TEST_KEY}.greet.out" "${TEST_KEY}.greet.out" <<<'Greet world!'
+file_cmp "${TEST_KEY}.greet.err" "${TEST_KEY}.greet.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{file-ext.bin}=.bin
+build.prop{fc.include-paths} = $HERE/hello
+build.prop{fc.lib-paths} = $HERE/hello
+build.prop{fc.libs} = hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet.f90	(revision 5095)
@@ -0,0 +1,4 @@
+program greet
+use greet_mod, only: greet_world
+call greet_world()
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet_mod.f90	(revision 5095)
@@ -0,0 +1,3 @@
+module greet_mod
+use hello_mod, only: greet_world
+end module greet_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet_world.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet_world.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src/greet_world.f90	(revision 5095)
@@ -0,0 +1,3 @@
+subroutine greet_world()
+write(*, '(a)') 'Greet World'
+end subroutine greet_world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src2/hello_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src2/hello_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/51-build-target-no-dep/src2/hello_mod.f90	(revision 5095)
@@ -0,0 +1,6 @@
+module hello_mod
+contains
+subroutine greet_world()
+write(*, '(a)') 'Greet world!'
+end subroutine greet_world
+end module hello_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/52-require-version.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/52-require-version.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/52-require-version.t	(revision 5095)
@@ -0,0 +1,53 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test "require-version" declaration. Note: Some tests will fail if we still
+# have FCM releases after the year 9999 and beyond, but I am sure FCM will be
+# retired long before that happens!
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 10
+#-------------------------------------------------------------------------------
+FCM_VERSION="$(fcm --version | cut -d' ' -f 2)"
+run_pass "${TEST_KEY_BASE}-min-older" fcm make "require-version=2015"
+run_pass "${TEST_KEY_BASE}-min-current" \
+    fcm make "require-version=${FCM_VERSION}"
+TEST_KEY="${TEST_KEY_BASE}-min-future"
+run_fail "${TEST_KEY}" fcm make "require-version=9999.12.0"
+sed -i '/require-version/!d' "${TEST_KEY}.err"
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <<__ERR__
+[FAIL] require-version = 9999.12.0: requested version mismatch
+__ERR__
+run_pass "${TEST_KEY_BASE}-min-current-max-current" \
+    fcm make "require-version=${FCM_VERSION} ${FCM_VERSION}"
+run_pass "${TEST_KEY_BASE}-min-old-max-future" \
+    fcm make "require-version=2015 9999"
+TEST_KEY="${TEST_KEY_BASE}-min-old-max-old"
+run_fail "${TEST_KEY}" fcm make "require-version=2014 2015"
+sed -i '/require-version/!d' "${TEST_KEY}.err"
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <<__ERR__
+[FAIL] require-version = 2014 2015: requested version mismatch
+__ERR__
+TEST_KEY="${TEST_KEY_BASE}-min-future-max-future"
+run_fail "${TEST_KEY}" fcm make "require-version=9999.01.0 9999.12.0"
+sed -i '/require-version/!d' "${TEST_KEY}.err"
+file_cmp "${TEST_KEY}.err" "${TEST_KEY}.err" <<__ERR__
+[FAIL] require-version = 9999.01.0 9999.12.0: requested version mismatch
+__ERR__
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/53-extract-conflict-incr.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/53-extract-conflict-incr.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/53-extract-conflict-incr.t	(revision 5095)
@@ -0,0 +1,59 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test extract conflict should continue to fail in incremental mode.
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 33
+
+mkdir -p 'foo/1' 'foo/2' 'foo/3'
+cat >'foo/1/hello.f90' <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello World!'
+end program hello
+__FORTRAN__
+cat >'foo/2/hello.f90' <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello Earth!'
+end program hello
+__FORTRAN__
+cat >'foo/3/hello.f90' <<'__FORTRAN__'
+program hello
+write(*, '(a)') 'Hello Mars!'
+end program hello
+__FORTRAN__
+
+cat >'fcm-make.cfg' <<'__FCM_MAKE_CFG__'
+steps=extract
+extract.ns=foo
+extract.location[foo]=$HERE/foo/1
+extract.location{diff}[foo]=$HERE/foo/2 $HERE/foo/3
+__FCM_MAKE_CFG__
+
+# 1 new + 10 incrementals
+for I in {0..10}; do
+    run_fail "${TEST_KEY_BASE}-${I}" fcm make
+    run_pass "${TEST_KEY_BASE}-${I}.log" \
+        grep -F "[FAIL] foo/hello.f90: merge results in conflict" 'fcm-make.log'
+    run_pass "${TEST_KEY_BASE}-${I}.log" \
+        grep -F "[FAIL] !!! source from location  2: ${PWD}/foo/3/hello.f90" \
+        'fcm-make.log'
+done
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns.t	(revision 5095)
@@ -0,0 +1,60 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test build.prop{ignore-missing-dep-ns}
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 7
+#-------------------------------------------------------------------------------
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* '.'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+
+run_fail "${TEST_KEY}-1" fcm make
+sed -n '/bad or missing/p; /required by/p' 'fcm-make.log' >'fcm-make.log.edited'
+file_cmp "${TEST_KEY}-1-log-edited" 'fcm-make.log.edited' <<'__LOG__'
+[FAIL] hello_mod.mod: bad or missing dependency (type=1.include)
+[FAIL]     required by: greet_mod.o
+[FAIL]     required by: greet_mod.mod
+[FAIL]     required by: greet.o
+[FAIL]     required by: greet.bin
+__LOG__
+
+# Remove dependency from target
+mkdir 'hello'
+(cd 'hello'; gfortran -c '../src2/hello_mod.f90')
+(cd 'hello'; ar rs 'libhello.a' 'hello_mod.o' 2>'/dev/null')
+run_pass "${TEST_KEY}-2" fcm make \
+    'build.prop{ignore-missing-dep-ns}=/'
+sed -n '/^\[info\] target /p; /ignore-missing-dep:/p' 'fcm-make.log' \
+    >"${TEST_KEY}.target.log"
+file_cmp "${TEST_KEY}-2.target.log" "${TEST_KEY}.target.log" <<'__LOG__'
+[WARN] greet_mod.o                   : ignore-missing-dep: (  include) hello_mod.mod
+[info] target greet.bin
+[info] target  - greet.o
+[info] target  -  - greet_mod.mod
+[info] target  -  -  - greet_mod.o
+[info] target  - greet_mod.o
+__LOG__
+
+run_pass "${TEST_KEY}.greet" "${PWD}/build/bin/greet.bin"
+file_cmp "${TEST_KEY}.greet.out" "${TEST_KEY}.greet.out" <<<'Greet world!'
+file_cmp "${TEST_KEY}.greet.err" "${TEST_KEY}.greet.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
+build.prop{file-ext.bin}=.bin
+build.prop{fc.include-paths} = $HERE/hello
+build.prop{fc.lib-paths} = $HERE/hello
+build.prop{fc.libs} = hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet.f90	(revision 5095)
@@ -0,0 +1,4 @@
+program greet
+use greet_mod, only: greet_world
+call greet_world()
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet_mod.f90	(revision 5095)
@@ -0,0 +1,3 @@
+module greet_mod
+use hello_mod, only: greet_world
+end module greet_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet_world.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet_world.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src/greet_world.f90	(revision 5095)
@@ -0,0 +1,3 @@
+subroutine greet_world()
+write(*, '(a)') 'Greet World'
+end subroutine greet_world
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src2/hello_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src2/hello_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/54-build-ignore-missing-dep-ns/src2/hello_mod.f90	(revision 5095)
@@ -0,0 +1,6 @@
+module hello_mod
+contains
+subroutine greet_world()
+write(*, '(a)') 'Greet world!'
+end subroutine greet_world
+end module hello_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment.t	(revision 5095)
@@ -0,0 +1,42 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test build, handle Fortran program unit with tail comments
+# https://github.com/metomi/fcm/issues/252
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 5
+#-------------------------------------------------------------------------------
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* '.'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+
+run_pass "${TEST_KEY}" fcm make
+sed -n '/source->target greet_mod.f90/p' 'fcm-make.log' >'fcm-make.log.edited'
+file_cmp "${TEST_KEY}.target.log" 'fcm-make.log.edited' <<'__LOG__'
+[info] source->target greet_mod.f90 -> (install) include/ greet_mod.f90
+[info] source->target greet_mod.f90 -> (compile+) include/ greet_mod.mod
+[info] source->target greet_mod.f90 -> (compile) o/ greet_mod.o
+__LOG__
+
+run_pass "${TEST_KEY}.greet" "${PWD}/build/bin/greet.exe"
+file_cmp "${TEST_KEY}.greet.out" "${TEST_KEY}.greet.out" <<<'Greet World'
+file_cmp "${TEST_KEY}.greet.err" "${TEST_KEY}.greet.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+steps=build
+build.source=$HERE/src
+build.target{task}=link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/src/greet.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/src/greet.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/src/greet.f90	(revision 5095)
@@ -0,0 +1,4 @@
+program greet
+use greet_mod, only: greet_world
+call greet_world()
+end program greet
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/src/greet_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/src/greet_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/55-build-fortran-prog-unit-tail-comment/src/greet_mod.f90	(revision 5095)
@@ -0,0 +1,6 @@
+module greet_mod ! a greeting module for testing
+contains
+subroutine greet_world()
+write(*, '(a)') 'Greet World'
+end subroutine greet_world
+end module greet_mod ! world does not end here
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule.t	(revision 5095)
@@ -0,0 +1,53 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Test build, handle Fortran submodule
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+tests 5
+#-------------------------------------------------------------------------------
+cp -r "${TEST_SOURCE_DIR}/${TEST_KEY_BASE}/"* '.'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}"
+
+run_pass "${TEST_KEY}" fcm make
+sed -n '/\[info\] target /p' 'fcm-make.log' >'fcm-make.log.edited'
+file_cmp "${TEST_KEY}.target.log" 'fcm-make.log.edited' <<'__LOG__'
+[info] target test.exe
+[info] target  - class_impl.o
+[info] target  - class_mod.o
+[info] target  - simple_impl.o
+[info] target  - simple_mod.o
+[info] target  - test.o
+[info] target  -  - class_mod.mod
+[info] target  -  -  - class_mod.o
+[info] target  -  - simple_mod.mod
+[info] target  -  -  - simple_mod.o
+__LOG__
+
+run_pass "${TEST_KEY}.test" "${PWD}/build/bin/test.exe"
+file_cmp "${TEST_KEY}.test.out" "${TEST_KEY}.test.out" <<'__OUT__'
+Returner 14
+
+Start with 12
+After mangle 29
+__OUT__
+file_cmp "${TEST_KEY}.test.err" "${TEST_KEY}.test.err" <'/dev/null'
+#-------------------------------------------------------------------------------
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+steps = build
+build.source = $HERE/src
+build.target{task} = link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/class_impl.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/class_impl.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/class_impl.f90	(revision 5095)
@@ -0,0 +1,27 @@
+submodule(class_mod) class_impl
+
+  implicit none
+
+contains
+
+  module function bar_initialiser( starter ) result(instance)
+    implicit none
+    integer, intent(in) :: starter
+    type(bar_type) :: instance
+    instance%stuff = starter
+  end function bar_initialiser
+
+
+  module subroutine bar_mangle(this, factor)
+    implicit none
+    class(bar_type), intent(inout) :: this
+    integer,         intent(in)    :: factor
+    this%stuff = ieor(this%stuff, factor)
+  end subroutine bar_mangle
+
+
+  module procedure bar_howmuch ! Alternative syntax
+    bar_howmuch = this%stuff
+  end procedure bar_howmuch
+
+end submodule class_impl
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/class_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/class_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/class_mod.f90	(revision 5095)
@@ -0,0 +1,54 @@
+module class_mod
+
+  implicit none
+
+  type, abstract :: foo_type
+    private
+    integer :: stuff
+  contains
+    private
+    procedure(mangle_if),   public, deferred :: mangle
+    procedure(how_much_if), public, deferred :: how_much
+  end type foo_type
+
+  interface
+    subroutine mangle_if(this, factor)
+      import foo_type
+      class(foo_type), intent(inout) :: this
+      integer,         intent(in) :: factor
+    end subroutine mangle_if
+    function how_much_if(this)
+      import foo_type
+      class(foo_type), intent(inout) :: this
+      integer :: how_much_if
+    end function how_much_if
+  end interface
+
+  type, extends(foo_type) :: bar_type
+    private
+  contains
+    private
+    procedure, public :: mangle   => bar_mangle
+    procedure, public :: how_much => bar_howmuch
+  end type bar_type
+
+  interface bar_type
+    procedure bar_initialiser
+  end interface bar_type
+
+  interface
+    module function bar_initialiser(starter) result(instance)
+      integer,intent(in) :: starter
+      type(bar_type) :: instance
+    end function bar_initialiser
+    module subroutine bar_mangle(this, factor)
+      class(bar_type), intent(inout) :: this
+      integer,         intent(in) :: factor
+    end subroutine bar_mangle
+    module function bar_howmuch(this)
+      class(bar_type), intent(inout) :: this
+      integer :: bar_howmuch
+    end function bar_howmuch
+  end interface
+
+end module class_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/simple_impl.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/simple_impl.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/simple_impl.f90	(revision 5095)
@@ -0,0 +1,18 @@
+submodule(simple_mod) simple_impl
+
+  implicit none
+
+contains
+
+  module function returnerer(thing)
+
+    implicit none
+
+    integer, intent(in) :: thing
+    integer :: returnerer
+
+    returnerer = 2 * thing
+
+  end function returnerer
+
+end submodule simple_impl
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/simple_mod.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/simple_mod.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/simple_mod.f90	(revision 5095)
@@ -0,0 +1,13 @@
+module simple_mod
+
+  implicit none
+
+  interface
+    module function returnerer(thing)
+      implicit none
+      integer, intent(in) :: thing
+      integer :: returnerer
+    end function returnerer
+  end interface
+
+end module simple_mod
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/test.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/test.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/56-build-fortran-submodule/src/test.f90	(revision 5095)
@@ -0,0 +1,21 @@
+program test
+
+  use iso_fortran_env, only : output_unit
+
+  use class_mod,  only : bar_type
+  use simple_mod, only : returnerer
+
+  implicit none
+
+  type(bar_type) :: thing
+
+  thing = bar_type(12)
+
+  write(output_unit, '("Returner ", I0)') returnerer(7)
+  write(output_unit, '()')
+
+  write(output_unit, '("Start with ", I0)') thing%how_much()
+  call thing%mangle(17)
+  write(output_unit, '("After mangle ", I0)') thing%how_much()
+
+end program test
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-make/test_header	(revision 5095)
@@ -0,0 +1,54 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+
+. $(dirname $0)/../lib/bash/test_header
+
+function file_cmp() {
+    local TEST_KEY=$1
+    local FILE_ACTUAL=$2
+    local FILE_EXPECT=${3:--}
+    if diff -u $FILE_EXPECT $FILE_ACTUAL >&2; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
+
+function file_test() {
+    local TEST_KEY=$1
+    local FILE=$2
+    local OPTION=${3:--e}
+    if test $OPTION $FILE; then
+        pass $TEST_KEY
+    else
+        fail $TEST_KEY
+    fi
+}
+
+function file_grep() {
+    local TEST_KEY=$1
+    local PATTERN=$2
+    local FILE=$3
+    if grep -q -e "$PATTERN" $FILE; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/00-simple.t	(revision 5095)
@@ -0,0 +1,279 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm merge".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 42
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output before merging.
+test_mergeinfo "$TEST_KEY_BASE-pre" \
+    $ROOT_URL/branches/dev/Share/merge1 - 9 <<__RESULTS__
+begin-prop
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1                  9       
+    |                  |       
+       --| |------------         branches/dev/Share/merge1
+      /                        
+     /                         
+  -------| |------------         trunk
+                       |       
+                       WC      
+end-info
+begin-eligible
+r5
+end-eligible
+begin-merged
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+# Tests fcm merge --dry-run
+TEST_KEY=$TEST_KEY_BASE-dry-run
+export SVN_EDITOR="sed -i 1i\foo"
+run_pass "$TEST_KEY" fcm merge --dry-run $ROOT_URL/branches/dev/Share/merge1
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+Eligible merge(s) from /${PROJECT}branches/dev/Share/merge1@9: 5
+--------------------------------------------------------------------------------
+Merge: /${PROJECT}branches/dev/Share/merge1@5
+ c.f.: /${PROJECT}trunk@1
+-------------------------------------------------------------------------dry-run
+--- Merging r4 through r5 into '.':
+A    added_directory
+A    added_directory/hello_constants.f90
+A    added_directory/hello_constants.inc
+A    added_directory/hello_constants_dummy.inc
+A    added_file
+A    module/tree_conflict_file
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+-------------------------------------------------------------------------dry-run
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge --dry-run
+TEST_KEY=$TEST_KEY_BASE-dry-run-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+?       unversioned_file
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge --dry-run
+TEST_KEY=$TEST_KEY_BASE-dry-run-diff
+run_pass "$TEST_KEY" svn diff
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm merge --non-interactive
+TEST_KEY=$TEST_KEY_BASE-non-interactive
+export SVN_EDITOR="sed -i 1i\foo" 
+run_pass "$TEST_KEY" fcm merge --non-interactive $ROOT_URL/branches/dev/Share/merge1
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+Eligible merge(s) from /${PROJECT}branches/dev/Share/merge1@9: 5
+--------------------------------------------------------------------------------
+Merge: /${PROJECT}branches/dev/Share/merge1@5
+ c.f.: /${PROJECT}trunk@1
+Merge succeeded.
+--------------------------------------------------------------------------actual
+--- Merging r4 through r5 into '.':
+A    added_directory
+A    added_directory/hello_constants.f90
+A    added_directory/hello_constants.inc
+A    added_directory/hello_constants_dummy.inc
+A    added_file
+A    module/tree_conflict_file
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+--- Recording mergeinfo for merge of r4 through r5 into '.':
+ U   .
+--------------------------------------------------------------------------actual
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge --non-interactive
+TEST_KEY=$TEST_KEY_BASE-non-interactive-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+ M      .
+?       unversioned_file
+A  +    added_directory
+A  +    added_file
+A  +    module/tree_conflict_file
+M       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+M       subroutine/hello_sub_dummy.h
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge --non-interactive
+TEST_KEY=$TEST_KEY_BASE-non-interactive-diff
+run_pass "$TEST_KEY" svn diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: .
+===================================================================
+--- .	(revision 9)
++++ .	(working copy)
+
+Property changes on: .
+___________________________________________________________________
+Added: svn:mergeinfo
+#IF SVN1.9/10/14 ## -0,0 +0,1 ##
+   Merged /branches/dev/Share/merge1:r4-5
+#IF SVN1.9/10/14 Index: added_directory/hello_constants.f90
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_directory/hello_constants.inc
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_directory/hello_constants_dummy.inc
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_file
+#IF SVN1.9/10/14 ===================================================================
+Index: lib/python/info/poems.py
+===================================================================
+--- lib/python/info/poems.py	(revision 9)
++++ lib/python/info/poems.py	(working copy)
+@@ -1,24 +1,23 @@
+-#!/usr/bin/env python
+-# -*- coding: utf-8 -*-
+ """The Python, by Hilaire Belloc
+ 
+ A Python I should not advise,--
+-It needs a doctor for its eyes,
++It needs a doctor FOR its eyes,
+ And has the measles yearly.
+-However, if you feel inclined
+-To get one (to improve your mind,
++However, if you feel INclINed
++To get one (
++to improve your mINd,
+ And not from fashion merely),
+ Allow no music near its cage;
+-And when it flies into a rage
++And when it flies INto a rage
+ Chastise it, most severely.
+-I had an aunt in Yucatan
++I had an aunt IN Yucatan
+ Who bought a Python from a man
+-And kept it for a pet.
++And kept it FOR a pet.
+ She died, because she never knew
+ These simple little rules and few;--
+-The Snake is living yet.
++The Snake is livINg yet.
+ """
+ 
+ import this
+ 
+-print "\n",  __doc__
++prINt "\n",  __doc__
+Index: module/hello_constants.f90
+===================================================================
+--- module/hello_constants.f90	(revision 9)
++++ module/hello_constants.f90	(working copy)
+@@ -1,5 +1,5 @@
+ MODULE Hello_Constants
+ 
+-INCLUDE 'hello_constants_dummy.inc'
++INCLUDE 'hello_constants_dummy.INc'
+ 
+ END MODULE Hello_Constants
+Index: module/hello_constants.inc
+===================================================================
+--- module/hello_constants.inc	(revision 9)
++++ module/hello_constants.inc	(working copy)
+@@ -1 +1,2 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
++CHARACTER (
++LEN=80), PARAMETER :: hello_strINg = 'Hello Earth!!'
+Index: module/hello_constants_dummy.inc
+===================================================================
+--- module/hello_constants_dummy.inc	(revision 9)
++++ module/hello_constants_dummy.inc	(working copy)
+@@ -1 +1 @@
+-INCLUDE 'hello_constants.inc'
++INCLUDE 'hello_constants.INc'
+#IF SVN1.9/10/14 Index: module/tree_conflict_file
+#IF SVN1.9/10/14 ===================================================================
+Index: subroutine/hello_sub_dummy.h
+===================================================================
+--- subroutine/hello_sub_dummy.h	(revision 9)
++++ subroutine/hello_sub_dummy.h	(working copy)
+@@ -1 +1,2 @@
+ #include "hello_sub.h"
++Modified a line
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output after merging.
+test_mergeinfo "$TEST_KEY_BASE-post" \
+    $ROOT_URL/branches/dev/Share/merge1 - 9 <<__RESULTS__
+begin-prop
+/branches/dev/Share/merge1:4-5
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1                  9       
+    |                  |       
+       --| |------------         branches/dev/Share/merge1
+      /                        
+     /                         
+  -------| |------------         trunk
+                       |       
+                       WC      
+end-info
+begin-eligible
+end-eligible
+begin-merged
+r4
+r5
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/01-complex.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/01-complex.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/01-complex.t	(revision 5095)
@@ -0,0 +1,1583 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# More complex tests for "fcm merge".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 234
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+cd $TEST_DIR/wc
+svn switch -q $ROOT_URL/branches/dev/Share/merge1
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output before merging.
+test_mergeinfo "$TEST_KEY_BASE-trunk-into-branch-1-pre" \
+    $ROOT_URL/trunk - 9 <<__RESULTS__
+begin-prop
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1                  9       
+    |                  |       
+  -------| |------------         trunk
+     \                         
+      \                        
+       --| |------------         branches/dev/Share/merge1
+                       |       
+                       WC      
+end-info
+begin-eligible
+r8
+r9
+end-eligible
+begin-merged
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+# Tests fcm merge of trunk-into-branch (1)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-1-non-root
+cd module
+run_pass "$TEST_KEY" fcm merge --non-interactive trunk
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+$TEST_DIR/wc: working directory changed to top of working copy.
+Eligible merge(s) from /${PROJECT}trunk@9: 9 8
+--------------------------------------------------------------------------------
+Merge: /${PROJECT}trunk@9
+ c.f.: /${PROJECT}trunk@1
+Merge succeeded.
+--------------------------------------------------------------------------actual
+--- Merging r2 through r9 into '.':
+U    lib/python/info/__init__.py
+--- Recording mergeinfo for merge of r2 through r9 into '.':
+ U   .
+--------------------------------------------------------------------------actual
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+cd ..
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-1-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+ M      .
+?       unversioned_file
+M       lib/python/info/__init__.py
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-1-diff
+run_pass "$TEST_KEY" svn diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: .
+===================================================================
+--- .	(revision 9)
++++ .	(working copy)
+
+Property changes on: .
+___________________________________________________________________
+Added: svn:mergeinfo
+#IF SVN1.9/10/14 ## -0,0 +0,1 ##
+   Merged /${PROJECT}trunk:r2-9
+Index: lib/python/info/__init__.py
+===================================================================
+--- lib/python/info/__init__.py	(revision 9)
++++ lib/python/info/__init__.py	(working copy)
+@@ -0,0 +1,2 @@
++trunk change
++another trunk change
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm commit of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-1-commit
+run_pass "$TEST_KEY" fcm commit <<__IN__
+y
+__IN__
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : branches/dev/Share/merge1]
+[Sub-dir: ]
+ M      .
+M       lib/python/info/__init__.py
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@9 cf. /${PROJECT}trunk@1
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO A Share BRANCH.
+*** Please ensure that you have the owner's permission.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Sending        .
+Sending        lib/python/info/__init__.py
+Committed revision 10.
+Updating '.':
+At revision 10.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm log of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-1-log
+run_pass "$TEST_KEY" fcm log
+sed -i "s/\(.*|.*|\).*\(|.*\)$/\1 date \2/g" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+------------------------------------------------------------------------
+r10 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@9 cf. /${PROJECT}trunk@1
+
+------------------------------------------------------------------------
+r5 | $LOGNAME | date | 1 line
+
+Made changes for future merge of this branch
+------------------------------------------------------------------------
+r4 | $LOGNAME | date | 1 line
+
+Made a branch Created /${PROJECT}branches/dev/Share/merge1 from /trunk@1.
+------------------------------------------------------------------------
+r1 | $LOGNAME | date | 1 line
+
+initial trunk import
+------------------------------------------------------------------------
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output after merging.
+test_mergeinfo "$TEST_KEY_BASE-trunk-into-branch-1-post" \
+    $ROOT_URL/trunk - 10 <<__RESULTS__
+begin-prop
+/trunk:2-9
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1         9        10      
+    |         |        |       
+  -------| |------------         trunk
+     \         \               
+      \         \              
+       --| |------------         branches/dev/Share/merge1
+                       |       
+                       WC      
+end-info
+begin-eligible
+end-eligible
+begin-merged
+r8
+r9
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+# Tests fcm merge of branch-into-trunk (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-1
+BRANCH_MOD_FILE="added_directory/hello_constants_dummy.inc"
+echo "# added this line for simple repeat testing" >>$BRANCH_MOD_FILE
+svn commit -q -m "edit on branch for merge repeat test"
+svn update -q
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/trunk $TEST_DIR/wc
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output before merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-trunk-1-pre" \
+    $ROOT_URL/branches/dev/Share/merge1 - 11 <<__RESULTS__
+begin-prop
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1                  11      
+    |                  |       
+       --| |------------         branches/dev/Share/merge1
+      /         /              
+     /         /               
+  -------| |------------         trunk
+              |        |       
+              9        WC      
+end-info
+begin-eligible
+r5
+r10
+r11
+end-eligible
+begin-merged
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-1
+run_pass "$TEST_KEY" fcm merge --non-interactive branches/dev/Share/merge1
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+Eligible merge(s) from /${PROJECT}branches/dev/Share/merge1@11: 11 10
+--------------------------------------------------------------------------------
+Merge: /${PROJECT}branches/dev/Share/merge1@11
+ c.f.: /${PROJECT}trunk@9
+Merge succeeded.
+--------------------------------------------------------------------------actual
+--- Merging differences between repository URLs into '.':
+A    added_directory
+A    added_directory/hello_constants.f90
+A    added_directory/hello_constants.inc
+A    added_directory/hello_constants_dummy.inc
+A    added_file
+A    module/tree_conflict_file
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+--- Recording mergeinfo for merge between repository URLs into '.':
+ U   .
+--------------------------------------------------------------------------actual
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-1-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+ M      .
+A  +    added_directory
+A  +    added_file
+A  +    module/tree_conflict_file
+M       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+M       subroutine/hello_sub_dummy.h
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-1-diff
+run_pass "$TEST_KEY" svn diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: .
+===================================================================
+--- .	(revision 11)
++++ .	(working copy)
+
+Property changes on: .
+___________________________________________________________________
+Added: svn:mergeinfo
+#IF SVN1.9/10/14 ## -0,0 +0,1 ##
+   Merged /${PROJECT}branches/dev/Share/merge1:r4-11
+#IF SVN1.9/10/14 Index: added_directory/hello_constants.f90
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_directory/hello_constants.inc
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_directory/hello_constants_dummy.inc
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_file
+#IF SVN1.9/10/14 ===================================================================
+Index: lib/python/info/poems.py
+===================================================================
+--- lib/python/info/poems.py	(revision 11)
++++ lib/python/info/poems.py	(working copy)
+@@ -1,24 +1,23 @@
+-#!/usr/bin/env python
+-# -*- coding: utf-8 -*-
+ """The Python, by Hilaire Belloc
+ 
+ A Python I should not advise,--
+-It needs a doctor for its eyes,
++It needs a doctor FOR its eyes,
+ And has the measles yearly.
+-However, if you feel inclined
+-To get one (to improve your mind,
++However, if you feel INclINed
++To get one (
++to improve your mINd,
+ And not from fashion merely),
+ Allow no music near its cage;
+-And when it flies into a rage
++And when it flies INto a rage
+ Chastise it, most severely.
+-I had an aunt in Yucatan
++I had an aunt IN Yucatan
+ Who bought a Python from a man
+-And kept it for a pet.
++And kept it FOR a pet.
+ She died, because she never knew
+ These simple little rules and few;--
+-The Snake is living yet.
++The Snake is livINg yet.
+ """
+ 
+ import this
+ 
+-print "\n",  __doc__
++prINt "\n",  __doc__
+Index: module/hello_constants.f90
+===================================================================
+--- module/hello_constants.f90	(revision 11)
++++ module/hello_constants.f90	(working copy)
+@@ -1,5 +1,5 @@
+ MODULE Hello_Constants
+ 
+-INCLUDE 'hello_constants_dummy.inc'
++INCLUDE 'hello_constants_dummy.INc'
+ 
+ END MODULE Hello_Constants
+Index: module/hello_constants.inc
+===================================================================
+--- module/hello_constants.inc	(revision 11)
++++ module/hello_constants.inc	(working copy)
+@@ -1 +1,2 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
++CHARACTER (
++LEN=80), PARAMETER :: hello_strINg = 'Hello Earth!!'
+Index: module/hello_constants_dummy.inc
+===================================================================
+--- module/hello_constants_dummy.inc	(revision 11)
++++ module/hello_constants_dummy.inc	(working copy)
+@@ -1 +1 @@
+-INCLUDE 'hello_constants.inc'
++INCLUDE 'hello_constants.INc'
+#IF SVN1.9/10/14 Index: module/tree_conflict_file
+#IF SVN1.9/10/14 ===================================================================
+Index: subroutine/hello_sub_dummy.h
+===================================================================
+--- subroutine/hello_sub_dummy.h	(revision 11)
++++ subroutine/hello_sub_dummy.h	(working copy)
+@@ -1 +1,2 @@
+ #include "hello_sub.h"
++Modified a line
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm commit of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-1-commit
+run_pass "$TEST_KEY" fcm commit <<__IN__
+y
+__IN__
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : trunk]
+[Sub-dir: ]
+ M      .
+A  +    added_directory
+A  +    added_file
+A  +    module/tree_conflict_file
+M       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+M       subroutine/hello_sub_dummy.h
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@11 cf. /${PROJECT}trunk@9
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO THE TRUNK.
+*** Please ensure that your change conforms to your project's working practices.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Sending        .
+Adding         added_directory
+Adding         added_file
+Adding         module/tree_conflict_file
+Sending        lib/python/info/poems.py
+Sending        module/hello_constants.f90
+Sending        module/hello_constants.inc
+Sending        module/hello_constants_dummy.inc
+Sending        subroutine/hello_sub_dummy.h
+Committed revision 12.
+Updating '.':
+At revision 12.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm log of fcm merge branch-into-trunk (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-1-log
+run_pass "$TEST_KEY" fcm log
+sed -i "s/\(.*|.*|\).*\(|.*\)$/\1 date \2/g" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+------------------------------------------------------------------------
+r12 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@11 cf. /${PROJECT}trunk@9
+
+------------------------------------------------------------------------
+r9 | $LOGNAME | date | 1 line
+
+Made another trunk change
+------------------------------------------------------------------------
+r8 | $LOGNAME | date | 1 line
+
+Made trunk change
+------------------------------------------------------------------------
+r1 | $LOGNAME | date | 1 line
+
+initial trunk import
+------------------------------------------------------------------------
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output after merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-trunk-1-post" \
+    $ROOT_URL/branches/dev/Share/merge1 - 12 <<__RESULTS__
+begin-prop
+/branches/dev/Share/merge1:4-11
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1         11       12      
+    |         |        |       
+       --| |------------         branches/dev/Share/merge1
+      /        \               
+     /          \              
+  -------| |------------         trunk
+                       |       
+                       WC      
+end-info
+begin-eligible
+end-eligible
+begin-merged
+r4
+r5
+r10
+r11
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+# Tests fcm merge of branch-into-trunk (2)
+svn switch -q $ROOT_URL/branches/dev/Share/merge1
+MOD_FILE="added_file"
+echo "call_extra_feature()" >>$MOD_FILE
+svn commit -q -m "Made branch change to add extra feature"
+svn update -q
+# Create a new branch to up the revision number, as a test.
+init_branch merge3 $REPOS_URL
+# Checkout the trunk.
+svn switch -q $ROOT_URL/trunk
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output before merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-trunk-2-pre" \
+    $ROOT_URL/branches/dev/Share/merge1 - 14 <<__RESULTS__
+begin-prop
+/branches/dev/Share/merge1:4-11
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1         11       14      
+    |         |        |       
+       --| |------------         branches/dev/Share/merge1
+      /        \               
+     /          \              
+  -------| |------------         trunk
+                       |       
+                       WC      
+end-info
+begin-eligible
+r13
+end-eligible
+begin-merged
+r4
+r5
+r10
+r11
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-2
+run_pass "$TEST_KEY" fcm merge --non-interactive branches/dev/Share/merge1
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Eligible merge(s) from /${PROJECT}branches/dev/Share/merge1@14: 13
+--------------------------------------------------------------------------------
+Merge: /${PROJECT}branches/dev/Share/merge1@13
+ c.f.: /${PROJECT}branches/dev/Share/merge1@11
+Merge succeeded.
+--------------------------------------------------------------------------actual
+--- Merging r12 through r13 into '.':
+U    added_file
+--- Recording mergeinfo for merge of r12 through r13 into '.':
+ U   .
+--------------------------------------------------------------------------actual
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge branch-into-trunk (2)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-2-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+M       added_file
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge branch-into-trunk (2)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-2-diff
+run_pass "$TEST_KEY" svn diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: .
+===================================================================
+--- .	(revision 14)
++++ .	(working copy)
+
+Property changes on: .
+___________________________________________________________________
+Modified: svn:mergeinfo
+#IF SVN1.9/10/14 ## -0,0 +0,1 ##
+   Merged /${PROJECT}branches/dev/Share/merge1:r12-13
+Index: added_file
+===================================================================
+--- added_file	(revision 14)
++++ added_file	(working copy)
+@@ -1 +1,2 @@
+ INCLUDE 'hello_constants.INc'
++call_extra_feature()
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm commit of fcm merge branch-into-trunk (2)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-2-commit
+run_pass "$TEST_KEY" fcm commit <<__IN__
+y
+__IN__
+sed -i "/^Updating '.':$/d" "$TEST_DIR/$TEST_KEY.out"
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : trunk]
+[Sub-dir: ]
+ M      .
+M       added_file
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@13 cf. /${PROJECT}branches/dev/Share/merge1@11
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO THE TRUNK.
+*** Please ensure that your change conforms to your project's working practices.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Sending        .
+Sending        added_file
+Committed revision 15.
+At revision 15.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm log of fcm merge branch-into-trunk (2)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-2-log
+run_pass "$TEST_KEY" fcm log
+sed -i "s/\(.*|.*|\).*\(|.*\)$/\1 date \2/g" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+------------------------------------------------------------------------
+r15 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@13 cf. /${PROJECT}branches/dev/Share/merge1@11
+
+------------------------------------------------------------------------
+r12 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@11 cf. /${PROJECT}trunk@9
+
+------------------------------------------------------------------------
+r9 | $LOGNAME | date | 1 line
+
+Made another trunk change
+------------------------------------------------------------------------
+r8 | $LOGNAME | date | 1 line
+
+Made trunk change
+------------------------------------------------------------------------
+r1 | $LOGNAME | date | 1 line
+
+initial trunk import
+------------------------------------------------------------------------
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output after merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-trunk-2-post" \
+    $ROOT_URL/branches/dev/Share/merge1 - 15 <<__RESULTS__
+begin-prop
+/branches/dev/Share/merge1:4-13
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1         13       15      
+    |         |        |       
+       --| |------------         branches/dev/Share/merge1
+      /        \               
+     /          \              
+  -------| |------------         trunk
+                       |       
+                       WC      
+end-info
+begin-eligible
+end-eligible
+begin-merged
+r4
+r5
+r10
+r11
+r13
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+# Tests fcm merge of trunk-into-branch (2)
+echo "# trunk modification" >>$MOD_FILE
+svn commit -q -m "Made trunk change - a simple edit of $MOD_FILE"
+svn update -q
+svn switch -q $ROOT_URL/branches/dev/Share/merge1
+
+echo "# added another line for simple repeat testing" >>$BRANCH_MOD_FILE
+svn commit -q -m "Made branch change for merge repeat test"
+svn update -q
+#------------------------------------------------------------------------------
+# Test the various mergeinfo output before merging.
+test_mergeinfo "$TEST_KEY_BASE-trunk-into-branch-2-pre" \
+    $ROOT_URL/trunk - 17 <<__RESULTS__
+begin-prop
+/trunk:2-9
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1                  17      
+    |                  |       
+  -------| |------------         trunk
+     \          /              
+      \        /               
+       --| |------------         branches/dev/Share/merge1
+              |        |       
+              13       WC      
+end-info
+begin-eligible
+r12
+r15
+r16
+end-eligible
+begin-merged
+r8
+r9
+end-merged
+__RESULTS__
+#------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-2
+run_pass "$TEST_KEY" fcm merge --non-interactive trunk
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Eligible merge(s) from /${PROJECT}trunk@17: 16 15
+--------------------------------------------------------------------------------
+Merge: /${PROJECT}trunk@16
+ c.f.: /${PROJECT}branches/dev/Share/merge1@13
+Merge succeeded.
+--------------------------------------------------------------------------actual
+--- Merging differences between repository URLs into '.':
+U    added_file
+--- Recording mergeinfo for merge between repository URLs into '.':
+ U   .
+--------------------------------------------------------------------------actual
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge trunk-into-branch (2)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-2-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+M       added_file
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge trunk-into-branch (2)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-2-diff
+run_pass "$TEST_KEY" svn diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: .
+===================================================================
+--- .	(revision 17)
++++ .	(working copy)
+
+Property changes on: .
+___________________________________________________________________
+Modified: svn:mergeinfo
+#IF SVN1.9/10/14 ## -0,0 +0,1 ##
+   Merged /${PROJECT}trunk:r10-16
+Index: added_file
+===================================================================
+--- added_file	(revision 17)
++++ added_file	(working copy)
+@@ -1,2 +1,3 @@
+ INCLUDE 'hello_constants.INc'
+ call_extra_feature()
++# trunk modification
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm commit of fcm merge trunk-into-branch (2)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-2-commit
+run_pass "$TEST_KEY" fcm commit <<__IN__
+y
+__IN__
+sed -i "/^Updating '.':$/d" "$TEST_DIR/$TEST_KEY.out"
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : branches/dev/Share/merge1]
+[Sub-dir: ]
+ M      .
+M       added_file
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@16 cf. /${PROJECT}branches/dev/Share/merge1@13
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO A Share BRANCH.
+*** Please ensure that you have the owner's permission.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Sending        .
+Sending        added_file
+Committed revision 18.
+At revision 18.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm log of fcm merge trunk-into-branch (2)
+TEST_KEY=$TEST_KEY_BASE-trunk-into-branch-2-log
+run_pass "$TEST_KEY" fcm log
+sed -i "s/\(.*|.*|\).*\(|.*\)$/\1 date \2/g" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+------------------------------------------------------------------------
+r18 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@16 cf. /${PROJECT}branches/dev/Share/merge1@13
+
+------------------------------------------------------------------------
+r17 | $LOGNAME | date | 1 line
+
+Made branch change for merge repeat test
+------------------------------------------------------------------------
+r13 | $LOGNAME | date | 1 line
+
+Made branch change to add extra feature
+------------------------------------------------------------------------
+r11 | $LOGNAME | date | 1 line
+
+edit on branch for merge repeat test
+------------------------------------------------------------------------
+r10 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@9 cf. /${PROJECT}trunk@1
+
+------------------------------------------------------------------------
+r5 | $LOGNAME | date | 1 line
+
+Made changes for future merge of this branch
+------------------------------------------------------------------------
+r4 | $LOGNAME | date | 1 line
+
+Made a branch Created /${PROJECT}branches/dev/Share/merge1 from /trunk@1.
+------------------------------------------------------------------------
+r1 | $LOGNAME | date | 1 line
+
+initial trunk import
+------------------------------------------------------------------------
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output after merging.
+test_mergeinfo "$TEST_KEY_BASE-trunk-into-branch-2-post" \
+    $ROOT_URL/trunk - 18 <<__RESULTS__
+begin-prop
+/trunk:2-16
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1         16       18      
+    |         |        |       
+  -------| |------------         trunk
+     \         \               
+      \         \              
+       --| |------------         branches/dev/Share/merge1
+                       |       
+                       WC      
+end-info
+begin-eligible
+end-eligible
+begin-merged
+r8
+r9
+r12
+r15
+r16
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+# Tests fcm merge of branch-into-trunk (3)
+svn delete -q $BRANCH_MOD_FILE
+svn copy -q $MOD_FILE $MOD_FILE.add
+svn commit -q -m "Made branch change - deleted $BRANCH_MOD_FILE, copied $MOD_FILE"
+svn update -q
+svn switch -q $ROOT_URL/trunk
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output before merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-trunk-3-pre" \
+    $ROOT_URL/branches/dev/Share/merge1 - 19 <<__RESULTS__
+begin-prop
+/branches/dev/Share/merge1:4-13
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1                  19      
+    |                  |       
+       --| |------------         branches/dev/Share/merge1
+      /         /              
+     /         /               
+  -------| |------------         trunk
+              |        |       
+              16       WC      
+end-info
+begin-eligible
+r17
+r18
+r19
+end-eligible
+begin-merged
+r4
+r5
+r10
+r11
+r13
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-3
+run_pass "$TEST_KEY" fcm merge --non-interactive branches/dev/Share/merge1
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+Eligible merge(s) from /${PROJECT}branches/dev/Share/merge1@19: 19 18
+--------------------------------------------------------------------------------
+Merge: /${PROJECT}branches/dev/Share/merge1@19
+ c.f.: /${PROJECT}trunk@16
+Merge succeeded.
+--------------------------------------------------------------------------actual
+--- Merging differences between repository URLs into '.':
+D    added_directory/hello_constants_dummy.inc
+A    added_file.add
+--- Recording mergeinfo for merge between repository URLs into '.':
+ U   .
+--------------------------------------------------------------------------actual
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge branch-into-trunk (3)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-3-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+D       added_directory/hello_constants_dummy.inc
+A  +    added_file.add
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge branch-into-trunk (3)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-3-diff
+run_pass "$TEST_KEY" svn diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: .
+===================================================================
+--- .	(revision 19)
++++ .	(working copy)
+
+Property changes on: .
+___________________________________________________________________
+Modified: svn:mergeinfo
+#IF SVN1.9/10/14 ## -0,0 +0,1 ##
+   Merged /${PROJECT}branches/dev/Share/merge1:r14-19
+Index: added_directory/hello_constants_dummy.inc
+===================================================================
+--- added_directory/hello_constants_dummy.inc	(revision 19)
++++ added_directory/hello_constants_dummy.inc	(working copy)
+@@ -1,2 +0,0 @@
+-INCLUDE 'hello_constants.INc'
+-# added this line for simple repeat testing
+#IF SVN1.9/10/14 Index: added_file.add
+#IF SVN1.9/10/14 ===================================================================
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm commit of fcm merge branch-into-trunk (3)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-3-commit
+run_pass "$TEST_KEY" fcm commit <<__IN__
+y
+__IN__
+sed -i "/^Updating '.':$/d" $TEST_DIR/"$TEST_KEY.out"
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : trunk]
+[Sub-dir: ]
+ M      .
+A  +    added_file.add
+D       added_directory/hello_constants_dummy.inc
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@19 cf. /${PROJECT}trunk@16
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO THE TRUNK.
+*** Please ensure that your change conforms to your project's working practices.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Sending        .
+Adding         added_file.add
+Deleting       added_directory/hello_constants_dummy.inc
+Committed revision 20.
+At revision 20.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm log of fcm merge branch-into-trunk (3)
+TEST_KEY=$TEST_KEY_BASE-branch-into-trunk-3-log
+run_pass "$TEST_KEY" fcm log $ROOT_URL
+sed -i "s/\(.*|.*|\).*\(|.*\)$/\1 date \2/g" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+------------------------------------------------------------------------
+r20 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@19 cf. /${PROJECT}trunk@16
+
+------------------------------------------------------------------------
+r19 | $LOGNAME | date | 1 line
+
+Made branch change - deleted added_directory/hello_constants_dummy.inc, copied added_file
+------------------------------------------------------------------------
+r18 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@16 cf. /${PROJECT}branches/dev/Share/merge1@13
+
+------------------------------------------------------------------------
+r17 | $LOGNAME | date | 1 line
+
+Made branch change for merge repeat test
+------------------------------------------------------------------------
+r16 | $LOGNAME | date | 1 line
+
+Made trunk change - a simple edit of added_file
+------------------------------------------------------------------------
+r15 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@13 cf. /${PROJECT}branches/dev/Share/merge1@11
+
+------------------------------------------------------------------------
+r14 | $LOGNAME | date | 1 line
+
+Made a branch Created /branches/dev/Share/merge3 from /trunk@1.
+------------------------------------------------------------------------
+r13 | $LOGNAME | date | 1 line
+
+Made branch change to add extra feature
+------------------------------------------------------------------------
+r12 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@11 cf. /${PROJECT}trunk@9
+
+------------------------------------------------------------------------
+r11 | $LOGNAME | date | 1 line
+
+edit on branch for merge repeat test
+------------------------------------------------------------------------
+r10 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@9 cf. /${PROJECT}trunk@1
+
+------------------------------------------------------------------------
+r9 | $LOGNAME | date | 1 line
+
+Made another trunk change
+------------------------------------------------------------------------
+r8 | $LOGNAME | date | 1 line
+
+Made trunk change
+------------------------------------------------------------------------
+r7 | $LOGNAME | date | 1 line
+
+Made changes for future merge
+------------------------------------------------------------------------
+r6 | $LOGNAME | date | 1 line
+
+Made a branch Created /${PROJECT}branches/dev/Share/merge2 from /trunk@1.
+------------------------------------------------------------------------
+r5 | $LOGNAME | date | 1 line
+
+Made changes for future merge of this branch
+------------------------------------------------------------------------
+r4 | $LOGNAME | date | 1 line
+
+Made a branch Created /${PROJECT}branches/dev/Share/merge1 from /trunk@1.
+------------------------------------------------------------------------
+r3 | $LOGNAME | date | 1 line
+
+ 
+------------------------------------------------------------------------
+r2 | $LOGNAME | date | 1 line
+
+make tags
+------------------------------------------------------------------------
+r1 | $LOGNAME | date | 1 line
+
+initial trunk import
+------------------------------------------------------------------------
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Test the various mergeinfo output after merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-trunk-3-post" \
+    $ROOT_URL/branches/dev/Share/merge1 - 20 <<__RESULTS__
+begin-prop
+/branches/dev/Share/merge1:4-19
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1         19       20      
+    |         |        |       
+       --| |------------         branches/dev/Share/merge1
+      /        \               
+     /          \              
+  -------| |------------         trunk
+                       |       
+                       WC      
+end-info
+begin-eligible
+end-eligible
+begin-merged
+r4
+r5
+r10
+r11
+r13
+r17
+r18
+r19
+end-merged
+__RESULTS__
+#-------------------------------------------------------------------------------
+# Tests fcm merge of branch-into-branch (1)
+cd $TEST_DIR
+rm -rf $TEST_DIR/wc
+mkdir $TEST_DIR/wc
+svn checkout -q $ROOT_URL/branches/dev/Share/merge2 $TEST_DIR/wc
+cd $TEST_DIR/wc
+BRANCH_2_MOD_FILE="module/hello_constants.f90"
+echo "Second branch change" >>$BRANCH_2_MOD_FILE
+svn commit -q -m "Made branch change - added to $BRANCH_2_MOD_FILE"
+svn update -q
+#------------------------------------------------------------------------------
+# Test the various mergeinfo output before merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-branch-1-pre" \
+    $ROOT_URL/branches/dev/Share/merge1 - 21 <<__RESULTS__
+begin-prop
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1                  21      
+    |                  |       
+       --| |------------         branches/dev/Share/merge1
+  ... /                        
+      \                        
+       --| |------------         branches/dev/Share/merge2
+                       |       
+                       WC      
+end-info
+begin-eligible
+r5
+r10
+r11
+r13
+r17
+r18
+r19
+end-eligible
+begin-merged
+end-merged
+__RESULTS__
+#------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-branch-into-branch-1
+run_pass "$TEST_KEY" fcm merge $ROOT_URL/branches/dev/Share/merge1 <<__IN__
+13
+y
+__IN__
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+Eligible merge(s) from /${PROJECT}branches/dev/Share/merge1@21: 19 18 17 13 11 10 5
+Enter a revision (or just press <return> for "19"): --------------------------------------------------------------------------------
+Merge: /${PROJECT}branches/dev/Share/merge1@13
+ c.f.: /${PROJECT}trunk@1
+-------------------------------------------------------------------------dry-run
+--- Merging r4 through r13 into '.':
+ U   .
+A    added_directory
+A    added_directory/hello_constants.f90
+A    added_directory/hello_constants.inc
+A    added_directory/hello_constants_dummy.inc
+A    added_file
+A    module/tree_conflict_file
+U    lib/python/info/__init__.py
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+-------------------------------------------------------------------------dry-run
+Would you like to go ahead with the merge?
+Enter "y" or "n" (or just press <return> for "n"): 
+Merge succeeded.
+--------------------------------------------------------------------------actual
+--- Merging r4 through r13 into '.':
+ U   .
+A    added_directory
+A    added_directory/hello_constants.f90
+A    added_directory/hello_constants.inc
+A    added_directory/hello_constants_dummy.inc
+A    added_file
+A    module/tree_conflict_file
+U    lib/python/info/__init__.py
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+--- Recording mergeinfo for merge of r4 through r13 into '.':
+ G   .
+--------------------------------------------------------------------------actual
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn status result of fcm merge branch-into-branch (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-branch-1-status
+run_pass "$TEST_KEY" svn status --config-dir=$TEST_DIR/.subversion/
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+ M      .
+A  +    added_directory
+A  +    added_file
+A  +    module/tree_conflict_file
+M       lib/python/info/__init__.py
+M       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+M       subroutine/hello_sub_dummy.h
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests svn diff result of fcm merge branch-into-branch (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-branch-1-diff
+run_pass "$TEST_KEY" svn diff
+diff_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp_filtered "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+
+Index: .
+===================================================================
+--- .	(revision 21)
++++ .	(working copy)
+
+Property changes on: .
+___________________________________________________________________
+Added: svn:mergeinfo
+#IF SVN1.9/10/14 ## -0,0 +0,2 ##
+   Merged /${PROJECT}trunk:r2-9
+   Merged /${PROJECT}branches/dev/Share/merge1:r4-13
+#IF SVN1.9/10/14 Index: added_directory/hello_constants.f90
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_directory/hello_constants.inc
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_directory/hello_constants_dummy.inc
+#IF SVN1.9/10/14 ===================================================================
+#IF SVN1.9/10/14 Index: added_file
+#IF SVN1.9/10/14 ===================================================================
+Index: lib/python/info/__init__.py
+===================================================================
+--- lib/python/info/__init__.py	(revision 21)
++++ lib/python/info/__init__.py	(working copy)
+@@ -0,0 +1,2 @@
++trunk change
++another trunk change
+Index: lib/python/info/poems.py
+===================================================================
+--- lib/python/info/poems.py	(revision 21)
++++ lib/python/info/poems.py	(working copy)
+@@ -1,24 +1,23 @@
+-#!/usr/bin/env python
+-# -*- coding: utf-8 -*-
+ """The Python, by Hilaire Belloc
+ 
+ A Python I should not advise,--
+-It needs a doctor for its eyes,
++It needs a doctor FOR its eyes,
+ And has the measles yearly.
+-However, if you feel inclined
+-To get one (to improve your mind,
++However, if you feel INclINed
++To get one (
++to improve your mINd,
+ And not from fashion merely),
+ Allow no music near its cage;
+-And when it flies into a rage
++And when it flies INto a rage
+ Chastise it, most severely.
+-I had an aunt in Yucatan
++I had an aunt IN Yucatan
+ Who bought a Python from a man
+-And kept it for a pet.
++And kept it FOR a pet.
+ She died, because she never knew
+ These simple little rules and few;--
+-The Snake is living yet.
++The Snake is livINg yet.
+ """
+ 
+ import this
+ 
+-print "\n",  __doc__
++prINt "\n",  __doc__
+Index: module/hello_constants.f90
+===================================================================
+--- module/hello_constants.f90	(revision 21)
++++ module/hello_constants.f90	(working copy)
+@@ -1,6 +1,6 @@
+ MODULE Hello_Constants
+ 
+-INCLUDE 'hello_constants_dummy.inc'
++INCLUDE 'hello_constants_dummy.INc'
+ 
+ END MODULE Hello_Constants
+ Second branch change
+Index: module/hello_constants.inc
+===================================================================
+--- module/hello_constants.inc	(revision 21)
++++ module/hello_constants.inc	(working copy)
+@@ -1 +1,2 @@
+-CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
++CHARACTER (
++LEN=80), PARAMETER :: hello_strINg = 'Hello Earth!!'
+Index: module/hello_constants_dummy.inc
+===================================================================
+--- module/hello_constants_dummy.inc	(revision 21)
++++ module/hello_constants_dummy.inc	(working copy)
+@@ -1 +1 @@
+-INCLUDE 'hello_constants.inc'
++INCLUDE 'hello_constants.INc'
+#IF SVN1.9/10/14 Index: module/tree_conflict_file
+#IF SVN1.9/10/14 ===================================================================
+Index: subroutine/hello_sub_dummy.h
+===================================================================
+--- subroutine/hello_sub_dummy.h	(revision 21)
++++ subroutine/hello_sub_dummy.h	(working copy)
+@@ -1 +1,2 @@
+ #include "hello_sub.h"
++Modified a line
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm commit of fcm merge branch-into-branch (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-branch-1-commit
+run_pass "$TEST_KEY" fcm commit <<__IN__
+y
+__IN__
+commit_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+[info] sed -i 1i\foo: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+[Root   : $REPOS_URL]
+[Project: ${TEST_PROJECT:-}]
+[Branch : branches/dev/Share/merge2]
+[Sub-dir: ]
+ M      .
+A  +    added_directory
+A  +    added_file
+A  +    module/tree_conflict_file
+M       lib/python/info/__init__.py
+M       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+M       subroutine/hello_sub_dummy.h
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+foo
+Merged into /${PROJECT}branches/dev/Share/merge2: /${PROJECT}branches/dev/Share/merge1@13 cf. /${PROJECT}trunk@1
+--------------------------------------------------------------------------------
+*** WARNING: YOU ARE COMMITTING TO A Share BRANCH.
+*** Please ensure that you have the owner's permission.
+Would you like to commit this change?
+Enter "y" or "n" (or just press <return> for "n"): Sending        .
+Adding         added_directory
+Adding         added_file
+Adding         module/tree_conflict_file
+Sending        lib/python/info/__init__.py
+Sending        lib/python/info/poems.py
+Sending        module/hello_constants.f90
+Sending        module/hello_constants.inc
+Sending        module/hello_constants_dummy.inc
+Sending        subroutine/hello_sub_dummy.h
+Committed revision 22.
+Updating '.':
+At revision 22.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm log of fcm merge branch-into-branch (1)
+TEST_KEY=$TEST_KEY_BASE-branch-into-branch-1-log
+run_pass "$TEST_KEY" fcm log $REPOS_URL
+sed -i "s/\(.*|.*|\).*\(|.*\)$/\1 date \2/g" $TEST_DIR/$TEST_KEY.out
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+------------------------------------------------------------------------
+r22 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge2: /${PROJECT}branches/dev/Share/merge1@13 cf. /${PROJECT}trunk@1
+
+------------------------------------------------------------------------
+r21 | $LOGNAME | date | 1 line
+
+Made branch change - added to module/hello_constants.f90
+------------------------------------------------------------------------
+r20 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@19 cf. /${PROJECT}trunk@16
+
+------------------------------------------------------------------------
+r19 | $LOGNAME | date | 1 line
+
+Made branch change - deleted added_directory/hello_constants_dummy.inc, copied added_file
+------------------------------------------------------------------------
+r18 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@16 cf. /${PROJECT}branches/dev/Share/merge1@13
+
+------------------------------------------------------------------------
+r17 | $LOGNAME | date | 1 line
+
+Made branch change for merge repeat test
+------------------------------------------------------------------------
+r16 | $LOGNAME | date | 1 line
+
+Made trunk change - a simple edit of added_file
+------------------------------------------------------------------------
+r15 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@13 cf. /${PROJECT}branches/dev/Share/merge1@11
+
+------------------------------------------------------------------------
+r14 | $LOGNAME | date | 1 line
+
+Made a branch Created /branches/dev/Share/merge3 from /trunk@1.
+------------------------------------------------------------------------
+r13 | $LOGNAME | date | 1 line
+
+Made branch change to add extra feature
+------------------------------------------------------------------------
+r12 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}trunk: /${PROJECT}branches/dev/Share/merge1@11 cf. /${PROJECT}trunk@9
+
+------------------------------------------------------------------------
+r11 | $LOGNAME | date | 1 line
+
+edit on branch for merge repeat test
+------------------------------------------------------------------------
+r10 | $LOGNAME | date | 3 lines
+
+foo
+Merged into /${PROJECT}branches/dev/Share/merge1: /${PROJECT}trunk@9 cf. /${PROJECT}trunk@1
+
+------------------------------------------------------------------------
+r9 | $LOGNAME | date | 1 line
+
+Made another trunk change
+------------------------------------------------------------------------
+r8 | $LOGNAME | date | 1 line
+
+Made trunk change
+------------------------------------------------------------------------
+r7 | $LOGNAME | date | 1 line
+
+Made changes for future merge
+------------------------------------------------------------------------
+r6 | $LOGNAME | date | 1 line
+
+Made a branch Created /${PROJECT}branches/dev/Share/merge2 from /trunk@1.
+------------------------------------------------------------------------
+r5 | $LOGNAME | date | 1 line
+
+Made changes for future merge of this branch
+------------------------------------------------------------------------
+r4 | $LOGNAME | date | 1 line
+
+Made a branch Created /${PROJECT}branches/dev/Share/merge1 from /trunk@1.
+------------------------------------------------------------------------
+r3 | $LOGNAME | date | 1 line
+
+ 
+------------------------------------------------------------------------
+r2 | $LOGNAME | date | 1 line
+
+make tags
+------------------------------------------------------------------------
+r1 | $LOGNAME | date | 1 line
+
+initial trunk import
+------------------------------------------------------------------------
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#------------------------------------------------------------------------------
+# Test the various mergeinfo output after merging.
+test_mergeinfo "$TEST_KEY_BASE-branch-into-branch-1-post" \
+    $ROOT_URL/branches/dev/Share/merge1 - 22 <<__RESULTS__
+begin-prop
+/branches/dev/Share/merge1:4-13
+/trunk:2-9
+end-prop
+begin-info
+    youngest common ancestor
+    |         last full merge
+    |         |        tip of branch
+    |         |        |         repository path
+
+    1         13       22      
+    |         |        |       
+       --| |------------         branches/dev/Share/merge1
+  ... /        \               
+      \         \              
+       --| |------------         branches/dev/Share/merge2
+                       |       
+                       WC      
+end-info
+begin-eligible
+r17
+r18
+r19
+end-eligible
+begin-merged
+r4
+r5
+r10
+r11
+r13
+end-merged
+__RESULTS__
+
+#-------------------------------------------------------------------------------
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/02-reverse.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/02-reverse.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/02-reverse.t	(revision 5095)
@@ -0,0 +1,135 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm merge --reverse".
+#-------------------------------------------------------------------------------
+. "$(dirname "$0")/test_header"
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 13
+#-------------------------------------------------------------------------------
+svnadmin create 'foo'
+URL_FOO="file://${PWD}/foo"
+svn mkdir -q -m"season greetings" --no-auth-cache "${URL_FOO}/greet"
+echo 'Merry Xmas' >'xmas.txt'
+echo 'Happy New Year' >'new-year.txt'
+for NAME in 'xmas.txt' 'new-year.txt'; do
+    svn import -q -m"import ${NAME}" --no-auth-cache \
+        "${NAME}" "${URL_FOO}/greet/${NAME}"
+done
+svn mkdir -q -m"hello world" --no-auth-cache --parents "${URL_FOO}/hello/trunk"
+svn co -q "${URL_FOO}/hello/trunk" 'hello-wc'
+cat >'hello-wc/world.txt' <<'__TXT__'
+Hello Mercury!
+Hello Venus!
+Hollow Earth!
+Hello Mars!
+__TXT__
+svn add -q 'hello-wc/world.txt'
+svn ci -q -m'hollow worlds' --no-auth-cache 'hello-wc'
+cat >'hello-wc/world.txt' <<'__TXT__'
+Hello Mercury!
+Hello Venus!
+Hello Earth!
+Hello Mars!
+__TXT__
+svn ci -q -m'hello worlds' --no-auth-cache 'hello-wc'
+rm -fr 'hello-wc'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-r3"
+svn co -q "${URL_FOO}/greet" 'greet-wc'
+cd 'greet-wc'
+run_pass "${TEST_KEY}" fcm merge --reverse --non-interactive
+cd ..
+svn status 'greet-wc' >"${TEST_KEY}.status"
+file_cmp "${TEST_KEY}.status" "${TEST_KEY}.status" <<'__STATUS__'
+D       greet-wc/new-year.txt
+__STATUS__
+file_cmp "${TEST_KEY}.message" 'greet-wc/#commit_message#' <<'__MESSAGE__'
+--FCM message (will be inserted automatically)--
+Reversed r3 of /greet
+
+__MESSAGE__
+rm -rf 'greet-wc'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-r2"
+svn co -q "${URL_FOO}/greet" 'greet-wc'
+cd 'greet-wc'
+run_pass "${TEST_KEY}" fcm merge --reverse -r 2 --non-interactive
+cd ..
+
+svn status 'greet-wc' >"${TEST_KEY}.status"
+file_cmp "${TEST_KEY}.status" "${TEST_KEY}.status" <<'__STATUS__'
+D       greet-wc/xmas.txt
+__STATUS__
+file_cmp "${TEST_KEY}.message" 'greet-wc/#commit_message#' <<'__MESSAGE__'
+--FCM message (will be inserted automatically)--
+Reversed r2 of /greet
+
+__MESSAGE__
+rm -rf 'greet-wc'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-r2-r3"
+svn co -q "${URL_FOO}/greet" 'greet-wc'
+cd 'greet-wc'
+run_pass "${TEST_KEY}" fcm merge --reverse -r 3:1 --non-interactive
+cd ..
+
+svn status 'greet-wc' | sort >"${TEST_KEY}.status"
+file_cmp "${TEST_KEY}.status" "${TEST_KEY}.status" <<'__STATUS__'
+D       greet-wc/new-year.txt
+D       greet-wc/xmas.txt
+__STATUS__
+file_cmp "${TEST_KEY}.message" 'greet-wc/#commit_message#' <<'__MESSAGE__'
+--FCM message (will be inserted automatically)--
+Reversed r3:1 of /greet
+
+__MESSAGE__
+rm -rf 'greet-wc'
+#-------------------------------------------------------------------------------
+TEST_KEY="${TEST_KEY_BASE}-r6"
+svn co -q "${URL_FOO}/hello/trunk" 'hello-wc'
+cd 'hello-wc'
+run_pass "${TEST_KEY}" fcm merge --reverse --non-interactive
+cd ..
+svn status 'hello-wc' >"${TEST_KEY}.status"
+file_cmp "${TEST_KEY}.status" "${TEST_KEY}.status" <<'__STATUS__'
+M       hello-wc/world.txt
+__STATUS__
+svn diff 'hello-wc' >"${TEST_KEY}.diff"
+file_cmp "${TEST_KEY}.diff" "${TEST_KEY}.diff" <<'__DIFF__'
+Index: hello-wc/world.txt
+===================================================================
+--- hello-wc/world.txt	(revision 6)
++++ hello-wc/world.txt	(working copy)
+@@ -1,4 +1,4 @@
+ Hello Mercury!
+ Hello Venus!
+-Hello Earth!
++Hollow Earth!
+ Hello Mars!
+__DIFF__
+file_cmp "${TEST_KEY}.message" 'hello-wc/#commit_message#' <<'__MESSAGE__'
+--FCM message (will be inserted automatically)--
+Reversed r6 of /hello/trunk
+
+__MESSAGE__
+rm -rf 'hello-wc'
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-merge/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-recover-svn-repos/00-basic.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-recover-svn-repos/00-basic.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-recover-svn-repos/00-basic.t	(revision 5095)
@@ -0,0 +1,108 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "fcm-recover-svn-repos".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+if ! which svnadmin 1>/dev/null 2>/dev/null; then
+    skip_all 'svnadmin not available'
+fi
+tests 26
+#-------------------------------------------------------------------------------
+set -e
+mkdir -p etc srv/svn var/svn/{backups,cache,dumps}
+# Configuration
+export FCM_CONF_PATH="$PWD/etc"
+cat >etc/admin.cfg <<__CONF__
+svn_backup_dir=$PWD/var/svn/backups
+svn_dump_dir=$PWD/var/svn/dumps
+svn_group=
+svn_live_dir=$PWD/srv/svn
+__CONF__
+# Create some repositories and populate them
+# Repository 1
+svnadmin create srv/svn/bar
+svn co -q file://$PWD/srv/svn/bar
+echo 'Barley drink.' >bar/barley
+svn add -q bar/*
+svn ci -q -m'test 1' bar
+svnadmin hotcopy srv/svn/bar var/svn/backups/bar
+tar -C var/svn/backups -czf $PWD/var/svn/backups/bar.tgz bar
+svnadmin dump srv/svn/bar -r 1 --incremental --deltas -q \
+        | gzip >var/svn/dumps/bar-1.gz
+# Repository 2
+svnadmin create srv/svn/foo
+svn co -q file://$PWD/srv/svn/foo
+echo 'Number of football players = 0' >foo/football
+echo 'Food is yummy.' >foo/food
+svn add -q foo/*
+svn ci -q -m'test 1' foo
+svnadmin hotcopy srv/svn/foo var/svn/backups/foo
+tar -C var/svn/backups -czf $PWD/var/svn/backups/foo.tgz foo
+rm -fr var/svn/backups/foo
+echo 'Fool is a clown.' >foo/fool
+svn add -q foo/fool
+svn ci -q -m'test 2' foo
+for I in {1..11}; do
+    echo "Number of football players = $I" >foo/football
+    svn ci -q -m"incr football player" foo
+done
+for I in {1..13}; do
+    svnadmin dump srv/svn/foo -r $I --incremental --deltas -q \
+        | gzip >var/svn/dumps/foo-$I.gz
+done
+rm -fr bar foo
+set +e
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-live-exists"
+run_fail "$TEST_KEY" "$FCM_HOME/sbin/fcm-recover-svn-repos"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" </dev/null
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" <<__ERR__
+$PWD/srv/svn/bar: live repository exists.
+__ERR__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE"
+mv srv/svn/{bar,foo} var/svn/cache
+run_pass "$TEST_KEY" "$FCM_HOME/sbin/fcm-recover-svn-repos"
+# Check revisions
+for NAME in bar foo; do
+    svnlook youngest var/svn/cache/$NAME >"$TEST_KEY-youngest-$NAME.exp"
+    svnlook youngest srv/svn/$NAME >"$TEST_KEY-youngest-$NAME"
+    file_cmp "$TEST_KEY-youngest-$NAME" \
+        "$TEST_KEY-youngest-$NAME" "$TEST_KEY-youngest-$NAME.exp"
+    for I in $(seq 1 $(<"$TEST_KEY-youngest-$NAME.exp")); do
+        svnlook changed -r $I var/svn/cache/$NAME \
+            >"$TEST_KEY-changed-$NAME-$I.exp"
+        svnlook changed -r $I srv/svn/$NAME >"$TEST_KEY-changed-$NAME-$I"
+        file_cmp "$TEST_KEY-changed-$NAME-$I" \
+            "$TEST_KEY-changed-$NAME-$I" "$TEST_KEY-changed-$NAME-$I.exp"
+    done
+    svn export -q file://$PWD/var/svn/cache/$NAME $NAME.orig
+    svn export -q file://$PWD/srv/svn/$NAME $NAME
+    FILES_ORIG=$(cd $NAME.orig; find -type f)
+    FILES=$(cd $NAME; find -type f)
+    run_pass "$TEST_KEY-$NAME-n-files" \
+        test $(wc -l <<<"$FILES_ORIG") -eq $(wc -l <<<"$FILES")
+    for FILE in $FILES_ORIG; do
+        file_cmp "$TEST_KEY-cmp-$NAME-$FILE" "$NAME.orig/$FILE" "$NAME/$FILE"
+    done
+done
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-recover-svn-repos/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-recover-svn-repos/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-recover-svn-repos/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-status/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-status/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-status/00-simple.t	(revision 5095)
@@ -0,0 +1,65 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm status".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 4
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests the setup for fcm status testing
+svn switch -q $ROOT_URL/trunk
+touch added_file
+svn add -q added_file
+svn commit -q -m "trunk modifications"
+svn update -q
+TEST_KEY=$TEST_KEY_BASE-setup
+run_pass "$TEST_KEY" fcm merge --non-interactive branches/dev/Share/merge1
+rm subroutine/hello_sub.h
+svn delete -q --force lib/python/info/poems.py
+#-------------------------------------------------------------------------------
+# Tests fcm status result of fcm merge (1)
+TEST_KEY=$TEST_KEY_BASE-status
+run_pass "$TEST_KEY" fcm status --config-dir=$TEST_DIR/.subversion
+status_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+ M      .
+A  +    added_directory
+      C added_file
+      >   local file obstruction, incoming file add upon merge
+D       lib/python/info/poems.py
+M       module/hello_constants.f90
+M       module/hello_constants.inc
+M       module/hello_constants_dummy.inc
+A  +    module/tree_conflict_file
+!       subroutine/hello_sub.h
+M       subroutine/hello_sub_dummy.h
+?       unversioned_file
+Summary of conflicts:
+  Tree conflicts: 1
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-status/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-status/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-status/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/00-simple.t	(revision 5095)
@@ -0,0 +1,111 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm switch".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 12
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm switch trunk
+svn switch -q $ROOT_URL/branches/dev/Share/merge1
+TEST_KEY=$TEST_KEY_BASE-trunk
+run_pass "$TEST_KEY" fcm switch trunk <<<"y"
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+switch: status of "$TEST_DIR/wc":
+?       unversioned_file
+switch: continue?
+Enter "y" or "n" (or just press <return> for "n"): 
+D    added_directory
+D    added_file
+D    module/tree_conflict_file
+U    lib/python/info/__init__.py
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm switch merge1 branch
+rm unversioned_file
+TEST_KEY=$TEST_KEY_BASE-branch-1
+run_pass "$TEST_KEY" fcm switch branches/dev/Share/merge1 <<<'y'
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+A    added_directory
+A    added_directory/hello_constants.f90
+A    added_directory/hello_constants.inc
+A    added_directory/hello_constants_dummy.inc
+A    added_file
+A    module/tree_conflict_file
+U    lib/python/info/__init__.py
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm switch merge2 branch
+TEST_KEY=$TEST_KEY_BASE-branch-2
+run_pass "$TEST_KEY" fcm switch --non-interactive dev/Share/merge2
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+ U   subroutine/hello_sub.h
+A    renamed_added_file
+D    added_directory
+D    added_file
+D    module/tree_conflict_file
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm switch trunk, without .svn/entries
+TEST_KEY=$TEST_KEY_BASE-trunk-2
+rm -f .svn/entries
+run_pass "$TEST_KEY" fcm switch trunk <<<'y'
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<'__OUT__'
+ U   subroutine/hello_sub.h
+D    renamed_added_file
+U    lib/python/info/__init__.py
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+teardown
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/01-subtree.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/01-subtree.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/01-subtree.t	(revision 5095)
@@ -0,0 +1,102 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm switch".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 9
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm switch trunk
+svn switch -q $ROOT_URL/branches/dev/Share/merge1
+TEST_KEY=$TEST_KEY_BASE-trunk
+cd module
+run_pass "$TEST_KEY" fcm switch trunk <<__IN__
+y
+__IN__
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+switch: status of "$TEST_DIR/wc":
+?       $TEST_DIR/wc/unversioned_file
+switch: continue?
+Enter "y" or "n" (or just press <return> for "n"): 
+D    $TEST_DIR/wc/added_directory
+D    $TEST_DIR/wc/added_file
+D    tree_conflict_file
+U    $TEST_DIR/wc/lib/python/info/__init__.py
+U    $TEST_DIR/wc/lib/python/info/poems.py
+U    $TEST_DIR/wc/subroutine/hello_sub_dummy.h
+U    hello_constants.f90
+U    hello_constants.inc
+U    hello_constants_dummy.inc
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm switch merge1 branch
+rm ../unversioned_file
+TEST_KEY=$TEST_KEY_BASE-branch-1
+run_pass "$TEST_KEY" fcm switch branches/dev/Share/merge1 <<__IN__
+y
+__IN__
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+A    $TEST_DIR/wc/added_directory
+A    $TEST_DIR/wc/added_directory/hello_constants.f90
+A    $TEST_DIR/wc/added_directory/hello_constants.inc
+A    $TEST_DIR/wc/added_directory/hello_constants_dummy.inc
+A    $TEST_DIR/wc/added_file
+A    tree_conflict_file
+U    $TEST_DIR/wc/lib/python/info/__init__.py
+U    $TEST_DIR/wc/lib/python/info/poems.py
+U    $TEST_DIR/wc/subroutine/hello_sub_dummy.h
+U    hello_constants.f90
+U    hello_constants.inc
+U    hello_constants_dummy.inc
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm switch merge2 branch
+TEST_KEY=$TEST_KEY_BASE-branch-2
+run_pass "$TEST_KEY" fcm switch --non-interactive dev/Share/merge2
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+ U   $TEST_DIR/wc/subroutine/hello_sub.h
+A    $TEST_DIR/wc/renamed_added_file
+D    $TEST_DIR/wc/added_directory
+D    $TEST_DIR/wc/added_file
+D    tree_conflict_file
+U    $TEST_DIR/wc/lib/python/info/poems.py
+U    $TEST_DIR/wc/subroutine/hello_sub_dummy.h
+U    hello_constants.f90
+U    hello_constants.inc
+U    hello_constants_dummy.inc
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-switch/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/00-simple.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/00-simple.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/00-simple.t	(revision 5095)
@@ -0,0 +1,104 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm update".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 7
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm update -r PREV
+svn switch -q $ROOT_URL/branches/dev/Share/merge1
+TEST_KEY=$TEST_KEY_BASE-r-PREV
+run_pass "$TEST_KEY" fcm update -r PREV <<__IN__
+y
+__IN__
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+update: status of ".":
+?       unversioned_file
+update: continue?
+Enter "y" or "n" (or just press <return> for "n"): 
+Updating '.':
+D    added_directory
+D    added_file
+D    module/tree_conflict_file
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+Updated to revision 4.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm update
+rm unversioned_file
+TEST_KEY=$TEST_KEY_BASE-normal
+run_pass "$TEST_KEY" fcm update <<__IN__
+y
+__IN__
+sed -n "/^  *\*/p" "$TEST_DIR/$TEST_KEY.out" | sort -o \
+    "$TEST_DIR/$TEST_KEY.update-status.sorted.out"
+file_cmp "$TEST_KEY.update-status.sorted.out" \
+         "$TEST_KEY.update-status.sorted.out" <<__OUT__
+        *            added_directory
+        *            added_directory/hello_constants.f90
+        *            added_directory/hello_constants.inc
+        *            added_directory/hello_constants_dummy.inc
+        *            added_file
+        *            module/tree_conflict_file
+        *        4   .
+        *        4   lib/python/info/poems.py
+        *        4   module
+        *        4   module/hello_constants.f90
+        *        4   module/hello_constants.inc
+        *        4   module/hello_constants_dummy.inc
+        *        4   subroutine/hello_sub_dummy.h
+__OUT__
+sed -i "/^  *\*/d" "$TEST_DIR/$TEST_KEY.out"
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+update: status of ".":
+update: continue?
+Enter "y" or "n" (or just press <return> for "n"): 
+Updating '.':
+A    added_directory
+A    added_directory/hello_constants.f90
+A    added_directory/hello_constants.inc
+A    added_directory/hello_constants_dummy.inc
+A    added_file
+A    module/tree_conflict_file
+U    lib/python/info/poems.py
+U    module/hello_constants.f90
+U    module/hello_constants.inc
+U    module/hello_constants_dummy.inc
+U    subroutine/hello_sub_dummy.h
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/01-subtree.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/01-subtree.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/01-subtree.t	(revision 5095)
@@ -0,0 +1,105 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Basic tests for "fcm update".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+check_svn_version
+tests 7
+#-------------------------------------------------------------------------------
+setup
+init_repos
+init_merge_branches merge1 merge2 $REPOS_URL
+export SVN_EDITOR="sed -i 1i\foo"
+cd $TEST_DIR/wc
+#-------------------------------------------------------------------------------
+# Tests fcm update -r PREV
+svn switch -q $ROOT_URL/branches/dev/Share/merge1
+TEST_KEY=$TEST_KEY_BASE-r-PREV
+cd module
+run_pass "$TEST_KEY" fcm update -r PREV <<__IN__
+y
+__IN__
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+update: status of "$TEST_DIR/wc":
+?       $TEST_DIR/wc/unversioned_file
+update: continue?
+Enter "y" or "n" (or just press <return> for "n"): 
+Updating '$TEST_DIR/wc':
+D    $TEST_DIR/wc/added_directory
+D    $TEST_DIR/wc/added_file
+D    tree_conflict_file
+U    $TEST_DIR/wc/lib/python/info/poems.py
+U    $TEST_DIR/wc/subroutine/hello_sub_dummy.h
+U    hello_constants.f90
+U    hello_constants.inc
+U    hello_constants_dummy.inc
+Updated to revision 4.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+# Tests fcm update
+rm ../unversioned_file
+TEST_KEY=$TEST_KEY_BASE-normal
+run_pass "$TEST_KEY" fcm update <<__IN__
+y
+__IN__
+sed -n "/^  *\*/p" "$TEST_DIR/$TEST_KEY.out" | sort -k 3 -o \
+    "$TEST_DIR/$TEST_KEY.update-status.sorted.out"
+file_cmp "$TEST_KEY.update-status.sorted.out" \
+         "$TEST_KEY.update-status.sorted.out" <<__OUT__
+        *            $TEST_DIR/wc/added_directory
+        *            $TEST_DIR/wc/added_directory/hello_constants.f90
+        *            $TEST_DIR/wc/added_directory/hello_constants.inc
+        *            $TEST_DIR/wc/added_directory/hello_constants_dummy.inc
+        *            $TEST_DIR/wc/added_file
+        *            $TEST_DIR/wc/module/tree_conflict_file
+        *        4   $TEST_DIR/wc
+        *        4   $TEST_DIR/wc/lib/python/info/poems.py
+        *        4   $TEST_DIR/wc/module
+        *        4   $TEST_DIR/wc/module/hello_constants.f90
+        *        4   $TEST_DIR/wc/module/hello_constants.inc
+        *        4   $TEST_DIR/wc/module/hello_constants_dummy.inc
+        *        4   $TEST_DIR/wc/subroutine/hello_sub_dummy.h
+__OUT__
+sed -i "/^  *\*/d" "$TEST_DIR/$TEST_KEY.out"
+merge_sort "$TEST_DIR/$TEST_KEY.out" "$TEST_DIR/$TEST_KEY.sorted.out"
+file_cmp "$TEST_KEY.sorted.out" "$TEST_KEY.sorted.out" <<__OUT__
+update: status of "$TEST_DIR/wc":
+update: continue?
+Enter "y" or "n" (or just press <return> for "n"): 
+Updating '$TEST_DIR/wc':
+A    $TEST_DIR/wc/added_directory
+A    $TEST_DIR/wc/added_directory/hello_constants.f90
+A    $TEST_DIR/wc/added_directory/hello_constants.inc
+A    $TEST_DIR/wc/added_directory/hello_constants_dummy.inc
+A    $TEST_DIR/wc/added_file
+A    tree_conflict_file
+U    $TEST_DIR/wc/lib/python/info/poems.py
+U    $TEST_DIR/wc/subroutine/hello_sub_dummy.h
+U    hello_constants.f90
+U    hello_constants.inc
+U    hello_constants_dummy.inc
+Updated to revision 9.
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+teardown
+#-------------------------------------------------------------------------------
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/fcm-update/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/lib/bash/svn_test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/lib/bash/svn_test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/lib/bash/svn_test_header	(revision 5095)
@@ -0,0 +1,235 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Optional enviroment variables:
+#   TEST_PROJECT (tests using given project name)
+#   TEST_REMOTE_HOST (tests using svn+ssh repositories located on given host)
+# ------------------------------------------------------------------------------
+
+. $(dirname $0)/../lib/bash/test_header
+
+function file_xxdiff() {
+    local TEST_KEY=$1
+    local FILE_ACTUAL=$2
+    local FILE_EXPECT=${3:--}
+    if xxdiff -D $TEST_DIR/$FILE_ACTUAL $FILE_EXPECT; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
+
+function init_repos() {
+    if [[ -n ${TEST_REMOTE_HOST:-} ]]; then
+        TEST_REMOTE_DIR=$(ssh $TEST_REMOTE_HOST "mktemp -d")
+        ssh $TEST_REMOTE_HOST bash -l -c "svnadmin create --fs-type fsfs\
+            $TEST_REMOTE_DIR"
+        REPOS_URL="svn+ssh://${TEST_REMOTE_HOST}$TEST_REMOTE_DIR"
+    else
+        svnadmin create --fs-type fsfs $TEST_DIR/test_repos
+        REPOS_URL="file://$TEST_DIR/test_repos"
+    fi
+    ROOT_URL=$REPOS_URL
+    PROJECT=
+    if [[ -n ${TEST_PROJECT:-} ]]; then
+        ROOT_URL=$REPOS_URL/$TEST_PROJECT
+        PROJECT=$TEST_PROJECT"/"
+    fi
+    svn import -q $TEST_SOURCE_DIR/../etc/repo_files \
+        $REPOS_URL/$PROJECT/trunk -m "initial trunk import"
+    svn mkdir -q $REPOS_URL/$PROJECT/tags -m "make tags"
+    svn mkdir -q --parents $REPOS_URL/$PROJECT/branches/dev/Share -m " "
+}
+
+function init_repos_layout_roses() {
+    if [[ -n ${TEST_REMOTE_HOST:-} ]]; then
+        TEST_REMOTE_DIR=$(ssh $TEST_REMOTE_HOST "mktemp -d")
+        ssh $TEST_REMOTE_HOST "svnadmin create --fs-type fsfs $TEST_REMOTE_DIR"
+        REPOS_URL="svn+ssh://${TEST_REMOTE_HOST}$TEST_REMOTE_DIR"
+    else
+        svnadmin create --fs-type fsfs $TEST_DIR/test_repos
+        REPOS_URL="file://$TEST_DIR/test_repos"
+    fi
+    svn import -q $TEST_SOURCE_DIR/../etc/repo_files \
+        $REPOS_URL/a/a/0/0/0/trunk/ -m "initial trunk import"
+    TMPFILE=$(mktemp)
+    cat >$TMPFILE <<__LAYOUT__
+depth-project = 5
+depth-branch = 1
+depth-tag = 1
+dir-trunk = trunk
+dir-branch =
+dir-tag =
+level-owner-branch =
+level-owner-tag =
+template-branch =
+template-tag =
+__LAYOUT__
+    TMPDIR=$(mktemp -d)
+    svn checkout -q $REPOS_URL $TMPDIR
+    svn propset -q --file=$TMPFILE fcm:layout $TMPDIR
+    svn commit -q -m " " $TMPDIR
+    rm -f $TMPFILE
+    rm -rf $TMPDIR
+    ROOT_URL=$REPOS_URL
+}
+
+function init_branch() {
+    local BRANCH_NAME=$1
+    local REPOS_URL=$2
+    local ROOT_URL=$REPOS_URL
+    local ROOT_PATH=
+    if [[ -n ${TEST_PROJECT:-} ]]; then
+        ROOT_URL=$REPOS_URL/$TEST_PROJECT
+        ROOT_PATH=$ROOT_PATH/$TEST_PROJECT
+    fi
+    MESSAGE=$(echo -e "Created $ROOT_PATH/branches/dev/Share/$BRANCH_NAME from /trunk@1.")
+    svn copy -q -r1 $ROOT_URL/trunk $ROOT_URL/branches/dev/Share/$BRANCH_NAME \
+                    -m "Made a branch $MESSAGE"
+}
+
+function init_branch_wc() {
+    local BRANCH_NAME=$1
+    local REPOS_URL=$2
+    local ROOT_URL=$REPOS_URL
+    if [[ -n ${TEST_PROJECT:-} ]]; then
+        ROOT_URL=$REPOS_URL/$TEST_PROJECT
+    fi
+    init_branch $BRANCH_NAME $REPOS_URL
+    svn checkout -q $ROOT_URL/branches/dev/Share/$BRANCH_NAME $TEST_DIR/wc
+}
+
+function init_merge_branches() {
+    local BRANCH_NAME=$1
+    local OTHER_BRANCH_NAME=$2
+    local REPOS_URL=$3
+    local ROOT_URL=$REPOS_URL
+    if [[ -n ${TEST_PROJECT:-} ]]; then
+        ROOT_URL=$REPOS_URL/$TEST_PROJECT
+    fi
+    init_branch_wc $BRANCH_NAME $REPOS_URL
+    cd $TEST_DIR/wc
+    modify_files="lib/python/info/__init__.py lib/python/info/poems.py \
+                  module/hello_constants.f90 module/hello_constants.inc \
+                  module/hello_constants_dummy.inc"
+    for file in $modify_files; do
+        sed -i "s/for/FOR/g; s/fi/end if/g; s/in/IN/g;" "$file"
+        sed -i "/#/d; /^ *!/d" "$file"
+        sed -i "s/!/!!/g; s/q/\nq/g; s/[(]/(\n/g" "$file"
+    done
+    copy_file="module/hello_constants_dummy.inc"    
+    copy_file_dir=$(dirname "$copy_file")
+    svn copy -q "$copy_file" "./added_file"
+    svn copy -q "$copy_file_dir" "added_directory"
+    touch "$copy_file_dir/tree_conflict_file"
+    append_line_file="subroutine/hello_sub_dummy.h"    
+    svn add -q "$copy_file_dir/tree_conflict_file"
+    echo "Modified a line" >>$append_line_file
+    svn commit -q -m "Made changes for future merge of this branch"
+    svn update -q
+    init_branch $OTHER_BRANCH_NAME $REPOS_URL
+    svn switch -q $ROOT_URL/branches/dev/Share/$OTHER_BRANCH_NAME
+    echo " " > unversioned_file
+    properties_file="subroutine/hello_sub.h"
+    svn propset -q svn:executable "executable" $properties_file
+    other_copy_file="module/hello_constants_dummy.inc"
+    svn copy -q "$other_copy_file" "renamed_added_file"
+    svn commit -q -m "Made changes for future merge"
+    svn update -q
+    svn switch -q $ROOT_URL/trunk
+    trunk_change_file="lib/python/info/__init__.py"
+    echo "trunk change" >>"$trunk_change_file"
+    svn commit -q -m "Made trunk change"
+    svn update -q
+    echo "another trunk change" >>"$trunk_change_file"
+    svn commit -q -m "Made another trunk change"
+    svn update -q
+}
+
+function get_results() {
+    local RESULT_KEY=$1
+    local RESULTS_FILE=$2
+    local BEGIN="^begin-$RESULT_KEY$"
+    local END="^end-$RESULT_KEY$"
+    local RESULTS_TEMP=$(mktemp)
+    sed -n "/$BEGIN/,/$END/{/$BEGIN\|$END/d; p;}" $RESULTS_FILE >$RESULTS_TEMP
+    echo $RESULTS_TEMP
+}
+
+function test_mergeinfo() {
+    local TEST_INFO_NAME="$1-svn-mergeinfo"
+    local DIFF_BRANCH=$2
+    local INPUT_RESULTS_FILE=${3:--}
+    local WC_REVISION=${4:-WC}
+    if [[ $SVN_MINOR_VERSION != "1.8" ]]; then
+        WC_REVISION=WC
+    elif (( WC_REVISION < 10 )); then
+        WC_REVISION="$WC_REVISION "
+    fi
+    local RESULTS_FILE=$(mktemp)
+    sed "s/^\(.\{23\}\)WC/\1$WC_REVISION/" "$INPUT_RESULTS_FILE" \
+        >"$RESULTS_FILE"
+    # Test svn:mergeinfo property.
+    TEST_KEY=$TEST_INFO_NAME-prop
+    PROP_RESULT_TEXT=$(cat <$(get_results prop $RESULTS_FILE))
+    if [[ -n "$PROP_RESULT_TEXT" ]]; then
+        run_pass "$TEST_KEY" svn propget svn:mergeinfo .
+        file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <$(get_results prop $RESULTS_FILE)
+        file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+    else
+        # Subversion 1.9 won't set null mergeinfo.
+        skip 3
+    fi
+    # Test svn mergeinfo output.
+    TEST_KEY=$TEST_INFO_NAME-info
+    run_pass "$TEST_KEY" svn mergeinfo $DIFF_BRANCH
+    file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <$(get_results info $RESULTS_FILE)    
+    file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+    # Test svn mergeinfo show-revs eligible before merge.
+    TEST_KEY=$TEST_INFO_NAME-show-revs-eligible
+    run_pass "$TEST_KEY" svn mergeinfo --show-revs eligible $DIFF_BRANCH
+    file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <$(get_results eligible $RESULTS_FILE)        
+    file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+    # Test svn mergeinfo show-revs merged before merge.
+    TEST_KEY=$TEST_INFO_NAME-show-revs-merged
+    run_pass "$TEST_KEY" svn mergeinfo --show-revs merged $DIFF_BRANCH
+    file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <$(get_results merged $RESULTS_FILE)            
+    file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+}
+
+function setup() {
+    mkdir -p $TEST_DIR/.subversion
+    mkdir -p $TEST_DIR/run
+    cd $TEST_DIR/run
+}
+
+function teardown() {
+    cd $TEST_DIR
+    rm -rf $TEST_DIR/test_repos
+    rm -rf $TEST_DIR/wc
+    rm -rf $TEST_DIR/run
+    rm -rf $TEST_DIR/.subversion
+    if [[ -n ${TEST_REMOTE_HOST:-} ]]; then
+        ssh $TEST_REMOTE_HOST "rm -rf $TEST_REMOTE_DIR"
+    fi
+}
+
+REPOS_URL=
+ROOT_URL=
+PROJECT=
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/lib/bash/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/lib/bash/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/lib/bash/test_header	(revision 5095)
@@ -0,0 +1,370 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# NAME
+#     test_header
+#
+# SYNOPSIS
+#     . $FCM_HOME/t/lib/bash/test_header
+#
+# DESCRIPTION
+#     Provide bash shell functions for writing tests for "fcm" commands to
+#     output in Perl's TAP format. Add "set -eu". Create a temporary working
+#     directory $TEST_DIR and change to it. Automatically increment test number.
+#     If $FCM_HOME is not specified, set it to point to the "fcm" source tree
+#     containing this script. Add $FCM_HOME/bin to the front of $PATH.
+#
+# FUNCTIONS
+#     tests N
+#         echo "1..$N".
+#     skip N REASON
+#         echo "ok $((++T)) # skip REASON" N times, where T is the test number.
+#     skip_all REASON
+#         echo "1..0 # SKIP $REASON" and exit.
+#     pass TEST_KEY
+#         echo "ok $T - $TEST_KEY" where T is the current test number.
+#     fail TEST_KEY
+#         echo "not ok $T - $TEST_KEY" where T is the current test number.
+#     run_pass TEST_KEY COMMAND ...
+#         Run $COMMAND. pass/fail $TEST_KEY if $COMMAND returns true/false.
+#         Write STDOUT and STDERR in $TEST_KEY.out and $TEST_KEY.err.
+#     run_fail TEST_KEY COMMAND ...
+#         Run $COMMAND. pass/fail $TEST_KEY if $COMMAND returns false/true.
+#         Write STDOUT and STDERR in $TEST_KEY.out and $TEST_KEY.err.
+#     file_cmp TEST_KEY FILE_ACTUAL [$FILE_EXPECT]
+#         Compare contents in $FILE_ACTUAL and $FILE_EXPECT. pass/fail
+#         $TEST_KEY if contents are identical/different. If $FILE_EXPECT is "-"
+#         or not defined, compare $FILE_ACTUAL with STDIN to this function.
+#     file_cmp_filtered TEST_KEY FILE_ACTUAL [$FILE_EXPECT]
+#         Preprocess $FILE_EXPECT based on relevant '#IF SVN1.X' prefixes before
+#         calling file_cmp.
+#     file_test TEST_KEY FILE [OPTION]
+#         pass/fail $TEST_KEY if "test $OPTION $FILE" returns 0/1. $OPTION is
+#         -e if not specified.
+#     file_grep TEST_KEY PATTERN FILE
+#         Run "grep -q PATTERN FILE". pass/fail $TEST_KEY accordingly.
+#     branch_tidy INPUT_FILE
+#         Standardise branch-create output between Subversion 1.8 and 1.9.
+#     commit_sort INPUT_FILE OUTPUT_FILE
+#         Sort status and transmitting info within the commit output.
+#     diff_sort INPUT_FILE OUTPUT_FILE
+#         Sort Subversion diff output by filename.
+#     status_sort INPUT_FILE OUTPUT_FILE
+#         Sort Subversion status lines.
+#     merge_sort INPUT_FILE OUTPUT_FILE
+#         Sort Subversion merge status lines.
+#     check_svn_version
+#         Check Subversion version and skip tests if not compatible.
+#     FINALLY
+#         This is run on EXIT or INT to remove the temporary working directory
+#         for the test. Call FINALLY_MORE if it is declared.
+#
+# VARIABLES
+#     FCM_HOME
+#         Root of FCM's installation. (Exported.)
+#     SIGNALS
+#         List of signals trapped by FINALLY, currently EXIT and INT.
+#     TEST_DIR
+#         Temporary directory that is also the working directory for this test.
+#     TEST_KEY_BASE
+#         Base root name of current test file.
+#     TEST_NUMBER
+#         Test number of latest test.
+#     TEST_SOURCE_DIR
+#         Directory containing the current test file.
+#-------------------------------------------------------------------------------
+set -eu
+
+SIGNALS="EXIT INT"
+TEST_DIR=
+function FINALLY() {
+    for S in $SIGNALS; do
+        trap '' $S
+    done
+    if [[ -n $TEST_DIR ]]; then
+        cd ~
+        # DPM
+        rm -rf $TEST_DIR
+    fi
+    if declare -F FINALLY_MORE >/dev/null; then
+        FINALLY_MORE
+    fi
+
+}
+for S in $SIGNALS; do
+    trap "FINALLY $S" $S
+done
+
+TEST_NUMBER=0
+
+function tests() {
+    echo "1..$1"
+}
+
+function skip() {
+    local N_SKIPS=$1
+    shift 1
+    local I=0
+    while ((I++ < N_SKIPS)); do
+        echo "ok $((++TEST_NUMBER)) # skip $@"
+    done
+}
+
+function skip_all() {
+    echo "1..0 # SKIP $@"
+    exit
+}
+
+function pass() {
+    echo "ok $((++TEST_NUMBER)) - $@"
+}
+
+function fail() {
+    echo "not ok $((++TEST_NUMBER)) - $@"
+}
+
+function run_pass() {
+    local TEST_KEY=$1
+    shift 1
+    if ! "$@" 1>$TEST_DIR/$TEST_KEY.out 2>$TEST_DIR/$TEST_KEY.err; then
+        fail $TEST_KEY
+        return
+    fi
+    pass $TEST_KEY
+}
+
+function run_fail() {
+    local TEST_KEY=$1
+    shift 1
+    if "$@" 1>$TEST_DIR/$TEST_KEY.out 2>$TEST_DIR/$TEST_KEY.err; then
+        fail $TEST_KEY
+        return
+    fi
+    pass $TEST_KEY
+}
+
+function file_cmp() {
+    local TEST_KEY=$1
+    local FILE_ACTUAL=$2
+    local FILE_EXPECT=${3:--}
+    if diff -u $FILE_EXPECT $TEST_DIR/$FILE_ACTUAL >&2; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
+
+function file_cmp_filtered() {
+    local FILE_EXPECT=${3:--}
+    if [[ $SVN_MINOR_VERSION == "1.8" ]]; then
+        cat $FILE_EXPECT | sed \
+          -e "s/^#IF SVN1.8 //g" \
+          -e "s/^#IF SVN1.8\/9 //g" \
+          -e "s/^#IF SVN1.8\/9\/10 //g" \
+          -e "/^#IF SVN1.9\/10\/14 /d" \
+          -e "/^#IF SVN1.10 /d" \
+          -e "/^#IF SVN1.10\/14 /d" \
+          -e "/^#IF SVN1.14 /d" \
+          >"$TEST_DIR/$TEST_KEY.filtered-ctrl"
+    elif [[ $SVN_MINOR_VERSION == "1.9" ]]; then
+        cat $FILE_EXPECT | sed \
+          -e "s/^#IF SVN1.8\/9 //g" \
+          -e "s/^#IF SVN1.8\/9\/10 //g" \
+          -e "s/^#IF SVN1.9\/10\/14 //g" \
+          -e "/^#IF SVN1.8 /d" \
+          -e "/^#IF SVN1.10 /d" \
+          -e "/^#IF SVN1.10\/14 /d" \
+          -e "/^#IF SVN1.14 /d" \
+          >"$TEST_DIR/$TEST_KEY.filtered-ctrl"
+    elif [[ $SVN_MINOR_VERSION == "1.10" ]]; then
+        cat $FILE_EXPECT | sed \
+          -e "s/^#IF SVN1.8\/9\/10 //g" \
+          -e "s/^#IF SVN1.9\/10\/14 //g" \
+          -e "s/^#IF SVN1.10 //g" \
+          -e "s/^#IF SVN1.10\/14 //g" \
+          -e "/^#IF SVN1.8 /d" \
+          -e "/^#IF SVN1.8\/9 /d" \
+          -e "/^#IF SVN1.14 /d" \
+          >"$TEST_DIR/$TEST_KEY.filtered-ctrl"
+    else
+        cat $FILE_EXPECT | sed \
+          -e "s/^#IF SVN1.10\/14 //g" \
+          -e "s/^#IF SVN1.9\/10\/14 //g" \
+          -e "/^#IF SVN1.8 /d" \
+          -e "/^#IF SVN1.8\/9 /d" \
+          -e "/^#IF SVN1.8\/9\/10 /d" \
+          -e "/^#IF SVN1.10 /d" \
+          -e "s/^#IF SVN1.14 //g" \
+          >"$TEST_DIR/$TEST_KEY.filtered-ctrl"
+    fi
+    file_cmp "$1" "$2" "$TEST_DIR/$TEST_KEY.filtered-ctrl"
+    rm "$TEST_DIR/$TEST_KEY.filtered-ctrl"
+}
+
+function file_test() {
+    local TEST_KEY=$1
+    local FILE=$2
+    local OPTION=${3:--e}
+    if test $OPTION $TEST_DIR/$FILE; then
+        pass $TEST_KEY
+    else
+        fail $TEST_KEY
+    fi
+}
+
+function file_grep() {
+    local TEST_KEY=$1
+    local PATTERN=$2
+    local FILE=$3
+    if grep -q -e "$PATTERN" $TEST_DIR/$FILE; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
+
+function branch_tidy() {
+    local INPUT_FILE=$TEST_DIR/$1
+    sed -i "/^Committing transaction/d; /^$/d" "$INPUT_FILE"
+}
+
+function commit_sort() {
+    local INPUT_FILE=$1
+    local OUTPUT_FILE=$2
+    local TMP_OUTPUT_FILE=$(mktemp)
+    # Sort the svn status part of the message
+    status_sort $INPUT_FILE $TMP_OUTPUT_FILE
+    # Sort the 'Adding/Deleting', etc part of the message
+    python3 -c 'import re, sys
+text = sys.stdin.read()
+sending_lines = re.findall(r"^\w+ing  +.*$", text, re.M)
+prefix = text[:text.index(sending_lines[0])]
+suffix = text[(text.index(sending_lines[-1]) + len(sending_lines[-1])):]
+sending_lines.sort()
+print(prefix + "\n".join(sending_lines) + suffix.rstrip())
+' <"$TMP_OUTPUT_FILE" >"$OUTPUT_FILE"
+    rm "$TMP_OUTPUT_FILE"
+    # Remove 1.8 to 1.9 specific changes (transmitting, transaction lines).
+    sed -i "/^Transmitting file data/d; /^Committing transaction/d; /^$/d" \
+        "$OUTPUT_FILE"
+}
+
+function diff_sort() {
+    local INPUT_FILE=$1
+    local OUTPUT_FILE=$2
+    # Sort the diff file order.
+    python3 -c 'import re, sys
+text = sys.stdin.read()
+print("\nIndex: ".join(
+    [l.strip() for l in sorted(re.compile(r"^Index: ", re.M).split(text))]))
+' <"$INPUT_FILE" >"$OUTPUT_FILE"
+    # In 1.9, new files are (nonexistent) rather than (working copy).
+    sed -i "s/(nonexistent)/(working copy)/" "$OUTPUT_FILE"
+}
+
+function status_sort() {
+    local INPUT_FILE=$1
+    local OUTPUT_FILE=$2
+    python3 -c 'import re, sys
+text = sys.stdin.read()
+status_lines = re.findall(r"^.{7} [\w./].*$", text, re.M)
+prefix = text[:text.index(status_lines[0])]
+suffix = text[(text.index(status_lines[-1]) + len(status_lines[-1])):]
+status_lines.sort()
+print(prefix + "\n".join(status_lines) + suffix.rstrip())
+' <"$INPUT_FILE" >"$OUTPUT_FILE"
+}
+
+function merge_sort() {
+    local INPUT_FILE=$1
+    local OUTPUT_FILE=$2
+    python3 -c 'import re, sys
+text = sys.stdin.read()
+status_lines = []
+for line in text.splitlines():
+    if line.startswith("Enter \"y\"") and ": " in line:
+        head, tail = line.split(": ", 1)
+        if status_lines:
+            print("\n".join(sorted(status_lines)))
+            status_lines = []
+        print(head + ": ")
+        if tail:
+            line = tail
+    if re.search(r"^.{4} [\w./].*$", line):
+        status_lines.append(line)
+    elif status_lines:
+        print("\n".join(sorted(status_lines)))
+        print(line)
+        status_lines = []
+    else:
+        print(line)
+if status_lines:
+    print("\n".join(sorted(status_lines)))
+' <"$INPUT_FILE" >"$OUTPUT_FILE"
+}
+
+function check_svn_version() {
+    if [[ ! $SVN_MINOR_VERSION =~ ^1\.(8|9|10|14)$ ]]; then
+        skip_all "Tests require Subversion 1.8 or later"
+        exit 0
+    fi
+}
+
+function fcm_make_build_hello_tests() {
+    local TEST_KEY=$1
+    local HELLO_EXT=${2:-}
+    shift 2
+    rm -fr \
+        .fcm-make \
+        build \
+        fcm-make-as-parsed.cfg \
+        fcm-make-on-success.cfg \
+        fcm-make.log
+    run_pass "$TEST_KEY" fcm make "$@"
+    file_test "$TEST_KEY.hello$HELLO_EXT" "$PWD/build/bin/hello$HELLO_EXT"
+    "$PWD/build/bin/hello$HELLO_EXT" >"$TEST_KEY.hello$HELLO_EXT.out"
+    file_cmp "$TEST_KEY.hello$HELLO_EXT.out" \
+        "$TEST_KEY.hello$HELLO_EXT.out" <<'__OUT__'
+Hello World!
+__OUT__
+}
+
+FCM_HOME=${FCM_HOME:-$(cd $(dirname $(readlink -f $BASH_SOURCE))/../../.. && pwd)}
+export FCM_HOME
+PATH=$FCM_HOME/bin:$PATH
+
+SVN_MINOR_VERSION=undef
+SVN_VERSION=undef
+if svn --version 1>/dev/null 2>&1; then
+    SVN_MINOR_VERSION=$(svn --version | \
+                        sed -n "s/^svn, version 1.\([0-9]\+\)\.[0-9]\+ .*/1.\1/p")
+    SVN_VERSION=$(svn --version | \
+                        sed -n "s/^svn, version 1.\([0-9\.]\+\) .*/1.\1/p")
+    # Treat version 1.13 like 1.14
+    [[ $SVN_MINOR_VERSION == "1.13" ]] && SVN_MINOR_VERSION=1.14
+fi
+
+TEST_KEY_BASE=$(basename $0 .t)
+TEST_SOURCE_DIR=$(cd $(dirname $0) && pwd)
+TEST_DIR=$(mktemp -d)
+export LC_ALL=C
+export LANG=C
+cd $TEST_DIR
+
+set +e
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/00-pre-revprop-change.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/00-pre-revprop-change.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/00-pre-revprop-change.t	(revision 5095)
@@ -0,0 +1,82 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "pre-revprop-change".
+#-------------------------------------------------------------------------------
+FCM_SVN_HOOK_ADMIN_EMAIL=your.admin.team
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 16
+#-------------------------------------------------------------------------------
+cp -p "$FCM_HOME/etc/svn-hooks/pre-revprop-change" "$REPOS_PATH/hooks/"
+echo Hello >file
+svn import -q -m'test' file "$REPOS_URL/file"
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE
+rm -f mail.out
+run_pass "$TEST_KEY" \
+    svn propset -q --revprop -r 1 'svn:log' 'Add hello file' "$REPOS_URL"
+run_fail "$TEST_KEY.mail.out" test -f mail.out
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-bad-prop
+run_fail "$TEST_KEY" \
+    svn propset -q --revprop -r 1 'svn:author' 'boogeyman' "$REPOS_URL"
+file_grep "$TEST_KEY.err" \
+    "\[M svn:author\] permission denied." \
+    "$TEST_KEY.err"
+EXPR="\[! .....*-..-..T..:..:..Z\] $REPOS_PATH 1 $USER svn:author M"
+file_grep "$TEST_KEY.log" "$EXPR" "$REPOS_PATH/log/pre-revprop-change.log"
+file_grep "$TEST_KEY.mail.out" "$EXPR" mail.out
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-bad-action
+run_fail "$TEST_KEY" \
+    svn propdel -q --revprop -r 1 'svn:log' "$REPOS_URL"
+file_grep "$TEST_KEY.err" \
+    "\[D svn:log\] permission denied. Can only do: \[M svn:log\]" \
+    "$TEST_KEY.err"
+EXPR="\[! .....*-..-..T..:..:..Z\] $REPOS_PATH 1 $USER svn:log D"
+file_grep "$TEST_KEY.log" "$EXPR" "$REPOS_PATH/log/pre-revprop-change.log"
+file_grep "$TEST_KEY.mail.out" "$EXPR" mail.out
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-conf-bad
+cat >"$REPOS_PATH/hooks/pre-revprop-change-ok.conf" <<'__CONF__'
+M svn:author
+M svn:log
+__CONF__
+run_fail "$TEST_KEY" svn propdel -q --revprop -r 1 'svn:author' "$REPOS_URL"
+file_grep "$TEST_KEY.err" \
+    "\[D svn:author\] permission denied. Can only do: \[M svn:author\] \[M svn:log\]" \
+    "$TEST_KEY.err"
+EXPR="\[! .....*-..-..T..:..:..Z\] $REPOS_PATH 1 $USER svn:author D"
+file_grep "$TEST_KEY.log" "$EXPR" "$REPOS_PATH/log/pre-revprop-change.log"
+file_grep "$TEST_KEY.mail.out" "$EXPR" mail.out
+rm -f "$REPOS_PATH/hooks/pre-revprop-change-ok.conf"
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-conf-good
+rm -f mail.out
+cat >"$REPOS_PATH/hooks/pre-revprop-change-ok.conf" <<'__CONF__'
+M svn:author
+M svn:log
+__CONF__
+run_pass "$TEST_KEY" \
+    svn propset -q --revprop -r 1 'svn:author' 'arthur' "$REPOS_URL"
+run_fail "$TEST_KEY.mail.out" test -f mail.out
+rm -f "$REPOS_PATH/hooks/pre-revprop-change-ok.conf"
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/01-post-revprop-change-bg.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/01-post-revprop-change-bg.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/01-post-revprop-change-bg.t	(revision 5095)
@@ -0,0 +1,118 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "post-revprop-change-bg".
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+tests 9
+#-------------------------------------------------------------------------------
+# Add pre-revprop-change to allow revprop change.
+cat >"$REPOS_PATH/hooks/pre-revprop-change" <<__BASH__
+#!/bin/bash
+exit
+__BASH__
+chmod +x "$REPOS_PATH/hooks/pre-revprop-change"
+# Add post-revprop-change
+cp -p "$FCM_HOME/etc/svn-hooks/post-revprop-change" \
+    "$REPOS_PATH/hooks/post-revprop-change"
+echo Hello >file
+svn import --no-auth-cache -q -m'test' file "$REPOS_URL/file"
+if [[ -n ${TRAC_ENV_PATH:-} ]]; then
+    if $TRAC_RESYNC; then
+        trac-admin "$TRAC_ENV_PATH" resync 1>/dev/null
+    else
+        trac-admin "$TRAC_ENV_PATH" changeset added "$REPOS_PATH" 1 1>/dev/null
+    fi
+fi
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE
+run_pass "$TEST_KEY" \
+    svn propset --no-auth-cache -q --revprop -r 1 'svn:log' 'Add hello file' \
+    "$REPOS_URL"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-revprop-change.log"
+date2datefmt "$REPOS_PATH/log/post-revprop-change.log" \
+    | sed '/^trac-admin/,$d; /^RET_CODE=/d' >"$TEST_KEY.log.expected"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log.expected" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ M svn:log @1 by $USER
+--- old-value
++++ new-value
+@@ -1 +1 @@
+-test
+\ No newline at end of file
++Add hello file
+\ No newline at end of file
+__LOG__
+run_fail "$TEST_KEY.mail.out" test -f mail.out
+if [[ -z ${TRAC_ENV_PATH:-} ]]; then
+    skip 1 "$TEST_KEY.trac.db: Trac unavailable"
+else
+    sqlite3 "$TRAC_ENV_PATH/db/trac.db" \
+        'SELECT cast(rev as integer),message FROM revision;' \
+        >"$TEST_KEY.trac.db.expected"
+    file_cmp "$TEST_KEY.trac.db" \
+        "$TEST_KEY.trac.db.expected" <<<'1|Add hello file'
+fi
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-author
+cat /dev/null >"$REPOS_PATH/log/post-revprop-change.log"
+run_pass "$TEST_KEY" \
+    svn propset --no-auth-cache --username=not-a-user --revprop -r 1 'svn:log' \
+    'Add welcome file' "$REPOS_URL"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-revprop-change.log"
+date2datefmt "$REPOS_PATH/log/post-revprop-change.log" \
+    | sed '/^trac-admin/,$d; /^RET_CODE=/d' >"$TEST_KEY.log.expected"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log.expected" <<'__LOG__'
+YYYY-mm-ddTHH:MM:SSZ+ M svn:log @1 by not-a-user
+--- old-value
++++ new-value
+@@ -1 +1 @@
+-Add hello file
+\ No newline at end of file
++Add welcome file
+\ No newline at end of file
+__LOG__
+if [[ -z ${TRAC_ENV_PATH:-} ]]; then
+    skip 1 "$TEST_KEY.trac.db: Trac unavailable"
+else
+    sqlite3 "$TRAC_ENV_PATH/db/trac.db" \
+        'SELECT cast(rev as integer),message FROM revision;' \
+        >"$TEST_KEY.trac.db.expected"
+    file_cmp "$TEST_KEY.trac.db" \
+        "$TEST_KEY.trac.db.expected" <<<'1|Add welcome file'
+fi
+date2datefmt mail.out | sed '/^trac-admin/,$d; /^RET_CODE=/d' \
+    >"$TEST_KEY.mail.out.expected"
+file_grep  "$TEST_KEY.mail.out.01" \
+    '-rnotifications@localhost -sfoo@1 \[M svn:log\] by not-a-user' \
+    "$TEST_KEY.mail.out.expected"
+sed '1d' "$TEST_KEY.mail.out.expected" >"$TEST_KEY.mail.out.expected.02"
+file_cmp  "$TEST_KEY.mail.out.02" "$TEST_KEY.mail.out.expected.02" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ M svn:log @1 by not-a-user
+========================================================================
+--- old-value
++++ new-value
+@@ -1 +1 @@
+-Add hello file
+\ No newline at end of file
++Add welcome file
+\ No newline at end of file
+__LOG__
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/02-pre-commit.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/02-pre-commit.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/02-pre-commit.t	(revision 5095)
@@ -0,0 +1,295 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "pre-commit".
+#-------------------------------------------------------------------------------
+FCM_SVN_HOOK_ADMIN_EMAIL='your.admin.team'
+. $(dirname $0)/test_header
+
+test_tidy() {
+    rm -f \
+        "$REPOS_PATH/hooks/pre-commit-custom" \
+        "$REPOS_PATH/hooks/pre-commit-size-threshold.conf" \
+        "$REPOS_PATH/hooks/commit.conf" \
+        "$REPOS_PATH/hooks/svnperms.conf" \
+        "$REPOS_PATH/log/pre-commit.log" \
+        README \
+        bin/svnperms.py \
+        file1 \
+        file2 \
+        file3 \
+        file4 \
+        mail.out \
+        pre-commit-custom.out \
+        svnperms.py.out
+}
+#-------------------------------------------------------------------------------
+tests 50
+#-------------------------------------------------------------------------------
+cp -p "$FCM_HOME/etc/svn-hooks/pre-commit" "$REPOS_PATH/hooks/"
+sed -i "/set -eu/a\
+echo \$2 >$PWD/txn" "$REPOS_PATH/hooks/pre-commit"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-svnperm-1" # Blocked by svnperms.py
+# Install fake svnperms.py
+test_tidy
+cat >bin/svnperms.py <<__BASH__
+#!/bin/bash
+echo "\$@" >$PWD/svnperms.py.out
+echo "Access denied!" >&2
+false
+__BASH__
+chmod +x "bin/svnperms.py"
+echo '[foo]' >"$REPOS_PATH/hooks/svnperms.conf"
+# Try commit
+touch file1
+run_fail "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file1 "$REPOS_URL/file1"
+TXN=$(<txn)
+# Tests
+file_grep "$TEST_KEY.err" 'Access denied!' "$TEST_KEY.err"
+date2datefmt "$REPOS_PATH/log/pre-commit.log" \
+    >"$TEST_KEY.pre-commit.log.expected"
+file_cmp "$TEST_KEY.pre-commit.log" "$TEST_KEY.pre-commit.log.expected" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file1
+Access denied!
+__LOG__
+file_cmp "$TEST_KEY.svnperms.py.out" svnperms.py.out <<__OUT__
+-r $REPOS_PATH -t $TXN -f $REPOS_PATH/hooks/svnperms.conf
+__OUT__
+date2datefmt mail.out >"$TEST_KEY.mail.out.expected"
+file_cmp  "$TEST_KEY.mail.out" "$TEST_KEY.mail.out.expected" <<__LOG__
+-rnotifications@localhost -s [pre-commit] $REPOS_PATH@$TXN your.admin.team
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file1
+Access denied!
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-svnperm-2" # svnperms.conf is bad symlink
+# Install fake svnperms.py
+test_tidy
+ln -f -s "no-such-file" "$REPOS_PATH/hooks/svnperms.conf"
+# Try commit
+touch file1
+run_fail "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file1 "$REPOS_URL/file1"
+TXN=$(<txn)
+# Tests
+file_grep "$TEST_KEY.err" 'foo: permission configuration file not found.' \
+    "$TEST_KEY.err"
+file_grep "$TEST_KEY.err-2" 'your.admin.team has been notified.' "$TEST_KEY.err"
+date2datefmt "$REPOS_PATH/log/pre-commit.log" \
+    >"$TEST_KEY.pre-commit.log.expected"
+file_cmp "$TEST_KEY.pre-commit.log" "$TEST_KEY.pre-commit.log.expected" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file1
+foo: permission configuration file not found.
+your.admin.team has been notified.
+__LOG__
+run_fail "$TEST_KEY.svnperms.py.out" test -e svnperms.py.out
+date2datefmt mail.out >"$TEST_KEY.mail.out.expected"
+file_cmp "$TEST_KEY.mail.out" "$TEST_KEY.mail.out.expected" <<__LOG__
+-rnotifications@localhost -s [pre-commit] $REPOS_PATH@$TXN your.admin.team
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file1
+foo: permission configuration file not found.
+your.admin.team has been notified.
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-svnperm-3" # Good svnperms.conf
+test_tidy
+cat >bin/svnperms.py <<__BASH__
+#!/bin/bash
+echo "\$@" >$PWD/svnperms.py.out
+__BASH__
+chmod +x bin/svnperms.py
+echo '[foo]' >"$REPOS_PATH/hooks/svnperms.conf"
+# Try commit
+touch file1
+run_pass "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file1 "$REPOS_URL/file1"
+TXN=$(<txn)
+# Tests
+run_fail "$TEST_KEY.pre-commit.log" test -s "$REPOS_PATH/log/pre-commit.log"
+file_cmp "$TEST_KEY.svnperms.py.out" "svnperms.py.out" <<__OUT__
+-r $REPOS_PATH -t $TXN -f $REPOS_PATH/hooks/svnperms.conf
+__OUT__
+run_fail "$TEST_KEY.mail.out" test -e mail.out
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-svnperm-4" # No svnperms.conf
+test_tidy
+# Try commit
+touch file2
+run_pass "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file2 "$REPOS_URL/file2"
+# Tests
+run_fail "$TEST_KEY.pre-commit.log" test -s "$REPOS_PATH/log/pre-commit.log"
+run_fail "$TEST_KEY.svnperms.py.out" test -e svnperms.py.out
+run_fail "$TEST_KEY.mail.out" test -e mail.out
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-size-1" # bigger than default
+test_tidy
+perl -e 'map {print(rand())} 1..2097152' >file3 # a large file
+run_fail "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file3 "$REPOS_URL/file3"
+TXN=$(<txn)
+file_grep "$TEST_KEY.err" "foo@$TXN: changeset size ..MB exceeds 10MB." \
+    "$TEST_KEY.err"
+file_grep "$TEST_KEY.err-2" \
+    'Email your.admin.team if you need to bypass this restriction.' "$TEST_KEY.err"
+date2datefmt "$REPOS_PATH/log/pre-commit.log" \
+    | sed 's/\(size \).*\(MB exceeds\)/\1??\2/' \
+    >"$TEST_KEY.pre-commit.log.expected"
+file_cmp "$TEST_KEY.pre-commit.log" "$TEST_KEY.pre-commit.log.expected" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file3
+foo@$TXN: changeset size ??MB exceeds 10MB.
+Email your.admin.team if you need to bypass this restriction.
+__LOG__
+date2datefmt mail.out | sed 's/\(size \).*\(MB exceeds\)/\1??\2/' \
+     >"$TEST_KEY.mail.out.expected"
+file_cmp "$TEST_KEY.mail.out.expected" "$TEST_KEY.mail.out.expected" <<__OUT__
+-rnotifications@localhost -s [pre-commit] $REPOS_PATH@$TXN your.admin.team
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file3
+foo@$TXN: changeset size ??MB exceeds 10MB.
+Email your.admin.team if you need to bypass this restriction.
+__OUT__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-size-2" # bigger than default, threshold increased
+test_tidy
+echo '40' >"$REPOS_PATH/hooks/pre-commit-size-threshold.conf"
+perl -e 'map {print(rand())} 1..2097152' >file3 # a large file
+run_pass "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file3 "$REPOS_URL/file3"
+TXN="$(<'txn')"
+# Tests
+date2datefmt "$REPOS_PATH/log/pre-commit.log" \
+    | sed 's/\(size \).*\(MB exceeds\)/\1??\2/' \
+    >"$TEST_KEY.pre-commit.log.expected"
+file_cmp "${TEST_KEY}.pre-commit.log" "${TEST_KEY}.pre-commit.log.expected" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ ${TXN} by ${USER}
+A   file3
+foo@${TXN}: changeset size ??MB exceeds 1MB.
+__LOG__
+run_fail "$TEST_KEY.mail.out" test -e 'mail.out'
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-custom-1" # block by custom script
+test_tidy
+cat >"$REPOS_PATH/hooks/pre-commit-custom" <<__BASH__
+#!/bin/bash
+echo "\$@" >$PWD/pre-commit-custom.out
+echo 'I am a blocker.' >&2
+false
+__BASH__
+chmod +x "$REPOS_PATH/hooks/pre-commit-custom"
+touch file4
+run_fail "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file4 "$REPOS_URL/file4"
+TXN=$(<txn)
+# Tests
+file_grep "$TEST_KEY.err" 'I am a blocker.' "$TEST_KEY.err"
+file_cmp "$TEST_KEY-custom.out" pre-commit-custom.out <<__OUT__
+$REPOS_PATH $TXN
+__OUT__
+date2datefmt "$REPOS_PATH/log/pre-commit.log" \
+    | sed 's/\(size \).*\(MB exceeds\)/\1??\2/' \
+     >"$TEST_KEY.pre-commit.log"
+file_cmp "$TEST_KEY.pre-commit.log" "$TEST_KEY.pre-commit.log" <<__OUT__
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file4
+I am a blocker.
+__OUT__
+date2datefmt mail.out | sed 's/\(size \).*\(MB exceeds\)/\1??\2/' \
+     >"$TEST_KEY.mail.out.expected"
+file_cmp "$TEST_KEY.mail.out.expected" "$TEST_KEY.mail.out.expected" <<__OUT__
+-rnotifications@localhost -s [pre-commit] $REPOS_PATH@$TXN your.admin.team
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   file4
+I am a blocker.
+__OUT__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-custom-2" # custom script OK
+test_tidy
+cat >"$REPOS_PATH/hooks/pre-commit-custom" <<__BASH__
+#!/bin/bash
+echo "\$@" >$PWD/pre-commit-custom.out
+__BASH__
+chmod +x "$REPOS_PATH/hooks/pre-commit-custom"
+touch file4
+run_pass "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file4 "$REPOS_URL/file4"
+TXN=$(<txn)
+# Tests
+file_cmp "$TEST_KEY-custom.out" pre-commit-custom.out <<__OUT__
+$REPOS_PATH $TXN
+__OUT__
+run_fail "$TEST_KEY.pre-commit.log" test -s "$REPOS_PATH/log/pre-commit.log"
+run_fail "$TEST_KEY.mail.out" test -e mail.out
+#-------------------------------------------------------------------------------
+# Branch create owner verify, goods
+echo 'Hello World' >README
+svn import -m "hello: new project" README "$REPOS_URL/hello/trunk/README"
+rm README
+for KEY in $USER Share Config Rel; do
+    test_tidy
+    TEST_KEY="$TEST_KEY_BASE-branch-owner-$KEY"
+    echo 'verify-branch-owner' >"$REPOS_PATH/hooks/commit.conf"
+    run_pass "$TEST_KEY" svn cp --parents -m "$TEST_KEY" \
+        "$REPOS_URL/hello/trunk" "$REPOS_URL/hello/branches/dev/$KEY/whatever"
+    run_fail "$TEST_KEY.pre-commit.log" test -s "$REPOS_PATH/log/pre-commit.log"
+done
+#-------------------------------------------------------------------------------
+# Branch create owner verify, bad
+test_tidy
+TEST_KEY="$TEST_KEY_BASE-branch-owner-bad"
+echo 'verify-branch-owner' >"$REPOS_PATH/hooks/commit.conf"
+run_fail "$TEST_KEY" svn cp --parents -m "$TEST_KEY" \
+    "$REPOS_URL/hello/trunk" "$REPOS_URL/hello/branches/dev/nosuchuser/whatever"
+TXN=$(<txn)
+file_grep "$TEST_KEY.err" \
+    '\[INVALID BRANCH OWNER\] A   hello/branches/dev/nosuchuser/whatever/' \
+    "$TEST_KEY.err"
+date2datefmt "$REPOS_PATH/log/pre-commit.log" \
+    >"$TEST_KEY.pre-commit.log.expected"
+file_cmp "$TEST_KEY.pre-commit.log" \
+    "$TEST_KEY.pre-commit.log.expected" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   hello/branches/dev/nosuchuser/
+A   hello/branches/dev/nosuchuser/whatever/
+[INVALID BRANCH OWNER] A   hello/branches/dev/nosuchuser/whatever/
+__LOG__
+date2datefmt mail.out >"$TEST_KEY.mail.out.expected"
+file_cmp  "$TEST_KEY.mail.out" "$TEST_KEY.mail.out.expected" <<__LOG__
+-rnotifications@localhost -s [pre-commit] $REPOS_PATH@$TXN your.admin.team
+YYYY-mm-ddTHH:MM:SSZ+ $TXN by $USER
+A   hello/branches/dev/nosuchuser/
+A   hello/branches/dev/nosuchuser/whatever/
+[INVALID BRANCH OWNER] A   hello/branches/dev/nosuchuser/whatever/
+__LOG__
+#-------------------------------------------------------------------------------
+# Branch create owner no verify, bad
+test_tidy
+TEST_KEY="$TEST_KEY_BASE-branch-owner-no-verify-bad"
+run_pass "$TEST_KEY" svn cp --parents -m "$TEST_KEY" \
+    "$REPOS_URL/hello/trunk" "$REPOS_URL/hello/branches/dev/nosuchuser/whatever"
+run_fail "$TEST_KEY.pre-commit.log" test -s "$REPOS_PATH/log/pre-commit.log"
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/03-post-commit-bg.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/03-post-commit-bg.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/03-post-commit-bg.t	(revision 5095)
@@ -0,0 +1,436 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "post-commit-bg".
+#-------------------------------------------------------------------------------
+FCM_SVN_HOOK_ADMIN_EMAIL=fcm.admin.team
+. $(dirname $0)/test_header
+
+test_tidy() {
+    rm -f \
+        "$REPOS_PATH/hooks/post-commit-bg-custom" \
+        "$REPOS_PATH/hooks/post-commit-background-custom" \
+        "$REPOS_PATH/hooks/commit.conf" \
+        "$REPOS_PATH/log/post-commit.log" \
+        'file1' \
+        'file2' \
+        'file3' \
+        'file4' \
+        'file5' \
+        'svnperms.conf' \
+        'mail.out'
+}
+#-------------------------------------------------------------------------------
+tests 44
+#-------------------------------------------------------------------------------
+cp -p "$FCM_HOME/etc/svn-hooks/post-commit" "$REPOS_PATH/hooks/"
+sed -i "/set -eu/a\
+echo \$2 >$PWD/rev; echo \$3 >$PWD/txn" "$REPOS_PATH/hooks/post-commit"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-basic"
+test_tidy
+touch file1
+svn import --no-auth-cache -q -m"$TEST_KEY" file1 "$REPOS_URL/file1"
+REV=$(<rev)
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+date2datefmt "$REPOS_PATH/log/post-commit.log" \
+    | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+    >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # <2MB
+RET_CODE=0
+__LOG__
+if [[ -n ${TRAC_ENV_PATH:-} ]] && ! $TRAC_RESYNC; then
+    sqlite3 "$TRAC_ENV_PATH/db/trac.db" \
+        'SELECT cast(rev as integer),message FROM revision;' \
+        >"$TEST_KEY.trac.db.expected"
+    file_cmp "$TEST_KEY.trac.db" \
+        "$TEST_KEY.trac.db.expected" <<<"$REV|$TEST_KEY"
+else
+    skip 1 '"trac-admin changeset added" not available'
+fi
+run_pass "$TEST_KEY.dump" test -s "$PWD/svn-dumps/foo-$REV.gz"
+run_fail "$TEST_KEY.mail.out" test -e mail.out
+#-------------------------------------------------------------------------------
+# Install and remove commit.conf, svnperms.conf
+for NAME in 'commit.conf' 'svnperms.conf'; do
+    TEST_KEY="$TEST_KEY_BASE-no-add-${NAME}"
+    test_tidy
+    # (Use "svnperms.conf" syntax. Doesn't matter for the purpose of this test.)
+    cat >"${NAME}" <<'__CONF__'
+[foo]
+.*=*(add,remove,update)
+__CONF__
+    mkdir -p "svn-hooks/foo"
+    cat >"svn-hooks/foo/${NAME}" <<'__CONF__'
+# This is the site override
+[foo]
+.*=*(add,remove,update)
+__CONF__
+    cp -p "svn-hooks/foo/${NAME}" "$REPOS_PATH/hooks/${NAME}"
+    svn import --no-auth-cache -q -m"${TEST_KEY}" "${NAME}" \
+        "${REPOS_URL}/${NAME}"
+    REV="$(<'rev')"
+    poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+    date2datefmt "$REPOS_PATH/log/post-commit.log" \
+        | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+        >"$TEST_KEY.log"
+    file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # <2MB
+RET_CODE=0
+__LOG__
+    file_cmp "${TEST_KEY}.conf" \
+        "svn-hooks/foo/${NAME}" "$REPOS_PATH/hooks/${NAME}"
+    rm -fr 'svn-hooks'
+
+    test_tidy
+    svn delete --no-auth-cache -q -m"Delete ${TEST_KEY}" "${REPOS_URL}/${NAME}"
+
+    TEST_KEY="$TEST_KEY_BASE-add-${NAME}"
+    test_tidy
+    # (Use "svnperms.conf" syntax. Doesn't matter for the purpose of this test.)
+    cat >${NAME} <<'__CONF__'
+[foo]
+.*=*(add,remove,update)
+__CONF__
+    svn import --no-auth-cache -q -m"$TEST_KEY" ${NAME} \
+        "$REPOS_URL/${NAME}"
+    REV=$(<rev)
+    poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+    date2datefmt "$REPOS_PATH/log/post-commit.log" \
+        | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+        >"$TEST_KEY.log"
+    file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # <2MB
+svnlook cat $REPOS_PATH ${NAME} >$REPOS_PATH/hooks/${NAME}
+RET_CODE=0
+__LOG__
+    file_cmp "$TEST_KEY.conf" ${NAME} "$REPOS_PATH/hooks/${NAME}"
+
+    TEST_KEY="$TEST_KEY_BASE-modify-${NAME}"
+    test_tidy
+    svn co -q "$REPOS_URL" work
+    # (Use "svnperms.conf" syntax. Doesn't matter for the purpose of this test.)
+    cat >work/${NAME} <<'__CONF__'
+[foo]
+.*=*(add,remove,update)
+
+[bar]
+__CONF__
+    svn commit --no-auth-cache -q -m"$TEST_KEY" work
+    REV=$(<rev)
+    poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+    date2datefmt "$REPOS_PATH/log/post-commit.log" \
+        | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+        >"$TEST_KEY.log"
+    file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # <2MB
+svnlook cat $REPOS_PATH ${NAME} >$REPOS_PATH/hooks/${NAME}
+RET_CODE=0
+__LOG__
+    file_cmp "$TEST_KEY.conf" work/${NAME} "$REPOS_PATH/hooks/${NAME}"
+    rm -f -r work
+
+    TEST_KEY="$TEST_KEY_BASE-remove-${NAME}"
+    test_tidy
+    touch "$REPOS_PATH/hooks/${NAME}"
+    svn rm --no-auth-cache -q -m'remove ${NAME}' "$REPOS_URL/${NAME}"
+    REV=$(<rev)
+    poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+    date2datefmt "$REPOS_PATH/log/post-commit.log" \
+        | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+        >"$TEST_KEY.log"
+    file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # <2MB
+rm -f $REPOS_PATH/hooks/${NAME}
+RET_CODE=0
+__LOG__
+    run_fail "$TEST_KEY.conf" test -e "$REPOS_PATH/hooks/${NAME}"
+done
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-size"
+test_tidy
+perl -e 'map {print(rand())} 1..524288' >file2 # compress should be >2MB
+svn import --no-auth-cache -q -m"$TEST_KEY" file2 "$REPOS_URL/file2"
+REV=$(<rev)
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+date2datefmt "$REPOS_PATH/log/post-commit.log" \
+    | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+    >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # >2MB
+RET_CODE=1
+__LOG__
+date2datefmt mail.out \
+    | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+    >"$TEST_KEY.mail.out"
+file_cmp "$TEST_KEY.mail.out" "$TEST_KEY.mail.out" <<__LOG__
+-rnotifications@localhost -s [post-commit-bg] $REPOS_PATH@$REV fcm.admin.team
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # >2MB
+RET_CODE=1
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-size-2"
+test_tidy
+perl -e 'map {print(rand())} 1..2097152' >'file3' # compress should be >10MB
+svn import --no-auth-cache -q -m"${TEST_KEY}" 'file3' "${REPOS_URL}/file3"
+REV="$(<'rev')"
+poll 10 grep -q '^RET_CODE=' "${REPOS_PATH}/log/post-commit.log"
+poll 10 test -e 'mail.out'
+date2datefmt "${REPOS_PATH}/log/post-commit.log" \
+    | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+    >"${TEST_KEY}.log"
+file_cmp "${TEST_KEY}.log" "${TEST_KEY}.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ ${REV} by ${USER}
+svnadmin dump -r${REV} --incremental --deltas ${REPOS_PATH} | gzip \\
+    | (dd 'conv=fsync' "of=${PWD}/svn-dumps/foo-${REV}-tmp.gz" 2>/dev/null)
+* Dumped revision ${REV}.
+mv "${PWD}/svn-dumps/foo-${REV}-tmp.gz" "${PWD}/svn-dumps/foo-${REV}.gz"
+REV_FILE_SIZE=??? # >2MB
+RET_CODE=1
+__LOG__
+date2datefmt mail.out \
+    | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+    >"${TEST_KEY}.mail.out"
+file_cmp "${TEST_KEY}.mail.out" "${TEST_KEY}.mail.out" <<__LOG__
+-rnotifications@localhost -s [post-commit-bg] ${REPOS_PATH}@${REV} fcm.admin.team
+YYYY-mm-ddTHH:MM:SSZ+ ${REV} by ${USER}
+svnadmin dump -r${REV} --incremental --deltas ${REPOS_PATH} | gzip \\
+    | (dd 'conv=fsync' "of=${PWD}/svn-dumps/foo-${REV}-tmp.gz" 2>/dev/null)
+* Dumped revision ${REV}.
+mv "${PWD}/svn-dumps/foo-${REV}-tmp.gz" "${PWD}/svn-dumps/foo-${REV}.gz"
+REV_FILE_SIZE=??? # >2MB
+RET_CODE=1
+__LOG__
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-custom-1" # good custom
+test_tidy
+touch file4
+cat >"$REPOS_PATH/hooks/post-commit-bg-custom" <<'__BASH__'
+#!/bin/bash
+echo "$@"
+__BASH__
+chmod +x "$REPOS_PATH/hooks/post-commit-bg-custom"
+svn import --no-auth-cache -q -m"$TEST_KEY" file4 "$REPOS_URL/file4"
+REV=$(<rev)
+TXN=$(<txn)
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+date2datefmt "$REPOS_PATH/log/post-commit.log" \
+    | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+    >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # <2MB
+$REPOS_PATH/hooks/post-commit-bg-custom $REPOS_PATH $REV $TXN
+$REPOS_PATH $REV $TXN
+RET_CODE=0
+__LOG__
+run_fail "$TEST_KEY.mail.out" test -e mail.out
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-custom-2" # bad custom
+test_tidy
+cat >"$REPOS_PATH/hooks/post-commit-background-custom" <<'__BASH__'
+#!/bin/bash
+echo 'I have gone to the dark side.' >&2
+false
+__BASH__
+chmod +x "$REPOS_PATH/hooks/post-commit-background-custom"
+touch file5
+svn import --no-auth-cache -q -m"$TEST_KEY" file5 "$REPOS_URL/file5"
+REV=$(<rev)
+TXN=$(<txn)
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+date2datefmt "$REPOS_PATH/log/post-commit.log" \
+    | sed '/^trac-admin/d; s/^\(REV_FILE_SIZE=\).*\( #\)/\1???\2/' \
+    >"$TEST_KEY.log"
+file_cmp "$TEST_KEY.log" "$TEST_KEY.log" <<__LOG__
+YYYY-mm-ddTHH:MM:SSZ+ $REV by $USER
+svnadmin dump -r$REV --incremental --deltas $REPOS_PATH | gzip \\
+    | (dd 'conv=fsync' "of=$PWD/svn-dumps/foo-$REV-tmp.gz" 2>/dev/null)
+* Dumped revision $REV.
+mv "$PWD/svn-dumps/foo-$REV-tmp.gz" "$PWD/svn-dumps/foo-$REV.gz"
+REV_FILE_SIZE=??? # <2MB
+$REPOS_PATH/hooks/post-commit-background-custom $REPOS_PATH $REV $TXN
+I have gone to the dark side.
+RET_CODE=1
+__LOG__
+file_test "$TEST_KEY.mail.out" mail.out
+#-------------------------------------------------------------------------------
+# Test branch owner notification
+echo 'Hello World' >file
+svn import -q -m'hello world' file "$REPOS_URL/hello/trunk/file"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+
+TEST_KEY="$TEST_KEY_BASE-branch-create-owner-1" # create author is owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+svn cp -q -m '' --parents \
+    "$REPOS_URL/hello/trunk" \
+    "$REPOS_URL/hello/branches/dev/$USER/whatever"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+run_fail "$TEST_KEY.mail.out" test -s mail.out
+
+TEST_KEY="$TEST_KEY_BASE-branch-create-owner-2" # create author not owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+svn cp -q -m '' --parents \
+    --username=root \
+    --no-auth-cache \
+    "$REPOS_URL/hello/trunk" \
+    "$REPOS_URL/hello/branches/test/$USER/whatever"
+REV=$(<rev)
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+file_grep "$TEST_KEY.mail.out.1" \
+    "^-rnotifications@localhost -sfoo@${REV} by root" mail.out
+file_grep "$TEST_KEY.mail.out.2" "^r${REV} | root" mail.out
+
+TEST_KEY="$TEST_KEY_BASE-branch-create-owner-3" # same as 2, but no notify
+test_tidy
+svn cp -q -m '' --parents \
+    --username=root \
+    --no-auth-cache \
+    "$REPOS_URL/hello/trunk" \
+    "$REPOS_URL/hello/branches/test/$USER/whatever2"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+run_fail "$TEST_KEY.mail.out" test -s mail.out
+
+TEST_KEY="$TEST_KEY_BASE-branch-modify-owner-1" # modify author is owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+svn co -q "$REPOS_URL/hello/branches/dev/$USER/whatever" hello
+echo 'Hello Earth' >hello/file
+svn ci -q -m'Hello Earth' hello/file
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+run_fail "$TEST_KEY.mail.out" test -s mail.out
+
+TEST_KEY="$TEST_KEY_BASE-branch-modify-owner-2" # modify author not owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+#svn co -q "$REPOS_URL/hello/branches/dev/$USER/whatever" hello
+echo 'Hello Alien' >hello/file
+svn ci -q -m'Hello Earth' --username=root --no-auth-cache hello/file
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+REV=$(<rev)
+file_grep "$TEST_KEY.mail.out.1" \
+    "^-rnotifications@localhost -sfoo@${REV} by root" mail.out
+file_grep "$TEST_KEY.mail.out.2" "^r${REV} | root" mail.out
+
+TEST_KEY="$TEST_KEY_BASE-share-branch-owner-1" # modify share author is owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+svn cp -q -m '' --parents \
+    "$REPOS_URL/hello/trunk" \
+    "$REPOS_URL/hello/branches/dev/Share/whatever"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+run_fail "$TEST_KEY.create.mail.out" test -s mail.out
+test_tidy
+echo 'Greet Alien' >'greet.txt'
+svn import -q -m '' 'greet.txt' \
+    "$REPOS_URL/hello/branches/dev/Share/whatever/greet.txt"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+run_fail "$TEST_KEY.modify.mail.out" test -s mail.out
+
+TEST_KEY="$TEST_KEY_BASE-share-branch-owner-2" # modify share author not owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+echo 'Hail Alien' >'hail.txt'
+svn import -q -m '' --username=root --no-auth-cache 'hail.txt' \
+    "$REPOS_URL/hello/branches/dev/Share/whatever/hail.txt"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+REV=$(<rev)
+file_grep "$TEST_KEY.mail.out.1" \
+    "^-rnotifications@localhost -sfoo@${REV} by root" 'mail.out'
+file_grep "$TEST_KEY.mail.out.2" "^r${REV} | root" 'mail.out'
+
+TEST_KEY="$TEST_KEY_BASE-branch-delete-owner-1" # delete author is owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+svn rm -q -m'No Hello' "$REPOS_URL/hello/branches/dev/$USER/whatever"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+run_fail "$TEST_KEY.mail.out" test -s mail.out
+
+TEST_KEY="$TEST_KEY_BASE-branch-delete-owner-2" # delete author not owner
+test_tidy
+echo 'notify-owner' >"${REPOS_PATH}/hooks/commit.conf"
+svn rm -q -m'No Hello' --username=root --no-auth-cache \
+    "$REPOS_URL/hello/branches/test/$USER/whatever"
+poll 10 grep -q '^RET_CODE=' "$REPOS_PATH/log/post-commit.log"
+REV=$(<rev)
+file_grep "$TEST_KEY.mail.out.1" \
+    "^-rnotifications@localhost -sfoo@${REV} by root" mail.out
+file_grep "$TEST_KEY.mail.out.2" "^r${REV} | root" mail.out
+#-------------------------------------------------------------------------------
+# Test owner notification
+TEST_KEY="${TEST_KEY_BASE}-owner-1" # trunk author is not owner
+test_tidy
+cat >"${REPOS_PATH}/hooks/commit.conf" <<__CONF__
+notify-owner
+owner = $USER
+__CONF__
+rm -fr 'hello'
+svn co -q "${REPOS_URL}/hello/trunk" 'hello'
+echo 'Hello' >'hello/file'
+svn ci -q -m 'hello whatever' '--no-auth-cache' '--username=root' 'hello'
+poll 10 grep -q '^RET_CODE=' "${REPOS_PATH}/log/post-commit.log"
+poll 10 test -e 'mail.out'
+REV="$(<rev)"
+file_grep "${TEST_KEY}.mail.out.1" \
+    "^-rnotifications@localhost -sfoo@${REV} by root" 'mail.out'
+file_grep "${TEST_KEY}.mail.out.2" "^r${REV} | root" 'mail.out'
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/04-svnperms.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/04-svnperms.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/04-svnperms.t	(revision 5095)
@@ -0,0 +1,120 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Basic tests for "pre-commit".
+#-------------------------------------------------------------------------------
+FCM_SVN_HOOK_ADMIN_EMAIL='your.admin.team'
+. $(dirname $0)/test_header
+
+svn mkdir --parents $REPOS_URL/foo/trunk -m "create foo trunk"
+svn mkdir --parents $REPOS_URL/foo/branches -m "create foo branches"
+svn mkdir --parents $REPOS_URL/bar/trunk -m "create bar trunk"
+svn mkdir --parents $REPOS_URL/bar/branches -m "create bar branches"
+
+test_tidy() {
+    rm -f \
+        "$REPOS_PATH/hooks/pre-commit-custom" \
+        "$REPOS_PATH/hooks/pre-commit-size-threshold.conf" \
+        "$REPOS_PATH/hooks/commit.conf" \
+        "$REPOS_PATH/hooks/svnperms.conf" \
+        "$REPOS_PATH/log/pre-commit.log" \
+        README \
+        bin/svnperms.py \
+        file1 \
+        file2 \
+        file3 \
+        file4 \
+        mail.out \
+        pre-commit-custom.out \
+        svnperms.py.out
+}
+#-------------------------------------------------------------------------------
+tests 9
+#-------------------------------------------------------------------------------
+cp -p "$FCM_HOME/etc/svn-hooks/pre-commit" "$REPOS_PATH/hooks/"
+sed -i "/set -eu/a\
+echo \$2 >$PWD/txn" "$REPOS_PATH/hooks/pre-commit"
+#-------------------------------------------------------------------------------
+cp "$FCM_HOME/sbin/svnperms.py" "$REPOS_PATH/hooks/"
+REPOS_SHORTNAME=$(basename $REPOS_PATH)
+cat >"$REPOS_PATH/hooks/svnperms.conf" <<__CONF__
+[$REPOS_SHORTNAME groups]
+admin = barry bazzy quxxy xyzzy wibbley
+users = wibbley wobbley wubbley $LOGNAME
+
+[$REPOS_SHORTNAME]
+.* = @admin(add,remove,update)
+
+foo/trunk/.* = @admin(add,remove,update)
+foo/branches/[^/]+/.* = *(add,remove,update)
+foo/tags/[^/]+/.* = @admin(add,remove,update)
+
+bar/trunk/.* = @users(add,remove,update)
+bar/branches/[^/]+/.* = @users(add,remove,update)
+bar/tags/[^/]+/.* = @admin(add,remove,update)
+
+[something_else_svn groups]
+admin = $LOGNAME whoever
+somepeople = bla blubb blabla
+
+[something_else_svn]
+foo/trunk/.* = @admin(add,remove,update)
+foo/branches/[^/]+/.* = @somepeople(add,remove,update)
+foo/tags/[^/]+/.* = @admin(add,remove,update)
+__CONF__
+touch file1
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-trunk-fail"
+run_fail "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file1 "$REPOS_URL/foo/trunk/file1"
+file_grep "$TEST_KEY.foo-trunk.err" \
+    "error: you don't have enough permissions for this transaction:" \
+    "$TEST_KEY.err"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-trunk-fail-custom-err-message"
+echo "
+[message]
+permerrors_prefix = Bad User!" >>"$REPOS_PATH/hooks/svnperms.conf"
+run_fail "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file1 "$REPOS_URL/foo/trunk/file1"
+file_grep "$TEST_KEY.foo-trunk.err" "Bad User!" "$TEST_KEY.err"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-trunk-fail-custom-err-message-repos"
+echo "
+[$REPOS_SHORTNAME message]
+permerrors_prefix = FCM police notified" >>"$REPOS_PATH/hooks/svnperms.conf"
+run_fail "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file1 "$REPOS_URL/foo/trunk/file1"
+file_grep "$TEST_KEY.foo-trunk.err" "FCM police notified" "$TEST_KEY.err"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-branch-pass-all"
+run_pass "$TEST_KEY" \
+    svn mkdir --parents --no-auth-cache -q -m 'test' \
+    "$REPOS_URL/foo/branches/dev/$LOGNAME/"$(date +%s.%N)
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-trunk-pass"
+run_pass "$TEST_KEY" \
+    svn import --no-auth-cache -q -m'test' file1 "$REPOS_URL/bar/trunk/file1"
+#-------------------------------------------------------------------------------
+TEST_KEY="$TEST_KEY_BASE-branch-pass-group"
+run_pass "$TEST_KEY" \
+    svn mkdir --parents --no-auth-cache -q -m 'test' \
+    "$REPOS_URL/bar/branches/dev/$LOGNAME/"$(date +%s.%N)
+#-------------------------------------------------------------------------------
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-hooks/test_header	(revision 5095)
@@ -0,0 +1,143 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# NAME
+#     test_header
+#
+# SYNOPSIS
+#     . $(dirname $0)/test_header
+#
+# DESCRIPTION
+#     Provide more bash shell functions for testing svn-hooks. Create a
+#     subversion repository. Create a Trac environment if possible. Set up
+#     repository hooks environment file.
+#
+# FUNCTIONS
+#     date2datefmt FILE
+#         Convert date time in YYYY-mm-ddTHH:MM:SSZ format into the format
+#         string itself.
+#     poll HOW_LONG COND ...
+#         Poll for COND to become true for HOW_LONG seconds.
+#
+# VARIABLES
+#     FCM_SVN_HOOK_REPOS_SUFFIX
+#     FCM_SVN_HOOK_ADMIN_EMAIL
+#         Optional. See definition in hook scripts.
+#     HOOK_PATH
+#         PATH for the hook scripts.
+#     REPOS_PATH
+#         Path to the Subversion repository ($PWD/svn-repos/foo).
+#     REPOS_URL
+#         URL of the Subversion repository root (file://$REPOS_PATH).
+#     TRAC_ENV_PATH
+#         Path to the Trac environment ($PWD/trac-env/foo).
+#     TRAC_RESYNC
+#         If true, use "trac-admin resync". If false, use "trac-admin
+#         changeset added|modified".
+#-------------------------------------------------------------------------------
+
+. $(dirname $0)/../lib/bash/test_header
+
+function file_cmp() {
+    local TEST_KEY=$1
+    local FILE_ACTUAL=$2
+    local FILE_EXPECT=${3:--}
+    if diff -u $FILE_EXPECT $FILE_ACTUAL >&2; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
+
+function file_grep() {
+    local TEST_KEY=$1
+    local PATTERN=$2
+    local FILE=$3
+    if grep -q -e "$PATTERN" $FILE; then
+        pass $TEST_KEY
+        return
+    fi
+    fail $TEST_KEY
+}
+
+date2datefmt() {
+    perl -p -e 's/\d+-\d\d-\d\dT\d\d:\d\d:\d\dZ/YYYY-mm-ddTHH:MM:SSZ/' "$@"
+}
+
+poll() {
+    local HOW_LONG=$1
+    shift 1
+    local WAIT_UNTIL=$(($(date +%s) + $HOW_LONG))
+    while (($(date +%s) < $WAIT_UNTIL)) && ! "$@" 2>/dev/null; do
+        sleep 1
+    done
+    "$@"
+}
+
+HOOK_PATH='/usr/local/bin:/usr/bin:/bin'
+mkdir bin svn-dumps svn-repos trac-env
+svnadmin create svn-repos/foo 2>/dev/null \
+    || skip_all 'cannot create Subversion repository'
+REPOS_PATH="$PWD/svn-repos/foo${FCM_SVN_HOOK_REPOS_SUFFIX:-}"
+REPOS_URL="file://$REPOS_PATH"
+ADMIN_DIR=$(dirname "$(which svnadmin)")
+if ! grep -q '^\(/usr/local/bin\|/usr/bin\|/bin\)$' <<<"$ADMIN_DIR"; then
+    HOOK_PATH="$ADMIN_DIR:$HOOK_PATH"
+fi
+if trac-admin trac-env/foo initenv foo sqlite:db/trac.db svn "$REPOS_PATH" \
+    1>/dev/null 2>&1
+then
+    FCM_SVN_HOOK_TRAC_ROOT_DIR="$PWD/trac-env"
+    TRAC_ENV_PATH="$FCM_SVN_HOOK_TRAC_ROOT_DIR/foo"
+    ADMIN_DIR=$(dirname "$(which trac-admin)")
+    if ! grep -q '^\(/usr/local/bin\|/usr/bin\|/bin\)$' <<<"$ADMIN_DIR"; then
+        HOOK_PATH="$ADMIN_DIR:$HOOK_PATH"
+    fi
+    if [[ $(trac-admin --version) == trac-admin\ 0.11* ]]; then
+        TRAC_RESYNC=true
+    else
+        TRAC_RESYNC=false
+    fi
+    cat >>"${TRAC_ENV_PATH}/conf/trac.ini" <<'__TRAC_INI___'
+
+[components]
+tracopt.versioncontrol.svn.* = enabled
+__TRAC_INI___
+fi
+unset ADMIN_DIR
+cat >bin/mail <<__BASH__
+#!/bin/bash
+{
+    echo "\$@"
+    cat
+} >$PWD/mail.out
+__BASH__
+chmod +x bin/mail
+HOOK_PATH="$PWD/bin:$HOOK_PATH"
+cat >"$REPOS_PATH/conf/hooks-env" <<__CONF__
+[default]
+FCM_HOME=$FCM_HOME
+FCM_SITE_HOME=$PWD
+FCM_SVN_HOOK_ADMIN_EMAIL=${FCM_SVN_HOOK_ADMIN_EMAIL:-}
+FCM_SVN_HOOK_COMMIT_DUMP_DIR=$PWD/svn-dumps
+FCM_SVN_HOOK_NOTIFICATION_FROM=notifications@localhost
+FCM_SVN_HOOK_REPOS_SUFFIX=${FCM_SVN_HOOK_REPOS_SUFFIX:-}
+FCM_SVN_HOOK_TRAC_ROOT_DIR=${FCM_SVN_HOOK_TRAC_ROOT_DIR:-}
+PATH=$HOOK_PATH
+__CONF__
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-username/00-branch.t
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-username/00-branch.t	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-username/00-branch.t	(revision 5095)
@@ -0,0 +1,103 @@
+#!/bin/bash
+# ------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+# ------------------------------------------------------------------------------
+# Test "fcm branch-create" and "fcm branch-list", alternate username.
+#-------------------------------------------------------------------------------
+. $(dirname $0)/test_header
+#-------------------------------------------------------------------------------
+setup
+init_repos
+cd $TEST_DIR
+svn cp -m 't1' --parents -q \
+    $ROOT_URL/trunk@1 $ROOT_URL/branches/dev/barn.owl/r1_wing
+if ! svnserve -r $TEST_DIR -d --pid-file pid-file; then
+    if [[ -s pid-file ]]; then
+        kill $(cat pid-file)
+    fi
+    skip_all 'svnserve failed'
+    teardown
+    exit
+fi
+tests 9
+#-------------------------------------------------------------------------------
+# Tests fcm branch-create, alternate username
+TEST_KEY="$TEST_KEY_BASE"
+FCM_SUBVERSION_SERVERS_CONF="$PWD/$TEST_KEY-svn-servers-conf"
+cat >$FCM_SUBVERSION_SERVERS_CONF <<'__CONF__'
+[groups]
+bar=localhost
+
+[bar]
+username=barn.owl
+__CONF__
+SVN_EDITOR=true \
+FCM_SUBVERSION_SERVERS_CONF=$FCM_SUBVERSION_SERVERS_CONF run_pass "$TEST_KEY" \
+    fcm branch-create hello svn://localhost/test_repos <<<'n'
+echo >>"$TEST_KEY.out" # Insert newline
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<__OUT__
+[info] Source: svn://localhost/test_repos/trunk@1 (4)
+[info] true: starting commit message editor...
+Change summary:
+--------------------------------------------------------------------------------
+A    svn://localhost/test_repos/branches/dev/barn.owl/r1_hello
+--------------------------------------------------------------------------------
+Commit message is as follows:
+--------------------------------------------------------------------------------
+Created /branches/dev/barn.owl/r1_hello from /trunk@1.
+--------------------------------------------------------------------------------
+Create the branch?
+Enter "y" or "n" (or just press <return> for "n") 
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-1
+FCM_SUBVERSION_SERVERS_CONF="$PWD/$TEST_KEY-svn-servers-conf"
+cat >$FCM_SUBVERSION_SERVERS_CONF <<'__CONF__'
+[groups]
+bar=localhost
+
+[bar]
+username=barn.owl
+__CONF__
+FCM_SUBVERSION_SERVERS_CONF=$FCM_SUBVERSION_SERVERS_CONF run_pass "$TEST_KEY" \
+    fcm branch-list svn://localhost/test_repos
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<'__OUT__'
+[info] svn://localhost/test_repos@4: 1 match(es)
+svn://localhost/test_repos/branches/dev/barn.owl/r1_wing@4
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+TEST_KEY=$TEST_KEY_BASE-0
+cat >$FCM_SUBVERSION_SERVERS_CONF <<'__CONF__'
+[groups]
+bar=localhost
+
+[bar]
+username=honey.bee
+__CONF__
+FCM_SUBVERSION_SERVERS_CONF=$FCM_SUBVERSION_SERVERS_CONF run_pass "$TEST_KEY" \
+    fcm branch-list svn://localhost/test_repos
+file_cmp "$TEST_KEY.out" "$TEST_KEY.out" <<'__OUT__'
+[info] svn://localhost/test_repos@4: 0 match(es)
+__OUT__
+file_cmp "$TEST_KEY.err" "$TEST_KEY.err" </dev/null
+#-------------------------------------------------------------------------------
+kill $(cat pid-file)
+teardown
+exit
Index: LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-username/test_header
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-username/test_header	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/t/svn-username/test_header	(revision 5095)
@@ -0,0 +1,1 @@
+link ../lib/bash/svn_test_header
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_results_fcm1
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_results_fcm1	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_results_fcm1	(revision 5095)
@@ -0,0 +1,217 @@
+#!/bin/ksh
+
+TEST_NAME=$1
+BASE_DIR=$PWD
+DIFF=${DIFF:-diff}
+
+if [[ ! -f control/$TEST_NAME/.tests.complete ]]; then
+  echo "WARNING: Control tests did not complete, skipping comparisons"
+  exit
+fi
+
+DIFF_OUTPUT=$BASE_DIR/$TEST_NAME.diff
+
+# Compare directory contents
+for DIR in done .cache/.bld etc flags src
+do
+  if [[ -d test/$TEST_NAME/$DIR ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $DIR directory contents ..."
+    fi
+    $DIFF -r control/$TEST_NAME/$DIR test/$TEST_NAME/$DIR >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $DIR directory contents differ from control"
+      exit 1
+    fi
+  fi
+done
+
+# Compare file listings from directories
+for DIR in .cache/.ext bin obj lib inc ppsrc
+do
+  if [[ -d test/$TEST_NAME/$DIR ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $DIR directory listings ..."
+    fi
+    cd $BASE_DIR/control/$TEST_NAME
+    ls -R -1 $DIR >$BASE_DIR/$TEST_NAME.control_files
+    cd $BASE_DIR/test/$TEST_NAME
+    ls -R -1 $DIR >$BASE_DIR/$TEST_NAME.test_files
+    cd $BASE_DIR
+    $DIFF $TEST_NAME.control_files $TEST_NAME.test_files >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $DIR file listing differs from control"
+      exit 1
+    fi
+    rm $TEST_NAME.control_files $TEST_NAME.test_files
+  fi
+done
+
+# Compare file listings from directories (non-recursive)
+for DIR in .
+do
+  if [[ -d test/$TEST_NAME/$DIR ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $DIR directory listings ..."
+    fi
+    cd $BASE_DIR/control/$TEST_NAME
+    ls -1 $DIR >$BASE_DIR/$TEST_NAME.control_files
+    cd $BASE_DIR/test/$TEST_NAME
+    ls -1 $DIR >$BASE_DIR/$TEST_NAME.test_files
+    cd $BASE_DIR/
+    $DIFF $TEST_NAME.control_files $TEST_NAME.test_files >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $DIR file listing differs from control"
+      exit 1
+    fi
+    rm $TEST_NAME.control_files $TEST_NAME.test_files
+  fi
+done
+
+# Compare files in inc directory (except *.mod)
+if [[ -d test/$TEST_NAME/inc ]]; then
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing inc directory contents ..."
+  fi
+  cd test/$TEST_NAME/inc
+  for FILE in $(ls -1 | grep -v "\.mod$")
+  do
+    $DIFF $BASE_DIR/control/$TEST_NAME/inc/$FILE $FILE >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+      exit 1
+    fi
+  done
+fi
+cd $BASE_DIR
+
+# Compare files in ppsrc directory (ignoring RUN_DIR in *.c files)
+if [[ -d test/$TEST_NAME/ppsrc ]]; then
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing ppsrc directory contents ..."
+  fi
+  cd test/$TEST_NAME
+  FILES=$(find ppsrc -type f)
+  cd $BASE_DIR
+  for FILE in $FILES
+  do
+    if [[ $FILE == ${FILE%.c} ]]; then
+      $DIFF control/$TEST_NAME/$FILE test/$TEST_NAME/$FILE >$DIFF_OUTPUT
+    else
+      cat control/$TEST_NAME/$FILE | sed "s#/.*/fcm_test_suite/control/##g" >$TEST_NAME.control_file
+      cat test/$TEST_NAME/$FILE | sed "s#/.*/fcm_test_suite/test/##g" >$TEST_NAME.test_file
+      $DIFF $TEST_NAME.control_file $TEST_NAME.test_file >$DIFF_OUTPUT
+    fi
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+      exit 1
+    fi
+    rm -f $TEST_NAME.control_file $TEST_NAME.test_file
+  done
+fi
+
+# Compare build command files
+cd test
+unset TEST_FILES CONTROL_FILES
+if [[ -f $TEST_NAME.build.commands.1 ]]; then
+  TEST_FILES=$(ls -1 $TEST_NAME.build.commands.*)
+fi
+cd $BASE_DIR/control
+if [[ -f $TEST_NAME.build.commands.1 ]]; then
+  CONTROL_FILES=$(ls -1 $TEST_NAME.build.commands.*)
+fi
+if [[ $TEST_FILES != $CONTROL_FILES ]]; then
+  echo "FAILED: $TEST_NAME - number of build command files differs from control"
+  exit 1
+fi
+cd $BASE_DIR
+for FILE in $TEST_FILES
+do
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing $FILE contents ..."
+  fi
+  if [[ $NPROC = 1 ]]; then
+    $DIFF control/$FILE test/$FILE >$DIFF_OUTPUT
+  else
+    cat control/$FILE | grep -v wrap_ar | sort >$TEST_NAME.control_file
+    cat test/$FILE | grep -v wrap_ar | sort >$TEST_NAME.test_file
+    $DIFF $TEST_NAME.control_file $TEST_NAME.test_file >$DIFF_OUTPUT
+  fi
+  if [[ $? != 0 ]]; then
+    echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+    exit 1
+  fi
+  rm -f $TEST_NAME.control_file $TEST_NAME.test_file
+done
+
+# Compare run output files
+cd test
+unset TEST_FILES CONTROL_FILES
+if [[ -f $TEST_NAME.exe.stdout.1 ]]; then
+  TEST_FILES="$TEST_FILES $(ls -1 $TEST_NAME.exe.stdout.*)"
+fi
+cd $BASE_DIR/control
+if [[ -f $TEST_NAME.exe.stdout.1 ]]; then
+  CONTROL_FILES="$CONTROL_FILES $(ls -1 $TEST_NAME.exe.stdout.*)"
+fi
+if [[ $TEST_FILES != $CONTROL_FILES ]]; then
+  echo "FAILED: $TEST_NAME - number of run output files differs from control"
+  exit 1
+fi
+cd $BASE_DIR
+for FILE in $TEST_FILES
+do
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing $FILE contents ..."
+  fi
+  $DIFF control/$FILE test/$FILE >$DIFF_OUTPUT
+  if [[ $? != 0 ]]; then
+    echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+    exit 1
+  fi
+done
+
+# Compare file contents ignoring RUN_DIR
+for FILE in Makefile fcm_env.sh cfg/bld.cfg cfg/parsed_bld.cfg cfg/ext.cfg cfg/parsed_ext.cfg
+do
+  if [[ -f test/$TEST_NAME/$FILE ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $FILE contents ..."
+    fi
+    cat control/$TEST_NAME/$FILE | sed "s#/.*/fcm_test_suite/control/##g" >$TEST_NAME.control_file
+    cat test/$TEST_NAME/$FILE | sed "s#/.*/fcm_test_suite/test/##g" >$TEST_NAME.test_file
+    $DIFF $TEST_NAME.control_file $TEST_NAME.test_file >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $FILE file contents differ from control"
+      exit 1
+    fi
+    rm $TEST_NAME.control_file $TEST_NAME.test_file
+  fi
+done
+
+if [[ $COMPARE_TIMES == true ]]; then
+  cd control
+  if [[ -f $TEST_NAME.extract.stdout.1 ]]; then
+    for FILE in $(ls -1 $TEST_NAME.extract.stdout.*)
+    do
+      echo "Extract times:"
+      cat $FILE | grep "\->.*second" > $TEST_NAME.control_extract_times
+      cat $BASE_DIR/test/$FILE | grep "\->.*second" > $TEST_NAME.test_extract_times
+      $DIFF --side-by-side $TEST_NAME.control_extract_times $TEST_NAME.test_extract_times
+      rm $TEST_NAME.control_extract_times $TEST_NAME.test_extract_times
+    done
+  fi
+  if [[ -f $TEST_NAME.build.stdout.1 ]]; then
+    for FILE in $(ls -1 $TEST_NAME.build.stdout.*)
+    do
+      echo "Build times:"
+      cat $FILE | grep "\->.*second" > $TEST_NAME.control_build_times
+      cat $BASE_DIR/test/$FILE | grep "\->.*second" > $TEST_NAME.test_build_times
+      $DIFF --side-by-side $TEST_NAME.control_build_times $TEST_NAME.test_build_times
+      rm $TEST_NAME.control_build_times $TEST_NAME.test_build_times
+    done
+  fi
+fi
+
+rm -f $DIFF_OUTPUT
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_results_fcm2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_results_fcm2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_results_fcm2	(revision 5095)
@@ -0,0 +1,205 @@
+#!/bin/ksh
+
+TEST_NAME=$1
+BASE_DIR=$PWD
+DIFF=${DIFF:-diff}
+
+if [[ ! -f control/$TEST_NAME/.tests.complete ]]; then
+  echo "WARNING: Control tests did not complete, skipping comparisons"
+  exit
+fi
+
+DIFF_OUTPUT=$BASE_DIR/$TEST_NAME.diff
+
+# Compare directory contents
+for DIR in extract build/etc .fcm-make/cache
+do
+  if [[ -d test/$TEST_NAME/$DIR ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $DIR directory contents ..."
+    fi
+    $DIFF -r --exclude=hello_sub2.F90 control/$TEST_NAME/$DIR test/$TEST_NAME/$DIR >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $DIR directory contents differ from control"
+      exit 1
+    fi
+  fi
+done
+
+# Compare file listings from directories
+for DIR in build/bin build/o build/include
+do
+  if [[ -d test/$TEST_NAME/$DIR ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $DIR directory listings ..."
+    fi
+    cd $BASE_DIR/control/$TEST_NAME
+    ls -R -1 $DIR >$BASE_DIR/$TEST_NAME.control_files
+    cd $BASE_DIR/test/$TEST_NAME
+    ls -R -1 $DIR >$BASE_DIR/$TEST_NAME.test_files
+    cd $BASE_DIR
+    $DIFF $TEST_NAME.control_files $TEST_NAME.test_files >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $DIR file listing differs from control"
+      exit 1
+    fi
+    rm $TEST_NAME.control_files $TEST_NAME.test_files
+  fi
+done
+
+# Compare file listings from directories (non-recursive)
+for DIR in . extract build preprocess
+do
+  if [[ -d test/$TEST_NAME/$DIR ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $DIR directory listings ..."
+    fi
+    cd $BASE_DIR/control/$TEST_NAME
+    ls -1 $DIR >$BASE_DIR/$TEST_NAME.control_files
+    cd $BASE_DIR/test/$TEST_NAME
+    ls -1 $DIR >$BASE_DIR/$TEST_NAME.test_files
+    cd $BASE_DIR/
+    $DIFF $TEST_NAME.control_files $TEST_NAME.test_files >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $DIR file listing differs from control"
+      exit 1
+    fi
+    rm $TEST_NAME.control_files $TEST_NAME.test_files
+  fi
+done
+
+# Compare files in build/include directory (except *.mod)
+if [[ -d test/$TEST_NAME/build/include ]]; then
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing build/include directory contents ..."
+  fi
+  cd test/$TEST_NAME/build/include
+  for FILE in $(ls -1 | grep -v "\.mod$")
+  do
+    $DIFF $BASE_DIR/control/$TEST_NAME/build/include/$FILE $FILE >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+      exit 1
+    fi
+  done
+fi
+cd $BASE_DIR
+
+# Compare files in preprocess directory (ignoring RUN_DIR in *.c & *.cpp files)
+if [[ -d test/$TEST_NAME/preprocess ]]; then
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing preprocess directory contents ..."
+  fi
+  cd test/$TEST_NAME
+  FILES=$(find preprocess -type f)
+  cd $BASE_DIR
+  for FILE in $FILES
+  do
+    if [[ $FILE == ${FILE%.c} && $FILE == ${FILE%.cpp} ]]; then
+      $DIFF control/$TEST_NAME/$FILE test/$TEST_NAME/$FILE >$DIFF_OUTPUT
+    else
+      sed "s#/.*/fcm_test_suite/control/##g" control/$TEST_NAME/$FILE >$TEST_NAME.control_file
+      sed "s#/.*/fcm_test_suite/test/##g" test/$TEST_NAME/$FILE >$TEST_NAME.test_file
+      $DIFF $TEST_NAME.control_file $TEST_NAME.test_file >$DIFF_OUTPUT
+    fi
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+      exit 1
+    fi
+    rm -f $TEST_NAME.control_file $TEST_NAME.test_file
+  done
+fi
+
+# Compare build command files
+cd test
+unset TEST_FILES CONTROL_FILES
+if [[ -f $TEST_NAME.build.commands.1 ]]; then
+  TEST_FILES=$(ls -1 $TEST_NAME.build.commands.*)
+fi
+cd $BASE_DIR/control
+if [[ -f $TEST_NAME.build.commands.1 ]]; then
+  CONTROL_FILES=$(ls -1 $TEST_NAME.build.commands.*)
+fi
+if [[ $TEST_FILES != $CONTROL_FILES ]]; then
+  echo "FAILED: $TEST_NAME - number of build command files differs from control"
+  exit 1
+fi
+cd $BASE_DIR
+for FILE in $TEST_FILES
+do
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing $FILE contents ..."
+  fi
+  if [[ $NPROC = 1 ]]; then
+    $DIFF control/$FILE test/$FILE >$DIFF_OUTPUT
+  else
+    sort control/$FILE >$TEST_NAME.control_file
+    sort test/$FILE >$TEST_NAME.test_file
+    $DIFF $TEST_NAME.control_file $TEST_NAME.test_file >$DIFF_OUTPUT
+  fi
+  if [[ $? != 0 ]]; then
+    echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+    exit 1
+  fi
+  rm -f $TEST_NAME.control_file $TEST_NAME.test_file
+done
+
+# Compare run output files
+cd test
+unset TEST_FILES CONTROL_FILES
+if [[ -f $TEST_NAME.exe.stdout.1 ]]; then
+  TEST_FILES="$TEST_FILES $(ls -1 $TEST_NAME.exe.stdout.*)"
+fi
+cd $BASE_DIR/control
+if [[ -f $TEST_NAME.exe.stdout.1 ]]; then
+  CONTROL_FILES="$CONTROL_FILES $(ls -1 $TEST_NAME.exe.stdout.*)"
+fi
+if [[ $TEST_FILES != $CONTROL_FILES ]]; then
+  echo "FAILED: $TEST_NAME - number of run output files differs from control"
+  exit 1
+fi
+cd $BASE_DIR
+for FILE in $TEST_FILES
+do
+  if [[ $DEBUG == true ]]; then
+    echo "Comparing $FILE contents ..."
+  fi
+  $DIFF control/$FILE test/$FILE >$DIFF_OUTPUT
+  if [[ $? != 0 ]]; then
+    echo "FAILED: $TEST_NAME - $FILE contents differ from control"
+    exit 1
+  fi
+done
+
+# Compare file contents ignoring RUN_DIR
+for FILE in .fcm-make/config-as-parsed.cfg .fcm-make/config-on-success.cfg
+do
+  if [[ -f test/$TEST_NAME/$FILE ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $FILE contents ..."
+    fi
+    cat control/$TEST_NAME/$FILE | sed "s#/.*/fcm_test_suite/control/##g" >$TEST_NAME.control_file
+    cat test/$TEST_NAME/$FILE | sed "s#/.*/fcm_test_suite/test/##g" >$TEST_NAME.test_file
+    $DIFF $TEST_NAME.control_file $TEST_NAME.test_file >$DIFF_OUTPUT
+    if [[ $? != 0 ]]; then
+      echo "FAILED: $TEST_NAME - $FILE file contents differ from control"
+      exit 1
+    fi
+    rm $TEST_NAME.control_file $TEST_NAME.test_file
+  fi
+done
+
+if [[ $COMPARE_TIMES == true ]]; then
+  cd control
+  for FILE in $(ls -1 $TEST_NAME.make.stdout.*)
+  do
+    echo "Make times:"
+    grep -e '\[done\]' -e '\[FAIL\]' $FILE >$TEST_NAME.control_make_times
+    grep -e '\[done\]' -e '\[FAIL\]' $BASE_DIR/test/$FILE >$TEST_NAME.test_make_times
+    $DIFF --side-by-side $TEST_NAME.control_make_times $TEST_NAME.test_make_times
+    rm $TEST_NAME.control_make_times $TEST_NAME.test_make_times
+  done
+fi
+
+rm -f $DIFF_OUTPUT
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_times_fcm1-2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_times_fcm1-2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/compare_times_fcm1-2	(revision 5095)
@@ -0,0 +1,27 @@
+#!/bin/ksh
+
+TEST=$1
+DIFF=${DIFF:-diff}
+
+for FILE in $(ls -1 $TEST.make.stdout.*)
+do
+  FCM1_EXTRACT=$(echo "fcm1_${FILE#fcm2_}" | sed 's/make/extract/')
+  FCM1_BUILD=$(echo "fcm1_${FILE#fcm2_}" | sed 's/make/build/')
+  if [[ -f $FCM1_EXTRACT || -f $FCM1_BUILD ]]; then
+    if [[ $DEBUG == true ]]; then
+      echo "Comparing $FILE with $FCM1_EXTRACT & $FCM1_BUILD ..."
+    fi
+    echo "Times:"
+    if [[ -f $FCM1_EXTRACT ]]; then
+      grep "\->.*second" $FCM1_EXTRACT | grep -v Setup | grep -v Parse >> $TEST.fcm1_times
+    fi
+    if [[ -f $FCM1_BUILD ]]; then
+      grep "\->.*second" $FCM1_BUILD | grep -v Setup | grep -v Parse >> $TEST.fcm1_times
+    fi
+    grep -e '\[done\]' -e '\[FAIL\]' $FILE > $TEST.fcm2_times
+    $DIFF --side-by-side $TEST.fcm1_times $TEST.fcm2_times
+    rm $TEST.fcm1_times $TEST.fcm2_times
+  fi
+done
+
+exit 0
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/create_hpc_batch_script
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/create_hpc_batch_script	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/create_hpc_batch_script	(revision 5095)
@@ -0,0 +1,90 @@
+#!/bin/ksh
+
+cat <<EOF >$BATCH_SCRIPT
+#!/bin/ksh
+#
+# @ job_type = serial
+# @ class = serial
+# @ resources = ConsumableCpus(6) ConsumableMemory(1gb)
+# @ initialdir = $RUN_DIR_HPC
+# @ output = hpc_batch.stdout
+# @ error  = hpc_batch.stderr
+# @ queue
+
+. prg_12_1_0_9
+export PATH=$RUN_DIR_HPC/fcm/bin:\$PATH
+export DIFF="/opt/freeware/bin/diff"
+export DEBUG=$DEBUG
+
+. \$MY_BIN/$BATCH_DIRS_NAME
+let failed=0
+for TEST in \$TESTS_FCM1
+do
+  cd $RUN_DIR_HPC/\$TEST
+  echo "\$(date): Running \$TEST ..."
+  find src -exec touch {} \\;
+  fcm build -v 2 -j 6 >../\$TEST.build.stdout.1 2>../\$TEST.build.stderr.1
+  RC=\$?
+  if [[ \$RC != 0 ]]; then
+    echo "FAILED: \$TEST failed"
+    let failed=failed+1
+  else
+EOF
+
+if [[ $TYPE == control ]]; then
+  echo "    touch .tests.complete" >>$BATCH_SCRIPT
+else
+  cat <<EOF >>$BATCH_SCRIPT
+    export COMPARE_TIMES=true
+    cd $BASE_DIR_HPC
+    $MY_BIN/compare_results_fcm1 \$TEST
+    if [[ \$? != 0 ]]; then
+      let failed=failed+1
+    fi
+EOF
+fi
+
+cat <<EOF >>$BATCH_SCRIPT
+  fi
+done
+for TEST in \$TESTS_FCM2
+do
+  cd $RUN_DIR_HPC/\$TEST
+  echo "\$(date): Running \$TEST ..."
+  find extract -exec touch {} \\;
+  fcm make -j 6 >../\$TEST.make.stdout.1 2>../\$TEST.make.stderr.1
+  RC=\$?
+  if [[ \$RC != 0 ]]; then
+    echo "FAILED: \$TEST failed"
+    let failed=failed+1
+  else
+EOF
+
+if [[ $TYPE == control ]]; then
+  cat <<EOF >>$BATCH_SCRIPT
+    touch .tests.complete
+    cd ..
+    $MY_BIN/compare_times_fcm1-2 \$TEST
+EOF
+else
+  cat <<EOF >>$BATCH_SCRIPT
+    export COMPARE_TIMES=true
+    cd $BASE_DIR_HPC
+    $MY_BIN/compare_results_fcm2 \$TEST
+    if [[ \$? != 0 ]]; then
+      let failed=failed+1
+    fi
+EOF
+fi
+
+cat <<EOF >>$BATCH_SCRIPT
+  fi
+done
+
+echo "\$(date): HPC performance tests finished"
+if [[ \$failed == 0 ]]; then
+  echo "SUMMARY: All HPC performance tests succeeded"
+else
+  echo "SUMMARY: \$failed HPC performance tests failed"
+fi
+EOF
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/create_repos
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/create_repos	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/create_repos	(revision 5095)
@@ -0,0 +1,197 @@
+#!/bin/ksh
+set -eu
+
+REPOS_FILES=$MY_BIN/repos
+
+echo "$(date): Creating repository ..."
+rm -rf $REPOS_DIR
+svnadmin create --fs-type fsfs $REPOS_DIR
+
+echo "$(date): Initial import ..."
+
+svn import -q $REPOS_FILES/trunk $REPOS_URL/trunk -m" "
+svn mkdir -q $REPOS_URL/branches -m" "
+svn mkdir -q $REPOS_URL/tags -m" "
+
+# Modify some files
+branch=modify_files_base
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+rm -rf $BASE_DIR/work
+svn co -q $REPOS_URL/branches/dev/Share/$branch $BASE_DIR/work
+cd $BASE_DIR/work
+perl -pi -e 's/IMPLICIT NONE/implicit none/' program/hello.F90
+perl -pi -e 's/Hello Earth/Hello Earthlings/' module/hello_constants.inc
+svn ci -m" "
+
+# Modify some files, one of which can be merged with modify_files_base branch
+branch=modify_files_merge1
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e "s/this = 'Hello'/this = 'HELLO'/" program/hello.F90
+perl -pi -e 's/Hello Earth/Hello Earthlings/' subroutine/hello_c.c
+svn ci -m" "
+
+# Modify a file which can be merged with the modify_files_base & modify_files_merge1 branches
+branch=modify_files_merge2
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/PROGRAM/program/' program/hello.F90
+svn ci -m" "
+
+# Modify a file which clashes with modify_files_base branch
+branch=modify_files_clash
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/IMPLICIT NONE/implicit NONE/' program/hello.F90
+svn ci -m" "
+
+# Modify a subroutine without altering its interface
+branch=modify_subroutine
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/integer (common)/integer (com)/' subroutine/hello_sub.F90
+svn ci -m" "
+
+# Modify a subroutine and alter its interface
+branch=modify_subroutine_interface
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/integer_arg/int_arg/' subroutine/hello_sub.F90
+svn ci -m" "
+
+# Modify a pre-processing include file
+branch=modify_pp_include
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/:/: Message - /' subroutine/hello_sub.h
+svn ci -m" "
+
+# Add lines to a file to coincide with following branch
+branch=add_lines
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+cp $REPOS_FILES/add_subroutine/hello.F90.add_lines program/hello.F90
+svn ci -m" "
+
+# Add a new file
+branch=add_file
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+cp $REPOS_FILES/add_subroutine/hello.F90 program/hello.F90
+cp $REPOS_FILES/add_subroutine/hello_sub2.f90 subroutine
+svn add subroutine/hello_sub2.f90
+svn ci -m" "
+
+# Add a new directory
+branch=add_directory
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+cp $REPOS_FILES/add_subroutine/hello.F90 program/hello.F90
+mkdir subroutine2
+cp $REPOS_FILES/add_subroutine/hello_sub2.f90 subroutine2
+svn add subroutine2
+svn ci -m" "
+
+# Add a duplicate subroutine
+branch=add_duplicate
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+cp subroutine/hello_sub.F90 subroutine/hello_sub2.F90
+svn add subroutine/hello_sub2.F90
+svn ci -m" "
+
+# Delete a file
+branch=delete_file
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm subroutine/hello_c.c
+svn ci -m" "
+
+# Delete a CPP file
+branch=delete_pp_file
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm subroutine/hello_sub.F90
+svn ci -m" "
+
+# Delete a Fortran include file
+branch=delete_inc_file
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm module/hello_constants.inc
+svn ci -m" "
+
+# Delete a CPP include file
+branch=delete_ppinc_file
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm subroutine/hello_sub.h
+svn ci -m" "
+
+# Delete a directory
+branch=delete_directory
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm subroutine
+svn ci -m" "
+
+# Rename the executable
+branch=exe_rename
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/hello.exe/hello_world.exe/' script/hello.sh
+svn ci -m" "
+
+# Use a .f90 file as an include file
+branch=change_src_type
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/hello_constants.inc/hello_constants_inc.f90/g' module/hello_constants_dummy.inc
+svn mv module/hello_constants.inc module/hello_constants_inc.f90
+svn ci -m" "
+
+# Add a symbolic link
+branch=symbolic_link
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+cd subroutine
+ln -s hello_sub.F90 hello_sub2.F90
+svn add hello_sub2.F90
+cd $OLDPWD
+svn ci -m" "
+
+# Use space in a path name
+branch=space_in_name
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn mv blockdata/hello_blockdata.F90 "blockdata/hello blockdata.F90"
+svn mv --force blockdata "block data"
+svn ci -m" "
+
+# Create a cylc dependency which will fail
+branch=cyclic_dep_fail
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+cp $REPOS_FILES/cyclic_dependency/hello.F90 program/hello.F90
+cp $REPOS_FILES/cyclic_dependency/hello_constants.f90.fail module/hello_constants.f90
+svn ci -m" "
+
+# Create a cylc dependency which should be OK
+branch=cyclic_dep_ok
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+cp $REPOS_FILES/cyclic_dependency/hello.F90 program/hello.F90
+cp $REPOS_FILES/cyclic_dependency/hello_constants.f90.ok module/hello_constants.f90
+cp $REPOS_FILES/cyclic_dependency/hello_sub2.f90 subroutine
+svn add subroutine/hello_sub2.f90
+svn ci -m" "
+
+# Create a dependency which needs to be defined manually
+branch=f77_dep
+fcm bc -t SHARE --rev-flag NONE --non-interactive $branch $REPOS_URL@1
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/INCLUDE /!INCLUDE /' program/hello.F90
+svn ci -m" "
+
+rm -rf $BASE_DIR/work
+echo "$(date): Finished"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/get_hpc_results
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/get_hpc_results	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/get_hpc_results	(revision 5095)
@@ -0,0 +1,34 @@
+#!/bin/ksh
+
+while getopts ":ch" opt
+do
+  case $opt in
+    c ) COPY=true ;;
+    h ) HELP=true ;;
+    \? ) echo "Invalid option"
+        HELP=true
+        break ;;
+  esac
+done
+if [[ $# != $(($OPTIND - 1)) ]]; then
+  echo "Invalid argument"
+  HELP=true
+fi
+
+if [[ $HELP == true ]]; then
+ echo 'Usage: get_hpc_results [options]'
+ echo 'Valid options:'
+ echo '-c'
+ echo '   Mirror the full test directory from the HPC'
+ echo '-h'
+ echo '   Print this help message'
+ exit 1
+fi
+
+export HPC=$(rose host-select -q hpc)
+ssh $HPC "cd working/fcm_test_suite; $MY_BIN/report_hpc_results" | tee $LOCALTEMP/fcm_test_suite/hpc.summary
+
+if [[ $COPY == true ]]; then
+  echo "Mirroring results from HPC ..."
+  rsync -a --delete --rsh="ssh" $HPC:fcm_test_suite/ $LOCALTEMP/fcm_test_suite/hpc_mirror
+fi
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/perform_test_fcm1
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/perform_test_fcm1	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/perform_test_fcm1	(revision 5095)
@@ -0,0 +1,165 @@
+#!/bin/ksh
+set -u
+
+echo "$(date): Running $TEST ..."
+
+cfg_name=$TEST
+if [[ -a $MY_BIN/test_config/$TEST ]]; then
+ . $MY_BIN/test_config/$TEST
+fi
+
+export THIS_RUN_DIR=$RUN_DIR/$TEST
+rm -rf $THIS_RUN_DIR
+mkdir $THIS_RUN_DIR
+cd $THIS_RUN_DIR
+mirror=${mirror:-false}
+if [[ $mirror == remote ]]; then
+  export THIS_RUN_DIR_HPC=$RUN_DIR_HPC/$TEST
+fi
+
+NPROC=${NPROC:-1}
+let count=1
+for this_cfg in $cfg_name
+do
+  extract=$(eval "echo \${extract_$count:-}")
+  build=$(eval "echo \${build_$count:-}")
+  run=$(eval "echo \${run_$count:-}")
+  if [[ $DEBUG == true ]]; then
+    echo "Running extract ($count) ..."
+  fi
+  fcm extract -v 3 $REPOS_URL/trunk/cfg/$this_cfg.cfg >../$TEST.extract.stdout.$count 2>../$TEST.extract.stderr.$count
+  RC=$?
+  if [[ $extract == fail ]]; then
+    if [[ $RC == 0 ]]; then
+      echo "FAILED: $TEST ($count) extract did not fail"
+      exit 1
+    fi
+  elif [[ $extract == fail_known ]]; then
+    if [[ $RC == 0 ]]; then
+      echo "FAILED: $TEST ($count) extract did not fail as expected (known problem fixed?)"
+      exit 1
+    else
+      if [[ $DEBUG == true ]]; then
+        echo "Known problem: $TEST ($count) extract failed"
+      fi
+      break
+    fi
+  elif [[ $extract == succeed_known ]]; then
+    if [[ $RC == 0 ]]; then
+      if [[ $DEBUG == true ]]; then
+        echo "Known problem: $TEST ($count) extract did not fail"
+      fi
+      break
+    else
+      echo "FAILED: $TEST ($count) extract did not succeed as expected (known problem fixed?)"
+      exit 1
+    fi
+  else
+    if [[ $RC != 0 ]]; then
+      echo "FAILED: $TEST ($count) extract failed"
+      exit 1
+    else
+      if [[ $mirror == local ]]; then
+        cd ${THIS_RUN_DIR}_mirror
+      elif [[ $mirror == remote ]]; then
+        echo "$TEST" >> $BATCH_DIRS
+        break
+      fi
+      if [[ $DEBUG == true ]]; then
+        echo "Running build ($count) ..."
+      fi
+      export command_file=$RUN_DIR/$TEST.build.commands.$count
+      touch $command_file
+      fcm build -v 2 -j $NPROC >../$TEST.build.stdout.$count 2>../$TEST.build.stderr.$count
+      RC=$?
+      if [[ $build == fail ]]; then
+        if [[ $RC == 0 ]]; then
+          echo "FAILED: $TEST ($count) build did not fail"
+          exit 1
+        fi
+      elif [[ $build == fail_known ]]; then
+        if [[ $RC == 0 ]]; then
+          echo "FAILED: $TEST ($count) build did not fail as expected (known problem fixed?)"
+          exit 1
+        else
+          if [[ $DEBUG == true ]]; then
+            echo "Known problem: $TEST ($count) build failed"
+          fi
+          break
+        fi
+      elif [[ $build == succeed_known ]]; then
+        if [[ $RC == 0 ]]; then
+          if [[ $DEBUG == true ]]; then
+            echo "Known problem: $TEST ($count) build did not fail"
+          fi
+          break
+        else
+          echo "FAILED: $TEST ($count) build did not succeed as expected (known problem fixed?)"
+          exit 1
+        fi
+      else
+        if [[ $RC != 0 ]]; then
+          echo "FAILED: $TEST ($count) build failed"
+          exit 1
+        else
+          if [[ $run != no ]]; then
+            exe_name=hello.sh
+            env_file=fcm_env.sh
+            if [[ ! -a $env_file ]]; then
+              echo "FAILED: $TEST ($count) env file does not exist"
+              exit 1
+            else
+              . $env_file
+              if [[ $DEBUG == true ]]; then
+                echo "Running executable ($count) ..."
+              fi
+              $exe_name >../$TEST.exe.stdout.$count 2>../$TEST.exe.stderr.$count
+              RC=$?
+              if [[ $run == fail ]]; then
+                if [[ $RC == 0 ]]; then
+                  echo "FAILED: $TEST ($count) run did not fail"
+                  exit 1
+                fi
+              elif [[ $run == fail_known ]]; then
+                if [[ $RC == 0 ]]; then
+                  echo "FAILED: $TEST ($count) run did not fail as expected (known problem fixed?)"
+                  exit 1
+                else
+                  if [[ $DEBUG == true ]]; then
+                    echo "Known problem: $TEST ($count) run failed"
+                  fi
+                  break
+                fi
+              elif [[ $run == succeed_known ]]; then
+                if [[ $RC == 0 ]]; then
+                  if [[ $DEBUG == true ]]; then
+                    echo "Known problem: $TEST ($count) run did not fail"
+                  fi
+                  break
+                else
+                  echo "FAILED: $TEST ($count) run did not succeed as expected (known problem fixed?)"
+                  exit 1
+                fi
+              else
+                if [[ $RC != 0 ]]; then
+                  echo "FAILED: $TEST ($count) run failed"
+                  exit 1
+                fi
+              fi
+            fi
+          fi
+        fi
+      fi
+    fi
+  fi
+  let count=count+1
+done
+
+rm -rf $BASE_DIR/work
+
+if [[ $TYPE == control ]]; then
+  touch $THIS_RUN_DIR/.tests.complete
+else
+  cd $BASE_DIR
+  $MY_BIN/compare_results_fcm1 $TEST
+fi
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/perform_test_fcm2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/perform_test_fcm2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/perform_test_fcm2	(revision 5095)
@@ -0,0 +1,204 @@
+#!/bin/ksh
+set -u
+
+echo "$(date): Running $TEST ..."
+
+cfg_name=$TEST
+if [[ -a $MY_BIN/test_config/$TEST ]]; then
+ . $MY_BIN/test_config/$TEST
+fi
+
+export THIS_RUN_DIR=$RUN_DIR/$TEST
+rm -rf $THIS_RUN_DIR
+mkdir $THIS_RUN_DIR
+cd $THIS_RUN_DIR
+mirror=${mirror:-false}
+if [[ $mirror == remote ]]; then
+  export THIS_RUN_DIR_HPC=$RUN_DIR_HPC/$TEST
+fi
+
+NPROC=${NPROC:-1}
+let count=1
+for this_cfg in $cfg_name
+do
+  make=$(eval "echo \${make_$count:-}")
+  run=$(eval "echo \${run_$count:-}")
+  export command_file=$RUN_DIR/$TEST.build.commands.$count
+  touch $command_file
+  if [[ $DEBUG == true ]]; then
+    echo "Running make ($count) ..."
+  fi
+  fcm make -j $NPROC -f $REPOS_URL/trunk/cfg/$this_cfg.cfg >../$TEST.make.stdout.$count 2>../$TEST.make.stderr.$count
+  RC=$?
+  if [[ $make == fail ]]; then
+    if [[ $RC == 0 ]]; then
+      echo "FAILED: $TEST ($count) make did not fail"
+      exit 1
+    fi
+  elif [[ $make == fail_known ]]; then
+    if [[ $RC == 0 ]]; then
+      echo "FAILED: $TEST ($count) make did not fail as expected (known problem fixed?)"
+      exit 1
+    else
+      if [[ $DEBUG == true ]]; then
+        echo "Known problem: $TEST ($count) make failed"
+      fi
+      break
+    fi
+  elif [[ $make == succeed_known ]]; then
+    if [[ $RC == 0 ]]; then
+      if [[ $DEBUG == true ]]; then
+        echo "Known problem: $TEST ($count) make did not fail"
+      fi
+      break
+    else
+      echo "FAILED: $TEST ($count) make did not succeed as expected (known problem fixed?)"
+      exit 1
+    fi
+  else
+    if [[ $RC != 0 ]]; then
+      echo "FAILED: $TEST ($count) make failed"
+      exit 1
+    else
+      if [[ $mirror == remote ]]; then
+        echo "$TEST" >> $BATCH_DIRS
+        break
+      elif [[ $mirror == local ]]; then
+        cd ${THIS_RUN_DIR}_mirror
+        RC=$?
+        if [[ $RC != 0 ]]; then
+          echo "FAILED: $TEST ($count) cd to mirror failed"
+          exit 1
+        fi
+        if [[ $DEBUG == true ]]; then
+          echo "Running make after mirror ($count) ..."
+        fi
+        fcm make -j $NPROC >../$TEST.make2.stdout.$count 2>../$TEST.make2.stderr.$count
+        RC=$?
+        if [[ $RC != 0 ]]; then
+          echo "FAILED: $TEST ($count) make after mirror failed"
+          exit 1
+        fi
+      fi
+      if [[ $run != no ]]; then
+        exe_name=hello.sh
+        build_dir=$PWD/build/bin
+        if [[ ! -a $build_dir ]]; then
+          echo "FAILED: $TEST ($count) build directory does not exist"
+          exit 1
+        else
+          PATH=$build_dir:$PATH
+          if [[ $DEBUG == true ]]; then
+            echo "Running executable ($count) ..."
+          fi
+          $exe_name >../$TEST.exe.stdout.$count 2>../$TEST.exe.stderr.$count
+          RC=$?
+          if [[ $run == fail ]]; then
+            if [[ $RC == 0 ]]; then
+              echo "FAILED: $TEST ($count) run did not fail"
+              exit 1
+            fi
+          elif [[ $run == fail_known ]]; then
+            if [[ $RC == 0 ]]; then
+              echo "FAILED: $TEST ($count) run did not fail as expected (known problem fixed?)"
+              exit 1
+            else
+              if [[ $DEBUG == true ]]; then
+                echo "Known problem: $TEST ($count) run failed"
+              fi
+              break
+            fi
+          elif [[ $run == succeed_known ]]; then
+            if [[ $RC == 0 ]]; then
+              if [[ $DEBUG == true ]]; then
+                echo "Known problem: $TEST ($count) run did not fail"
+              fi
+              break
+            else
+              echo "FAILED: $TEST ($count) run did not succeed as expected (known problem fixed?)"
+              exit 1
+            fi
+          else
+            if [[ $RC != 0 ]]; then
+              echo "FAILED: $TEST ($count) run failed"
+              exit 1
+            else
+              build_dir=$PWD/build2/bin
+              if [[ -a $build_dir ]]; then
+                PATH=$build_dir:$PATH
+                if [[ $DEBUG == true ]]; then
+                  echo "Running executable from build2 ($count) ..."
+                fi
+                $exe_name >../$TEST.exe2.stdout.$count 2>../$TEST.exe2.stderr.$count
+                RC=$?
+                if [[ $RC != 0 ]]; then
+                  echo "FAILED: $TEST ($count) build2 run failed"
+                  exit 1
+                fi
+              fi
+            fi
+          fi
+        fi
+      fi
+    fi
+  fi
+  let count=count+1
+done
+
+rm -rf $BASE_DIR/work
+
+if [[ $TYPE == control ]]; then
+  touch $THIS_RUN_DIR/.tests.complete
+  compare_fcm1=${compare_fcm1:-true}
+  if [[ $compare_fcm1 == true ]]; then
+    cd $RUN_DIR
+    # Test that fcm2 triggers the same build commands as the equivalent from fcm1
+    if [[ -f $TEST.build.commands.1 ]]; then
+      for FILE in $(ls -1 $TEST.build.commands.*)
+      do
+        FCM1_FILE=fcm1_${FILE#fcm2_}
+        if [[ -f $FCM1_FILE ]]; then
+          if [[ $DEBUG == true ]]; then
+            echo "Comparing $FILE with $FCM1_FILE ..."
+          fi
+          cat $FCM1_FILE | grep -v wrap_ar | sed 's#wrap_ld#wrap_fc#' | sed 's# -c # #' | sed 's#__fcm__##' | sed 's#\.F#\.f#' | \
+             sed 's#/fcm1_[^ ]*/##g' | sed 's#-Iinc #-Iinclude #g' | sed 's# -Llib -Llib# -Llib#' | \
+             sed 's# -Llib -lmkmdblseq##' | sort > $TEST.fcm1.build.commands
+          cat $FILE | grep -v wrap_ar  | sed 's# -c # #' | sed 's#-oo/#-o #' | sed 's#-obin/#-o #' | sed 's# o/# #g' | \
+             sed 's#\.F#\.f#' | sed 's#/fcm2_[^ ]*/##g' | sed 's#\.\./\.\.##g' | sed 's#wrap_fc2 \(.*\.exe\)#wrap_fc \1#' | \
+             sort > $TEST.fcm2.build.commands
+          diff $TEST.fcm1.build.commands $TEST.fcm2.build.commands > $TEST.diff
+          if [[ $? != 0 ]]; then
+            echo "FAILED: $FILE - build commands differ from FCM1 case"
+            exit 1
+          else
+            rm $TEST.fcm1.build.commands $TEST.fcm2.build.commands $TEST.diff
+          fi
+        fi
+      done
+    fi
+    # Test that fcm2 run results match equivalent from fcm1
+    if [[ -f $TEST.exe.stdout.1 ]]; then
+      for FILE in $(ls -1 $TEST.exe.stdout.*)
+      do
+        FCM1_FILE=fcm1_${FILE#fcm2_}
+        if [[ -f $FCM1_FILE ]]; then
+          if [[ $DEBUG == true ]]; then
+            echo "Comparing $FILE with $FCM1_FILE ..."
+          fi
+          diff -q $FCM1_FILE $FILE
+          if [[ $? != 0 ]]; then
+            echo "FAILED: $FILE - contents differ from FCM1 case"
+            exit 1
+          fi
+        fi
+      done
+    fi
+    if [[ $COMPARE_TIMES == true ]]; then
+      $MY_BIN/compare_times_fcm1-2 $TEST
+    fi
+  fi
+else
+  cd $BASE_DIR
+  $MY_BIN/compare_results_fcm2 $TEST
+fi
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/report_hpc_results
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/report_hpc_results	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/report_hpc_results	(revision 5095)
@@ -0,0 +1,14 @@
+#!/bin/ksh
+
+if [[ -f control/hpc_batch.stderr ]]; then
+  echo "##### Control run stderr ############################################"
+  cat control/hpc_batch.stderr
+  echo "##### Control run stdout ############################################"
+  cat control/hpc_batch.stdout | sed -ne '1,/^SUMMARY/p'
+fi
+if [[ -f test/hpc_batch.stderr ]]; then
+  echo "##### Test run stderr ###############################################"
+  cat test/hpc_batch.stderr
+  echo "##### Test run stdout ###############################################"
+  cat test/hpc_batch.stdout | sed -ne '1,/^SUMMARY/p'
+fi
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello.F90	(revision 5095)
@@ -0,0 +1,26 @@
+PROGRAM Hello
+
+#if !defined(LOCAL_STRING)
+USE Hello_Constants, ONLY: hello_string
+#endif
+
+IMPLICIT NONE
+
+#if defined(LOCAL_STRING)
+CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Mother Earth!'
+#endif
+
+#if defined(CALL_HELLO_SUB)
+INCLUDE 'hello_sub.interface'
+#endif
+INCLUDE 'hello_sub2.interface'
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello'
+
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
+#if defined(CALL_HELLO_SUB)
+CALL Hello_Sub (HUGE(0))
+#endif
+CALL Hello_Sub2 ()
+
+END PROGRAM Hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello.F90.add_lines
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello.F90.add_lines	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello.F90.add_lines	(revision 5095)
@@ -0,0 +1,28 @@
+PROGRAM Hello
+
+#if !defined(LOCAL_STRING)
+USE Hello_Constants, ONLY: hello_string
+#endif
+
+IMPLICIT NONE
+
+#if defined(LOCAL_STRING)
+CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Mother Earth!'
+#endif
+
+INTEGER :: integer_arg = 1234
+
+#if defined(CALL_HELLO_SUB)
+INCLUDE 'hello_sub.interface'
+#endif
+
+! Just a comment line
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello'
+
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
+#if defined(CALL_HELLO_SUB)
+CALL Hello_Sub (integer_arg)
+#endif
+
+END PROGRAM Hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello_sub2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello_sub2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/add_subroutine/hello_sub2.f90	(revision 5095)
@@ -0,0 +1,11 @@
+SUBROUTINE Hello_Sub2
+
+USE Hello_Constants, ONLY: hello_string
+
+IMPLICIT NONE
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello_Sub2'
+
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
+
+END SUBROUTINE Hello_Sub2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello.F90	(revision 5095)
@@ -0,0 +1,14 @@
+PROGRAM Hello
+
+USE Hello_Constants, ONLY: hello_string, Hello_Sub_Wrapper
+
+IMPLICIT NONE
+
+INTEGER :: integer_arg = 1234
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello'
+
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
+CALL Hello_Sub_Wrapper (integer_arg)
+
+END PROGRAM Hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_constants.f90.fail
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_constants.f90.fail	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_constants.f90.fail	(revision 5095)
@@ -0,0 +1,19 @@
+MODULE Hello_Constants
+
+INCLUDE 'hello_constants_dummy.inc'
+
+CONTAINS
+
+SUBROUTINE Hello_Sub_Wrapper (integer_arg)
+
+IMPLICIT NONE
+
+INTEGER :: integer_arg
+
+INCLUDE 'hello_sub.interface'
+
+CALL Hello_Sub (integer_arg)
+
+END SUBROUTINE Hello_Sub_Wrapper
+
+END MODULE Hello_Constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_constants.f90.ok
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_constants.f90.ok	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_constants.f90.ok	(revision 5095)
@@ -0,0 +1,19 @@
+MODULE Hello_Constants
+
+INCLUDE 'hello_constants_dummy.inc'
+
+CONTAINS
+
+SUBROUTINE Hello_Sub_Wrapper (integer_arg)
+
+IMPLICIT NONE
+
+INTEGER :: integer_arg
+
+INCLUDE 'hello_sub2.interface'
+
+CALL Hello_Sub2 (integer_arg)
+
+END SUBROUTINE Hello_Sub_Wrapper
+
+END MODULE Hello_Constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_sub2.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_sub2.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/cyclic_dependency/hello_sub2.f90	(revision 5095)
@@ -0,0 +1,11 @@
+SUBROUTINE Hello_Sub2 (integer_arg)
+
+IMPLICIT NONE
+
+INTEGER :: integer_arg
+
+INCLUDE 'hello_sub.interface'
+
+CALL Hello_Sub (integer_arg)
+
+END SUBROUTINE Hello_Sub2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/blockdata/hello_blockdata.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/blockdata/hello_blockdata.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/blockdata/hello_blockdata.F90	(revision 5095)
@@ -0,0 +1,9 @@
+BLOCK DATA hello_blockdata
+INTEGER :: integer_common
+COMMON /general/integer_common
+#if defined(ODD)
+DATA integer_common/1357/
+#else
+DATA integer_common/2468/
+#endif
+END BLOCK DATA hello_blockdata
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_directory.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_directory.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_directory.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/add_directory
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_directory_expsrc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_directory_expsrc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_directory_expsrc.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/add_directory
+EXPSRC::test::branch1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_file.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/add_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_add_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/add_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_base.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_base.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_base.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base_inc.cfg
+
+BLD::TARGET                             hello.sh test__namelist.etc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_base_inc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_base_inc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_base_inc.cfg	(revision 5095)
@@ -0,0 +1,23 @@
+DEST::ROOTDIR                           $PWD
+
+REPOS::test::base                       fcm:test_suite_tr
+EXPSRC::test::base
+
+BLD::TOOL::FC                           wrap_fc
+BLD::TOOL::FFLAGS                       -assume nosource_include
+
+BLD::TOOL::CC                           wrap_cc
+BLD::TOOL::CFLAGS                       -O3
+
+BLD::TOOL::FFLAGS::test/subroutine      %BLD::TOOL::FFLAGS -O3
+
+BLD::TOOL::LD                           wrap_ld
+BLD::TOOL::AR                           wrap_ar
+
+BLD::TOOL::FPP                          wrap_pp
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub HELLO_SUB
+BLD::TOOL::FPPKEYS::test/program/hello        CALL_HELLO_SUB
+BLD::PP::test/subroutine/hello_sub      true
+BLD::PP::test/program                   true
+
+BLD::BLOCKDATA                          hello_blockdata
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_clash.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_clash.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_clash.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_clash
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/modify_files_merge2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_conflict_fail.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_conflict_fail.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_conflict_fail.cfg	(revision 5095)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/modify_files_merge2
+
+CONFLICT                                fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_conflict_override.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_conflict_override.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_conflict_override.cfg	(revision 5095)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/modify_files_merge2
+
+CONFLICT                                override
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_inherit.cfg	(revision 5095)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/modify_files_merge2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_inherit_wrong_include.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_inherit_wrong_include.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_inherit_wrong_include.cfg	(revision 5095)
@@ -0,0 +1,12 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/modify_files_merge2
+
+BLD::TOOL::FFLAGS::test/module          -O3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_wcopies.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_wcopies.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_wcopies.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    $BASE_DIR/work/b1
+REPOS::test::branch2                    $BASE_DIR/work/b2
+REPOS::test::branch3                    $BASE_DIR/work/b3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_wcopy.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_wcopy.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_branches_merge_wcopy.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    $BASE_DIR/work
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_cflags.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_cflags.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_cflags.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::CFLAGS::test/subroutine      -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_change_src_type.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_change_src_type.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_change_src_type.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/change_src_type
+BLD::SRC_TYPE::test/module/hello_constants_inc.f90  FORTRAN::FORTRAN9X::INCLUDE
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_directory.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_directory.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_directory.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/delete_directory
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_directory_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_directory_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_directory_inherit.cfg	(revision 5095)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/delete_directory
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_file.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/delete_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/delete_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_inc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_inc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit_force.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit_force.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit_force.cfg	(revision 5095)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_inc_file
+
+BLD::TOOL::FFLAGS::test/module          -assume nosource_include -O3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_pp_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_pp_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_pp_file.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_pp_include
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/delete_pp_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_pp_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_pp_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_pp_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_pp_include
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/delete_pp_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_ppinc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_ppinc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit_force.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit_force.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit_force.cfg	(revision 5095)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_ppinc_file
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub HELLO_SUB DUMMY
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_duplicate_target.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_duplicate_target.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_duplicate_target.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/add_duplicate
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub2 HELLO_SUB
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exclude_dependency.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exclude_dependency.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exclude_dependency.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::EXCL_DEP                           USE::Hello_Constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exe_permissions.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exe_permissions.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exe_permissions.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    $BASE_DIR/work
+BLD::EXE_NAME::hello                    hello_world.exe
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exe_rename.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exe_rename.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_exe_rename.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/exe_rename
+BLD::EXE_NAME::hello                    hello_world.exe
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fc.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FC                           wrap_fc2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags1.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags1.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags1.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FFLAGS::test                 %BLD::TOOL::FFLAGS -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags2.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags2.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags2.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FFLAGS::test/subroutine/hello_sub     -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_fflags_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+BLD::TOOL::FFLAGS::test/subroutine/hello_sub     -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inc_devnull.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inc_devnull.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inc_devnull.cfg	(revision 5095)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base_inc.cfg
+
+# The UM makes use of the following so we have to support it
+INC                                     /dev/null
+
+BLD::TARGET                             hello.sh test__namelist.etc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inherit_invalid_path.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inherit_invalid_path.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inherit_invalid_path.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     /invalid/path
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inherit_target.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inherit_target.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_inherit_target.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+BLD::INHERIT::TARGET                    true
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_base_url.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_base_url.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_base_url.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::base                       fcm:test_suite/invalid
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_branch_url.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_branch_url.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_branch_url.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite/invalid
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_inc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_inc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_inc.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/invalid.cfg
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_namespace.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_namespace.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_namespace.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FFLAGS::test/invalid         -O2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_variable.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_variable.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_invalid_variable.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FFLAGS::test/subroutine/hello_sub    $INVALID
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_ld.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_ld.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_ld.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::LD                           wrap_ld2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_library.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_library.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_library.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base_inc.cfg
+
+BLD::TARGET                             libtest.a
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_library_rename.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_library_rename.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_library_rename.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base_inc.cfg
+
+BLD::LIB::test/module                   module
+BLD::TARGET                             libmodule.a
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_mirror.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_mirror.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_mirror.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+RDEST::MACHINE                          localhost
+RDEST::REMOTE_SHELL                     ssh
+RDEST                                   ${THIS_RUN_DIR}_mirror
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_mirror_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_mirror_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_mirror_inherit.cfg	(revision 5095)
@@ -0,0 +1,12 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_mirror
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/modify_files_merge2
+
+RDEST                                   ${THIS_RUN_DIR}_mirror
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_modify_subroutine_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_modify_subroutine_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_modify_subroutine_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_subroutine
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_modify_subroutine_interface_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_modify_subroutine_interface_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_modify_subroutine_interface_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_subroutine_interface
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_multi_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_multi_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_multi_inherit.cfg	(revision 5095)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_fflags_inherit
+USE                                     $RUN_DIR/fcm1_modify_subroutine_inherit
+
+BLD::TOOL::FFLAGS::test/module         -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_no_dep.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_no_dep.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_no_dep.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::NO_DEP                             true
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_ops.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_ops.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_ops.cfg	(revision 5095)
@@ -0,0 +1,211 @@
+# ------------------------------------------------------------------------------
+# File header
+# ------------------------------------------------------------------------------
+
+CFG::TYPE                                                                 ext
+CFG::VERSION                                                              1.0
+
+# ------------------------------------------------------------------------------
+# Destination
+# ------------------------------------------------------------------------------
+
+DEST                                                                      $PWD
+
+# ------------------------------------------------------------------------------
+# Build declarations
+# ------------------------------------------------------------------------------
+
+bld::excl_dep                                                             INC::mpif.h
+bld::excl_dep                                                             USE::F90_UNIX_IO
+bld::excl_dep                                                             USE::netcdf
+bld::excl_dep                                                             USE::YOMLUN
+bld::excl_dep                                                             USE::XLFUTILITY
+bld::exe_dep::OpsProg_BackErrCreate.exe                                   gcom
+bld::exe_dep::OpsProg_ExtractAndProcess.exe                               ops_admin::src::code::MetDB_BUFR_RETRIEVAL::source  ops_admin::src::code::MetDB_Bufr ops_admin::src::code::lapack ops_admin::src::code::blas gcom
+bld::exe_dep::OpsProg_MOPS.exe                                            ops_admin::src::code::MetDB_BUFR_RETRIEVAL::source  ops_admin::src::code::MetDB_Bufr gcom
+bld::exe_dep::OpsProg_SatRad_BiasCheck.exe                                gcom::build::gc
+bld::pp::gcom                                                             1
+bld::pp::ops::src::code::OpsMod_Altimeter::Ops_AltWriteNetCDF             1
+bld::pp::ops::src::code::OpsMod_OceanSound::Ops_OcnWriteNetCDF            1
+bld::pp::ops::src::code::OpsMod_Radar::Ops_WriteBackGrdOutput             1
+bld::pp::ops::src::code::OpsMod_Radar::Ops_WriteQCOutput                  1
+bld::pp::ops::src::code::OpsMod_Radar::Ops_WriteSoOutput                  1
+bld::pp::ops::src::code::OpsMod_RadarZ::Ops_ReadRadarRefl                 1
+bld::pp::ops::src::code::OpsMod_RadarZ::Ops_WriteRadarRTM                 1
+bld::pp::ops::src::code::OpsMod_RadarZ::Ops_WriteRadarRefl                1
+bld::pp::ops::src::code::OpsMod_SeaIce::Ops_SeaIceWriteNetCDF             1
+bld::pp::ops::src::code::OpsMod_SurfaceSST::Ops_SSTWriteNetCDF            1
+bld::pp::ops::src::code::OpsProg_ExtractAndProcess::Ops_ObStructureCreate 1
+bld::pp::ops::src::code::OpsProg_ExtractAndProcess::Ops_SetupControlInfo  1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_ad                   1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_direct               1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_k                    1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_tl                   1
+bld::target                                                               OpsScr_Build
+bld::tool::cc                                                             wrap_cc
+bld::tool::cflags                                                         
+bld::tool::cppkeys::gen::src::code::GenMod_Platform                       UNDERSCORE LOWERCASE C_LONG_LONG_INT FRL8
+bld::tool::cppkeys::gen::src::code::UM_Platform                           VAROPSVER C_LOW_U LINUX LITTLE_END C_LONG_LONG_INT FRL8
+bld::tool::cppkeys::ops::src::code::MetDB_ClientServer                    hpux DEBUG LL64 UNDERSCORE
+bld::tool::fc                                                             wrap_fc
+bld::tool::fflags                                                         -implicitnone -stand f95 -warn all -warn nointerfaces -i8 -r8 -i-static
+bld::tool::fflags::gcom                                                   -implicitnone -stand f95 -warn all -i8 -r8 -i-static -warn none -I$(OPSDIR)/mpi/mpich2-1.4-ukmo-v1/ifort-12/include
+bld::tool::fflags::ops::src::code::Ops_RTTOV9                             -implicitnone -stand f95 -warn all -warn nointerfaces -i-static -O3
+bld::tool::fppkeys::gcom                                                  GC_VERSION="'3.4'" GC_DESCRIP="'MPP'" GC_BUILD_DATE="'17285'" MPI_SRC MPILIB_32B PREC_64B GC__FORTERRUNIT=0 GC__FLUSHUNIT6 MPI_BSEND_BUFFER_SIZE=2560000
+bld::tool::fppkeys::gen::src::code::GenMod_Utilities::Gen_FlushUnit       USE_FLUSH
+bld::tool::fppkeys::gen::src::code::UM_COEX                               VAROPSVER
+bld::tool::fppkeys::gen::src::code::UM_Platform                           VAROPSVER
+bld::tool::fppkeys::ops::src::code::MetDB_GRIB                            SX6
+bld::tool::fppkeys::ops::src::code::OpsMod_Extract                        LITTLE_END
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9                            _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_ad        _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_AD
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_direct    _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_DIRECT
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_k         _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_K
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_tl        _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_TL
+bld::tool::fppkeys::ops_admin::src::code::MetDB_Bufr                      BPATH
+bld::tool::ld                                                             wrap_ld
+bld::tool::ldflags                                                        -i-static -L$(OPSDIR)/mpi/mpich2-1.4-ukmo-v1/ifort-12/lib -lmpich -lmpl -lpthread
+bld::tool::make                                                           gmake
+bld::tool::ar                                                             wrap_ar
+bld::tool::fpp                                                            wrap_pp
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+REPOS::ops::base                                                          svn://fcm4/OPS_svn/OPS/trunk
+REVISION::ops::base                                                       19069
+SRC::ops::base                                                            src/code/MetDB_ClientServer
+SRC::ops::base                                                            src/code/MetDB_GRIB
+SRC::ops::base                                                            src/code/MetDB_GRIB2
+SRC::ops::base                                                            src/code/OpsMod_Aircraft
+SRC::ops::base                                                            src/code/OpsMod_Altimeter
+SRC::ops::base                                                            src/code/OpsMod_AssocData
+SRC::ops::base                                                            src/code/OpsMod_BackErrCreate
+SRC::ops::base                                                            src/code/OpsMod_BiasCorrect
+SRC::ops::base                                                            src/code/OpsMod_Bogus
+SRC::ops::base                                                            src/code/OpsMod_CXGenerate
+SRC::ops::base                                                            src/code/OpsMod_CXInfo
+SRC::ops::base                                                            src/code/OpsMod_Constants
+SRC::ops::base                                                            src/code/OpsMod_Control
+SRC::ops::base                                                            src/code/OpsMod_Extract
+SRC::ops::base                                                            src/code/OpsMod_GPSRO
+SRC::ops::base                                                            src/code/OpsMod_GeoCloud
+SRC::ops::base                                                            src/code/OpsMod_GeoIR
+SRC::ops::base                                                            src/code/OpsMod_GroundGPS
+SRC::ops::base                                                            src/code/OpsMod_HorizontalInterp
+SRC::ops::base                                                            src/code/OpsMod_Index
+SRC::ops::base                                                            src/code/OpsMod_Listing
+SRC::ops::base                                                            src/code/OpsMod_MFSST
+SRC::ops::base                                                            src/code/OpsMod_MOPS
+SRC::ops::base                                                            src/code/OpsMod_ModelColumnIO
+SRC::ops::base                                                            src/code/OpsMod_ModelIO
+SRC::ops::base                                                            src/code/OpsMod_ModelObInfo
+SRC::ops::base                                                            src/code/OpsMod_Monitor
+SRC::ops::base                                                            src/code/OpsMod_NSST100
+SRC::ops::base                                                            src/code/OpsMod_NetCDF
+SRC::ops::base                                                            src/code/OpsMod_ObsIO
+SRC::ops::base                                                            src/code/OpsMod_ObsInfo
+SRC::ops::base                                                            src/code/OpsMod_OceanSound
+SRC::ops::base                                                            src/code/OpsMod_Process
+SRC::ops::base                                                            src/code/OpsMod_QC
+SRC::ops::base                                                            src/code/OpsMod_RTTOV
+SRC::ops::base                                                            src/code/OpsMod_Radar
+SRC::ops::base                                                            src/code/OpsMod_RadarZ
+SRC::ops::base                                                            src/code/OpsMod_SBUV
+SRC::ops::base                                                            src/code/OpsMod_SSMI
+SRC::ops::base                                                            src/code/OpsMod_SatSST
+SRC::ops::base                                                            src/code/OpsMod_SatSound
+SRC::ops::base                                                            src/code/OpsMod_Satwind
+SRC::ops::base                                                            src/code/OpsMod_Scatwind
+SRC::ops::base                                                            src/code/OpsMod_SeaIce
+SRC::ops::base                                                            src/code/OpsMod_Sonde
+SRC::ops::base                                                            src/code/OpsMod_Sort
+SRC::ops::base                                                            src/code/OpsMod_StationList
+SRC::ops::base                                                            src/code/OpsMod_Stats
+SRC::ops::base                                                            src/code/OpsMod_Surface
+SRC::ops::base                                                            src/code/OpsMod_SurfaceSST
+SRC::ops::base                                                            src/code/OpsMod_TCBogus
+SRC::ops::base                                                            src/code/OpsMod_TURBO
+SRC::ops::base                                                            src/code/OpsMod_TropicalRain
+SRC::ops::base                                                            src/code/OpsMod_Utilities
+SRC::ops::base                                                            src/code/OpsMod_Varobs
+SRC::ops::base                                                            src/code/OpsMod_VerticalInterp
+SRC::ops::base                                                            src/code/OpsMod_VisControl
+SRC::ops::base                                                            src/code/OpsProg_BackErrCreate
+SRC::ops::base                                                            src/code/OpsProg_ExtractAndProcess
+SRC::ops::base                                                            src/code/OpsProg_KillRPC
+SRC::ops::base                                                            src/code/OpsProg_MOPS
+SRC::ops::base                                                            src/code/OpsProg_RTTOV9
+SRC::ops::base                                                            src/code/Ops_AIRS_1DVar
+SRC::ops::base                                                            src/code/Ops_AIRS_Utilities
+SRC::ops::base                                                            src/code/Ops_GPSRO_Info
+SRC::ops::base                                                            src/code/Ops_GPSRO_Process
+SRC::ops::base                                                            src/code/Ops_RTTOV7
+SRC::ops::base                                                            src/code/Ops_RTTOV7_RTTOVCLD
+SRC::ops::base                                                            src/code/Ops_RTTOV9
+SRC::ops::base                                                            src/code/Ops_SSMI_1DVar
+SRC::ops::base                                                            src/code/Ops_SatRad_Info
+SRC::ops::base                                                            src/code/Ops_SatRad_Process
+SRC::ops::base                                                            src/code/Ops_SatRad_SetUp
+SRC::ops::base                                                            src/code/Ops_SatRad_Stats
+SRC::ops::base                                                            src/code/Ops_SatRad_Utilities
+SRC::ops::base                                                            src/scripts/Ops_SatRad_Scripts
+SRC::ops::base                                                            src/scripts/Ops_Scripts
+
+REPOS::gen::base                                                          svn://fcm1/GEN_svn/GEN/trunk
+REVISION::gen::base                                                       3194
+SRC::gen::base                                                            src/code/GenMod_Constants
+SRC::gen::base                                                            src/code/GenMod_Control
+SRC::gen::base                                                            src/code/GenMod_FortranIO
+SRC::gen::base                                                            src/code/GenMod_GetEnv
+SRC::gen::base                                                            src/code/GenMod_ModelIO
+SRC::gen::base                                                            src/code/GenMod_Platform
+SRC::gen::base                                                            src/code/GenMod_Reporting
+SRC::gen::base                                                            src/code/GenMod_Trace
+SRC::gen::base                                                            src/code/GenMod_UMConstants
+SRC::gen::base                                                            src/code/GenMod_Utilities
+SRC::gen::base                                                            src/code/Reconfiguration
+SRC::gen::base                                                            src/code/UM_COEX
+SRC::gen::base                                                            src/code/UM_General
+SRC::gen::base                                                            src/code/UM_Platform
+
+REPOS::ops_admin::base                                                    svn://fcm4/OPS_svn/Admin/trunk
+REVISION::ops_admin::base                                                 19069
+SRC::ops_admin::base                                                      src/code/MetDB_BUFR_RETRIEVAL/apps/create_mdblseq
+SRC::ops_admin::base                                                      src/code/MetDB_BUFR_RETRIEVAL/source
+SRC::ops_admin::base                                                      src/code/MetDB_Bufr
+SRC::ops_admin::base                                                      src/code/blas
+SRC::ops_admin::base                                                      src/code/lapack
+
+REPOS::gcom::base                                                         svn://fcm2/UM_svn/GCOM/trunk
+REVISION::gcom::base                                                      17285
+SRC::gcom::base                                                           build/gc
+SRC::gcom::base                                                           build/gcg
+SRC::gcom::base                                                           build/include
+SRC::gcom::base                                                           build/mpl
+
+REPOS::um_admin::base                                                     svn://fcm2/UM_svn/Admin/trunk
+REVISION::um_admin::base                                                  11210
+SRC::um_admin::base                                                       utilities/IBM_signal_hander
+
+# ------------------------------------------------------------------------------
+# Extra stuff to build ENS code
+# ------------------------------------------------------------------------------
+
+repos::ens::base   fcm:ens_tr/forecast/code
+REVISION::ens::base                                                       1460
+src::ens::base     EnsProg_ETKF
+src::ens::base     EnsMod_Header
+src::ens::base     EnsMod_Obstore
+src::ens::base     EnsMod_Utilities
+src::ens::base     EnsMod_Varobs
+src::ens::base     EnsProg_TrimObstore
+
+bld::tool::fppkeys::ens                                                   IBM
+bld::tool::cppkeys::ens                                                   LOWERCASE
+bld::tool::ldflags::ens                                                   -i-static -L$(OPSDIR)/mpi/mpich2-1.4-ukmo-v1/ifort-12/lib -lmpich -lmpl -lpthread -llapack
+bld::pp::ens                                                              1
+bld::exe_dep::EnsProg_ETKF.exe                                            gcom
+bld::exe_dep::EnsProg_TrimObstore.exe                                     gcom
+bld::target                                                               EnsProg_ETKF.exe EnsProg_TrimObstore.exe
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_postproc_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_postproc_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_postproc_hpc.cfg	(revision 5095)
@@ -0,0 +1,209 @@
+# ------------------------------------------------------------------------------
+# File header
+# ------------------------------------------------------------------------------
+
+CFG::TYPE                                                              ext
+CFG::VERSION                                                           1.0
+
+# ------------------------------------------------------------------------------
+# Destination
+# ------------------------------------------------------------------------------
+
+DEST                                                                   $PWD
+RDEST                                                                  $THIS_RUN_DIR_HPC
+RDEST::MACHINE                                                         $HPC
+
+# ------------------------------------------------------------------------------
+# Build declarations
+# ------------------------------------------------------------------------------
+
+bld::target         PPQYINTERP.ksh run_nae_pp.ksh run_qv_downscaling.ksh run_glo_pp.ksh run_EuroPP.ksh first_start.ksh PP4KOPER.ksh
+bld::infile_ext::cpp  C::SOURCE
+bld::tool::fc       xlf90_r
+bld::tool::ld       xlf90_r
+bld::tool::cc       xlC_r
+bld::tool::make     gmake
+bld::tool::cpp      xlC_r
+bld::tool::cppflags   -E -C
+bld::tool::fpp      cpp -E -P -traditional
+bld::excl_dep       USE::F90_UNIX_IO
+bld::excl_dep       H::gc_constants.h
+bld::excl_dep       H::gc_kinds.h
+bld::excl_dep       H::gc_options.h
+bld::tool::fflags   -c -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+bld::tool::fflags::rst  -c -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname -qrealsize=8 -qintsize=8 
+bld::tool::fflags::ver  -c -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname -qrealsize=8 -qintsize=8 
+bld::tool::fflags::ops  -c -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname -qrealsize=8 -qintsize=8 
+bld::tool::cflags   -c -qarch=pwr6 -qtune=pwr6 -O0 -qhot
+bld::tool::ldflags  
+bld::tool::ldflags::pp::model_processing::format %bld::tool::ldflags -L/projects/um1/gcom/gcom3.2/meto_ibm_pwr6_serial/lib -lgcom
+bld::tool::ldflags::rst  %bld::tool::ldflags -L/projects/um1/gcom/gcom3.2/meto_ibm_pwr6_serial/lib -lgcom -bnoquiet -L/projects/um1/lib -lsig -L/usr/lib -lmass -lessl -qsmp
+bld::pp::pp::utilities  1
+bld::pp::pp::steps  1
+bld::pp::pp::get_metdb_obs::MetDB_source 1
+bld::pp::gen 1
+bld::pp::rst::src         1
+bld::pp::ver            1
+bld::tool::cppkeys::pp::get_metdb_obs::MetDB_source  LOWERCASE L64 UNDERSCORE
+bld::tool::cppkeys::gen  C_LOW_U FRL8 C_LONG_INT NEC VAROPSVER UTILIO UNDERSCORE LOWERCASE
+bld::tool::fppkeys::gen  C_LOW_U FRL8 C_LONG_INT NEC VAROPSVER UTILIO UNDERSCORE UPPERCASE
+bld::tool::fppkeys::rst::src  NEC
+bld::tool::fflags::gen               %bld::tool::fflags -qrealsize=8 -qintsize=8
+bld::tool::cflags::gen               %bld::tool::cflags -qrealsize=8 -qintsize=8
+bld::tool::fflags::gen::UM_Platform  %bld::tool::fflags::gen -qfixed=132
+bld::tool::fflags::gen::UM_Platform::IOERROR  %bld::tool::fflags::gen
+bld::tool::fflags::gen::UM_General   %bld::tool::fflags::gen -qfixed=132
+bld::tool::fflags::gen::UM_COEX      %bld::tool::fflags::gen -qfixed=132
+bld::tool::cflags::pp::get_metdb_obs::MetDB_source    
+bld::tool::fflags::pp::get_metdb_obs::get_metdb_obs  -c -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+bld::tool::fflags::pp::moses-pdm-rfm  %bld::tool::fflags -qstrict
+bld::tool::fflags::pp::moses-pdm-rfm::dlongrad  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::down_rad_calc  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nimrod_extr_comp  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::read_ancil  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::dsolrad  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nimrod_extr_wind  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nimrod_hdr_read  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::ssdm  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::morloc  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::get_row_and_column  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nimrod_idata_read  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::ssdm_var_generator  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::um_solpos  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::morangstrom  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::sun_angles  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::moses_cloud_cover  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nimrod_3d_idata_read  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::q_vp_from_t_td  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::daynumber  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nimrod_3dextr_comp  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::moses_qs_from_t  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::route_runoff  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::regrid_real  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::initialise_routing  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::routing  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nextpoint  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::wavespeed  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::nearest_real  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::moses-pdm-rfm::sam  %bld::tool::fflags::pp::moses-pdm-rfm -qfixed=132
+bld::tool::fflags::pp::pressure_wind  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::pressure_wind::an_smear  %bld::tool::fflags
+bld::tool::fflags::pp::pressure_wind::gust_adjust  %bld::tool::fflags
+bld::tool::fflags::pp::pressure_wind::gust_analysis  %bld::tool::fflags
+bld::tool::fflags::pp::pressure_wind::pwindanal  %bld::tool::fflags
+bld::tool::fflags::pp::pressure_wind::bilin_mdi  %bld::tool::fflags
+bld::tool::fflags::pp::pressure_wind::convert_winduv  %bld::tool::fflags
+bld::tool::fflags::pp/precip_fcst  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp/precip_fcst/accmerge  %bld::tool::fflags
+bld::tool::fflags::pp/precip_fcst/object_motion  %bld::tool::fflags
+bld::tool::fflags::pp/precip_fcst/scale  %bld::tool::fflags
+bld::tool::fflags::pp/precip_fcst/wind_forecast_precip  %bld::tool::fflags
+bld::tool::fflags::pp::precip::get_surface_obs %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::precip::lightning_forecast %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::precip::lightning_merge %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::precip::metar_to_synop_weather %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::precip::read_adv_fc %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::precip::read_rad %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::model_processing::get_um_info  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::beammap_ascii_to_nimrod  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::ccitt  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::datetime_c_to_i_secs  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::def_head  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::domain_to_ng  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::european_observations_area  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::get_free_lun  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::icutout  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::ll_to_ng  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::ll_to_ps  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::locate_FCST_string  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::nearest  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::nearest_file  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::ng_to_ll  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::ng_to_ll_array  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::nimrod_i4read  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::nimrod_open2  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::nimrod_open_i4read  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::nimrod_open_i4write  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::nimrod_regrid  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::observations_area_metdb  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::ps_to_ll  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::regrid  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::round_cycle_string  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::subtract_time  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::time_diff  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::time_difference_prog  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::total_accum  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::trim  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::utilities::zpdate  %bld::tool::fflags -qfixed=132
+bld::tool::fflags::pp::verification  %bld::tool::fflags -qfixed=132
+bld::tool::cflags::pp::steps  -c -O0 -qarch=pwr6 -qtune=pwr6 -q64 -I/home/nwp/fr/ihab/fftw_opt/include/
+bld::tool::ld::pp::steps       xlC_r
+bld::tool::ldflags::pp::steps  -L/home/nwp/fr/ihab/fftw_opt/lib/ -lfftw3 -I/home/nwp/fr/ihab/fftw_opt/include/
+bld::tool::fflags::rst::src     -qarch=pwr6 -qtune=pwr6 -O0 -c -qfullpath -qextname -qrealsize=8 -qintsize=8  -qfixed=132
+bld::tool::fflags::rst::src::ReadFrcData -qarch=pwr6 -qtune=pwr6 -O0 -qstrict -c -qfullpath -qextname -qrealsize=8 -qintsize=8
+bld::tool::fflags::rst::src::profile -qarch=pwr6 -qtune=pwr6 -O0 -c -qfullpath -qextname -qrealsize=8 -qintsize=8  -qfixed=132 -qsmp=auto
+bld::tool::fflags::rst::src::MORST_main -qarch=pwr6 -qtune=pwr6 -O0 -c -qfullpath -qextname -qrealsize=8 -qintsize=8  -qfixed=132 -qsmp=omp
+bld::tool::fflags::rst::src::output_status -qarch=pwr6 -qtune=pwr6 -O0 -c -qfullpath -qextname
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+repos::pp::trunk                    svn://fcm9/PostProc_svn/PostProc/trunk
+repos::ancil::trunk                 svn://fcm9/PostProc_svn/PostProcAncil/trunk
+repos::site_ext::trunk              svn://fcm9/PostProc_svn/SiteExtract/trunk
+repos::gen::code                    svn://fcm1/GEN_svn/GEN/trunk/src/code
+repos::rst::trunk                   svn://fcm9/PostProc_svn/RoadTemp/trunk
+repos::ver::code                    svn://fcm6/VER_svn/VER/trunk/src/code
+repos::ops::code                    svn://fcm4/OPS_svn/OPS/trunk/src/code
+revision::pp::trunk         2728
+revision::ancil::trunk      2524
+revision::site_ext::trunk   2481
+revision::gen::code         3015
+revision::rst::trunk        2416
+revision::ver::code         4739
+revision::ops::code        18088
+
+src::pp::trunk                      cloud
+src::pp::trunk                      CDP
+src::pp::trunk                      frasia
+src::pp::trunk                      get_metdb_obs
+src::pp::trunk                      get_metdb_obs/MetDB_source
+src::pp::trunk                      model_processing
+src::pp::trunk                      precip
+src::pp::trunk                      precip_fcst
+src::pp::trunk                      pressure_wind
+src::pp::trunk                      product_gen
+src::pp::trunk                      scripts
+src::pp::trunk                      utilities
+src::pp::trunk                      verification
+src::pp::trunk                      visibility
+src::pp::trunk                      moses-pdm-rfm
+src::pp::trunk                      steps
+src::pp::trunk                      SCW
+src::site_ext::trunk                FssMod_DMO
+src::gen::code  		    GenMod_UMConstants
+src::gen::code                      UM_Platform
+src::gen::code                      UM_General
+src::gen::code                      UM_COEX
+src::gen::code                      Reconfiguration
+src::gen::code                      GenMod_Constants
+src::gen::code                      GenMod_Control
+src::gen::code                      GenMod_FortranIO
+src::gen::code                      GenMod_GetEnv
+src::gen::code                      GenMod_Platform
+src::gen::code                      GenMod_Reporting
+src::gen::code                      GenMod_Trace
+src::gen::code                      GenMod_Utilities
+src::ancil::trunk                   .
+src::ancil::trunk                   ./archive
+src::ancil::trunk                   ./steps_det
+src::ancil::trunk                   ./steps_ens
+src::rst::trunk                     src
+src::rst::trunk                     scripts
+src::ver::code                      VerMod_FieldsIO
+src::ver::code                      VerMod_General
+src::ver::code                      VerMod_Grid
+src::ops::code                      OpsMod_MOPS
+src::ops::code                      OpsMod_ObsInfo
+src::ops::code                      OpsMod_Constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_blockdata.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_blockdata.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_blockdata.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FPPKEYS::test/blockdata      ODD 
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_dependency.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_dependency.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_dependency.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FPPKEYS::test/program/hello 
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_include.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_include.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_include.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_pp_include
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_include_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_include_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_change_include_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_pp_include
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub 
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub 
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit_force.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit_force.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit_force.cfg	(revision 5095)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_base
+
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub 
+BLD::TOOL::LD                           wrap_ld2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_revmatch_false.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_revmatch_false.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_revmatch_false.cfg	(revision 5095)
@@ -0,0 +1,13 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/modify_files_merge1
+REPOS::test::branch3                    fcm:test_suite_br/dev/Share/modify_files_merge2
+
+REVISION::test::base                    21
+REVISION::test::branch1                 21
+REVISION::test::branch2                 21
+REVISION::test::branch3                 21
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_revmatch_true.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_revmatch_true.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_revmatch_true.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_revmatch_false.cfg
+
+REVMATCH                                true
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_sps.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_sps.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_sps.cfg	(revision 5095)
@@ -0,0 +1,129 @@
+# ------------------------------------------------------------------------------
+# File header
+# ------------------------------------------------------------------------------
+
+CFG::TYPE                                                                 ext
+CFG::VERSION                                                              1.0
+
+# ------------------------------------------------------------------------------
+# Destination
+# ------------------------------------------------------------------------------
+
+DEST                                                                      $PWD
+
+# ------------------------------------------------------------------------------
+# Build declarations
+# ------------------------------------------------------------------------------
+
+%FOPT -CB -traceback -u -convert big_endian 
+%SPS_LIBDIR /home/h04/cfsa/SPS/libraries/RHEL6
+bld::tool::fc      wrap_fc
+bld::tool::fflags  %FOPT -I%{SPS_LIBDIR}/grib_api/include
+bld::tool::cc      wrap_cc
+bld::tool::cflags  -Wall -O2 -DLOWERCASE -I%{SPS_LIBDIR}/hdf5/include
+bld::tool::ar      wrap_ar
+bld::tool::ld      wrap_ld
+bld::tool::ldflags -openmp -L%{SPS_LIBDIR}/hdf5/lib -lhdf5 -lhdf5_hl \
+                   -L%{SPS_LIBDIR}/bufr_ifort -lbufr \
+                   -L%{SPS_LIBDIR}/grib_ifort -lgrib_ifort \
+                   -L%{SPS_LIBDIR}/g2lib -lg2 \
+                   -L%{SPS_LIBDIR}/grib_api/lib -lgrib_api_f90 -lgrib_api \
+                   -L%{SPS_LIBDIR}/jasper/lib -ljasper \
+		   -L/usr/lib -llapack \
+                   -ljpeg -lpng
+bld::target   SpsScr_Install
+bld::target   sps__data__Sps_Fire.etc
+bld::target   sps__data__coeffs.etc
+bld::target   sps__data__palettes.etc
+bld::target   sps__data__products.etc
+bld::target   sps__data__sad.etc
+bld::target   sps__data__slotstore.etc
+bld::exe_name::h5admin  h5admin
+bld::exe_name::h5getatt h5getatt
+bld::exe_name::SpsProg_GetCoords sps_get_coords
+bld::excl_dep use::grib_api
+bld::tool::fflags::gen::src::code::GenMod_UMConstants             %FOPT -w -132
+bld::tool::cflags::sps::src::code::SpsMod_Image                   -Wall -DLOWERCASE -I%{SPS_LIBDIR}/hdf5/include
+bld::tool::fflags::sps::src::code::SpsMod_Utilities               %FOPT -Duse_f90_unix=''
+bld::tool::fflags::sps::src::code::SpsTask_HDFReader              %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsProg_ImageGrib              %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsProg_GlobalComposite        %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::rttov10                        %FOPT -openmp
+bld::tool::fflags::sps::src::code::rttov10::main::rttov_locpat_k.F90 %FOPT
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+repos::sps::base  fcm:sps_tr
+revision::sps::base   4964
+src::sps::base    src/code/SpsMod_AutosatFormat
+src::sps::base    src/code/SpsMod_Calibration
+src::sps::base    src/code/SpsMod_CloudContamination
+src::sps::base    src/code/SpsMod_CloudStoreTypes
+src::sps::base    src/code/SpsMod_Constants
+src::sps::base    src/code/SpsMod_Coordinates
+src::sps::base    src/code/SpsMod_FogParameters
+src::sps::base    src/code/SpsMod_GRIB
+src::sps::base    src/code/SpsMod_HDF
+src::sps::base    src/code/SpsMod_Image
+src::sps::base    src/code/SpsMod_InterpolateModelToPixel
+src::sps::base    src/code/SpsMod_LoadCloudMaskInfo
+src::sps::base    src/code/SpsMod_MPEFBufrDecode
+src::sps::base    src/code/SpsMod_NAME
+src::sps::base    src/code/SpsMod_NimrodFile
+src::sps::base    src/code/SpsMod_RTTOV
+src::sps::base    src/code/SpsMod_ScienceInterface
+src::sps::base    src/code/SpsMod_SdiSeg
+src::sps::base    src/code/SpsMod_Setup
+src::sps::base    src/code/SpsMod_SlotStoreGroups
+src::sps::base    src/code/SpsMod_Slotstore
+src::sps::base    src/code/SpsMod_Store
+src::sps::base    src/code/SpsMod_Utilities
+src::sps::base    src/code/SpsProg_ATDnetStrikes
+src::sps::base    src/code/SpsProg_BufrEncode_SEVIRI
+src::sps::base    src/code/SpsProg_Calibration
+src::sps::base    src/code/SpsProg_CloudProducts
+src::sps::base    src/code/SpsProg_Geometry
+src::sps::base    src/code/SpsProg_ImageGen
+src::sps::base    src/code/SpsProg_ImageGrib
+src::sps::base    src/code/SpsProg_MakeGRIB
+src::sps::base    src/code/SpsProg_ModelFieldsToHDF
+src::sps::base    src/code/SpsProg_MPEFBufrDecode
+src::sps::base    src/code/SpsProg_PreProcessCMAData
+src::sps::base    src/code/SpsProg_ProcessGOES_SA
+src::sps::base    src/code/SpsProg_RunRTTOV
+src::sps::base    src/code/SpsProg_GlobalComposite
+src::sps::base    src/code/SpsTask_Alpha
+src::sps::base    src/code/SpsTask_CalcParallaxErrors
+src::sps::base    src/code/SpsTask_CloudMask
+src::sps::base    src/code/SpsTask_CTHProcessing
+src::sps::base    src/code/SpsTask_Dust
+src::sps::base    src/code/SpsTask_Fog
+src::sps::base    src/code/SpsTask_HDFReader
+src::sps::base    src/code/SpsTask_MetOGII
+src::sps::base    src/code/SpsTask_PrecipIndex
+src::sps::base    src/code/SpsTask_SatPrecip
+src::sps::base    src/code/SpsTask_TauRe
+src::sps::base    src/code/SpsTask_VolcanicAsh
+src::sps::base    src/code/SpsTask_VolcanicSO2
+src::sps::base    src/code/Sps_MSGData
+src::sps::base    src/code/Sps_SlotstoreUtilities
+src::sps::base    src/code/Sps_Utils
+expsrc::sps::base src/code/rttov10
+src::sps::base    src/idl/Sps_Generic
+src::sps::base    src/idl/Sps_FireDetection
+src::sps::base    src/idl/Sps_MSGAOD
+src::sps::base    src/idl/Sps_MODISAOD
+src::sps::base    src/scripts/Sps_Perl
+src::sps::base    src/scripts/Sps_Scripts
+src::sps::base    src/scripts/Sps_Slotstore
+src::sps::base    src/scripts/Sps_Utils
+expsrc::sps::base src/scripts/sched
+expsrc::sps::base src/scripts/lib
+expsrc::sps::base data
+expsrc::sps::base control
+
+repos::gen::base  fcm:gen_tr
+revision::gen::base   3953
+src::gen::base    src/code/GenMod_UMConstants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_suite.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_suite.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_suite.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+BLD::SRC::test                          $RUN_DIR/fcm1_base/bin
+
+BLD::TARGET                             hello.sh
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_symbolic_link.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_symbolic_link.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_symbolic_link.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/symbolic_link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um.cfg	(revision 5095)
@@ -0,0 +1,54 @@
+# ------------------------------------------------------------------------------
+# File header
+# ------------------------------------------------------------------------------
+
+CFG::TYPE                                                              ext
+CFG::VERSION                                                           1.0
+
+# ------------------------------------------------------------------------------
+# Destination
+# ------------------------------------------------------------------------------
+
+DEST                                                                   $PWD
+
+# ------------------------------------------------------------------------------
+# Build declarations
+# ------------------------------------------------------------------------------
+
+bld::blockdata                                                            blkdata.o
+bld::excl_dep                                                             USE::NetCDF
+bld::excl_dep                                                             INC::netcdf.inc
+bld::excl_dep                                                             INC::mpif.h
+bld::excl_dep                                                             USE::mpl
+bld::excl_dep                                                             USE::mod_prism_proto
+bld::excl_dep                                                             USE::mod_prism_grids_writing
+bld::excl_dep                                                             USE::mod_prism_def_partition_proto
+bld::excl_dep                                                             USE::mod_prism_put_proto
+bld::excl_dep                                                             USE::mod_prism_get_proto
+bld::excl_dep::UM::script                                                 EXE
+bld::exe_dep                                                              portio2a.o pio_data_conv.o pio_io_timer.o
+bld::exe_name::flumeMain                                                  um.exe
+bld::pp::UM                                                               1
+bld::target                                                               um.exe
+bld::tool::ar                                                             ar
+bld::tool::cc                                                             wrap_cc
+bld::tool::cpp                                                            wrap_mpicc
+bld::tool::cppflags                                                       -E
+bld::tool::cppkeys                                                        C_LONG_LONG_INT=c_long_long_int MPP=mpp C_LOW_U=c_low_u FRL8=frl8 LINUX=linux BUFRD_IO=bufrd_io LITTLE_END=little_end LINUX_INTEL_COMPILER=linux_intel_compiler CONTROL=control REPROD=reprod ATMOS=atmos GLOBAL=global A04_ALL=a04_all A01_3C=a01_3c A02_3C=a02_3c A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a A06_4A=a06_4a A08_7A=a08_7a A09_2A=a09_2a A10_2A=a10_2a A11_2A=a11_2a A12_2A=a12_2a A13_2A=a13_2a A14_1B=a14_1b A15_1A=a15_1a A16_1A=a16_1a A17_2B=a17_2b A18_0A=a18_0a A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a A30_1A=a30_1a A31_0A=a31_0a A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a A35_0A=a35_0a A38_0A=a38_0a A70_1C=a70_1c A71_1A=a71_1a C70_1A=c70_1a C72_0A=c72_0a C80_1A=c80_1a C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a C94_1A=c94_1a C95_2A=c95_2a C96_1C=c96_1c C97_3A=c97_3a
+bld::tool::fc                                                             wrap_mpif90
+bld::tool::fflags                                                         -i8 -r8 -w -I /home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/inc -O0
+bld::tool::fppflags                                                       -E -P -traditional -I /home/h04/opsrc/ops0/mpi/mpich2-1.4-ukmo-v1/ifort-12/include
+bld::tool::fppkeys                                                        C_LONG_LONG_INT=c_long_long_int MPP=mpp C_LOW_U=c_low_u FRL8=frl8 LINUX=linux BUFRD_IO=bufrd_io LITTLE_END=little_end LINUX_INTEL_COMPILER=linux_intel_compiler CONTROL=control REPROD=reprod ATMOS=atmos GLOBAL=global A04_ALL=a04_all A01_3C=a01_3c A02_3C=a02_3c A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a A06_4A=a06_4a A08_7A=a08_7a A09_2A=a09_2a A10_2A=a10_2a A11_2A=a11_2a A12_2A=a12_2a A13_2A=a13_2a A14_1B=a14_1b A15_1A=a15_1a A16_1A=a16_1a A17_2B=a17_2b A18_0A=a18_0a A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a A30_1A=a30_1a A31_0A=a31_0a A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a A35_0A=a35_0a A38_0A=a38_0a A70_1C=a70_1c A71_1A=a71_1a C70_1A=c70_1a C72_0A=c72_0a C80_1A=c80_1a C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a C94_1A=c94_1a C95_2A=c95_2a C96_1C=c96_1c C97_3A=c97_3a
+bld::tool::geninterface                                                   none
+bld::tool::ar                                                             wrap_ar
+bld::tool::ld                                                             wrap_mpif90
+bld::tool::ldflags                                                        -L/home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/lib -lgcom -Wl,--noinhibit-exec -Vaxlib
+bld::tool::fpp                                                            wrap_pp
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+repos::UM::base        fcm:um-tr/src/
+version::UM::base      vn7.3
+expsrc::UM::base
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_hpc.cfg	(revision 5095)
@@ -0,0 +1,60 @@
+# ------------------------------------------------------------------------------
+# File header
+# ------------------------------------------------------------------------------
+
+CFG::TYPE                                                              ext
+CFG::VERSION                                                           1.0
+
+# ------------------------------------------------------------------------------
+# Destination
+# ------------------------------------------------------------------------------
+
+DEST                                                                   $PWD
+RDEST                                                                  $THIS_RUN_DIR_HPC
+RDEST::MACHINE                                                         $HPC
+
+# ------------------------------------------------------------------------------
+# Build declarations
+# ------------------------------------------------------------------------------
+
+bld::blockdata                                                            blkdata.o
+bld::excl_dep                                                             USE::NetCDF
+bld::excl_dep                                                             INC::netcdf.inc
+bld::excl_dep                                                             INC::mpif.h
+bld::excl_dep                                                             USE::mpl
+bld::excl_dep                                                             USE::mod_prism_proto
+bld::excl_dep                                                             USE::mod_prism_grids_writing
+bld::excl_dep                                                             USE::mod_prism_def_partition_proto
+bld::excl_dep                                                             USE::mod_prism_put_proto
+bld::excl_dep                                                             USE::mod_prism_get_proto
+bld::excl_dep::UM::script                                                 EXE
+bld::exe_dep                                                              portio2a.o pio_data_conv.o pio_io_timer.o print_from_c.o
+bld::exe_name::flumeMain                                                  um.exe
+bld::pp::UM                                                               1
+bld::target                                                               um.exe
+bld::tool::ar                                                             ar
+bld::tool::cc                                                             xlc_r
+bld::tool::cpp                                                            xlc
+bld::tool::cppflags                                                       -E -C
+bld::tool::cppkeys                                                        C_LONG_INT=c_long_int MPP=mpp C_LOW_U=c_low_u FRL8=frl8 BUFRD_IO=bufrd_io VECTLIB=vectlib IBM=ibm CONTROL=control REPROD=reprod MPP=mpp ATMOS=atmos GLOBAL=global A04_ALL=a04_all A01_3C=a01_3c A02_3C=a02_3c A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a A06_4A=a06_4a A08_7A=a08_7a A09_2A=a09_2a A10_2A=a10_2a A11_2A=a11_2a A12_2A=a12_2a A13_2A=a13_2a A14_1B=a14_1b A15_1A=a15_1a A16_1A=a16_1a A17_2B=a17_2b A18_0A=a18_0a A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a A30_1A=a30_1a A31_0A=a31_0a A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a A35_0A=a35_0a A38_0A=a38_0a A70_1C=a70_1c A71_1A=a71_1a C70_1A=c70_1a C72_0A=c72_0a C80_1A=c80_1a C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a C94_1A=c94_1a C95_2A=c95_2a C96_1C=c96_1c C97_3A=c97_3a
+bld::tool::fc                                                             mpxlf90_r
+bld::tool::fflags                                                         -I/projects/um1/gcom/gcom3.3/meto_ibm_pwr6_mpp/inc -I/projects/um1/lib/netcdf3.20090102/include -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8 -NS32768 -O0
+bld::tool::fflags::UM::atmosphere::dynamics_advection::eta_vert_weights_e -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8  -O0 -NS32768
+bld::tool::fflags::UM::control::top_level::atm_step                       -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8  -O0 -NS32768
+bld::tool::fflags::UM::control::top_level::u_model                        -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8  -O0 -NS32768
+bld::tool::fpp                                                            cpp
+bld::tool::fppflags                                                       -E -P -traditional
+bld::tool::fppkeys                                                        C_LONG_INT=c_long_int MPP=mpp C_LOW_U=c_low_u FRL8=frl8 BUFRD_IO=bufrd_io VECTLIB=vectlib IBM=ibm CONTROL=control REPROD=reprod MPP=mpp ATMOS=atmos GLOBAL=global A04_ALL=a04_all A01_3C=a01_3c A02_3C=a02_3c A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a A06_4A=a06_4a A08_7A=a08_7a A09_2A=a09_2a A10_2A=a10_2a A11_2A=a11_2a A12_2A=a12_2a A13_2A=a13_2a A14_1B=a14_1b A15_1A=a15_1a A16_1A=a16_1a A17_2B=a17_2b A18_0A=a18_0a A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a A30_1A=a30_1a A31_0A=a31_0a A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a A35_0A=a35_0a A38_0A=a38_0a A70_1C=a70_1c A71_1A=a71_1a C70_1A=c70_1a C72_0A=c72_0a C80_1A=c80_1a C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a C94_1A=c94_1a C95_2A=c95_2a C96_1C=c96_1c C97_3A=c97_3a
+bld::tool::geninterface                                                   none
+bld::tool::ld                                                             mpxlf90_r
+bld::tool::ldflags                                                        -lmass -lmassvp6 -L/projects/um1/gcom/gcom3.3/meto_ibm_pwr6_mpp/lib -lgcom -L/projects/um1/lib -lgrib -lsig -L/projects/um1/lib/netcdf3.20090102/lib64 -lnetcdf 
+bld::tool::make                                                           gmake
+
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+repos::UM::base        fcm:um-tr/src/
+version::UM::base      vn7.3
+expsrc::UM::base
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_inherit.cfg	(revision 5095)
@@ -0,0 +1,49 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/fcm1_um
+
+repos::UM::branch1     fcm:um_br/dev/Share/VN7.3_hg3_dust_443/src
+version::UM::branch1   11858
+expsrc::UM::branch1    /
+repos::UM::branch2     fcm:um_br/dev/Share/VN7.3_hg3_ccw_precip/src
+version::UM::branch2   11857
+expsrc::UM::branch2    /
+repos::UM::branch3     fcm:um_br/dev/hadco/VN7.3_HG3_porting_lsp_fixes/src
+version::UM::branch3   12029
+expsrc::UM::branch3    /
+repos::UM::branch4     fcm:um_br/dev/hadco/VN7.3_pc2_qcl_gt_tiny/src
+version::UM::branch4   12142
+expsrc::UM::branch4    /
+repos::UM::branch5     fcm:um_br/dev/hadas/VN7.3_w_CAPE_diag/src
+version::UM::branch5   12012
+expsrc::UM::branch5    /
+repos::UM::branch7     fcm:um_br/dev/frlk/VN7.3_BLLEVS_fixes/src
+version::UM::branch7   13938
+expsrc::UM::branch7    /
+repos::UM::branch8     fcm:um_br/dev/frwm/VN7.3_Cu_Diag_Low_LCL/src
+version::UM::branch8   12011
+expsrc::UM::branch8    /
+repos::UM::branch9     fcm:um_br/dev/frwm/VN7.3_LimitCnvParcPert/src
+version::UM::branch9   12041
+expsrc::UM::branch9    /
+repos::UM::branch10     fcm:um_br/dev/hadip/VN7.3_ilp_moose/src
+version::UM::branch10   13314
+expsrc::UM::branch10    /
+repos::UM::branch11     fcm:um_br/dev/hadco/VN7.3_temp_fix_solver/src
+version::UM::branch11   12740
+expsrc::UM::branch11    /
+repos::UM::branch12     fcm:um_br/dev/hadng/VN7.3_wetlands_rothc/src
+version::UM::branch12   12603
+expsrc::UM::branch12    /
+repos::UM::branch13     fcm:um_br/dev/frtg/VN7.3_reconf_extern_ancil/src
+version::UM::branch13   13063
+expsrc::UM::branch13    /
+repos::UM::branch15     fcm:um_br/dev/frma/VN7.3_rad_dev/src
+version::UM::branch15   12732
+expsrc::UM::branch15    /
+repos::UM::branch16     fcm:um_br/dev/frlk/VN7.3_melt_fix/src
+version::UM::branch16   13822
+expsrc::UM::branch16    /
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_inherit_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_inherit_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_um_inherit_hpc.cfg	(revision 5095)
@@ -0,0 +1,51 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+RDEST                                   $THIS_RUN_DIR_HPC
+RDEST::MACHINE                          $HPC
+
+USE                                     $RUN_DIR/fcm1_um_hpc
+
+repos::UM::branch1     fcm:um_br/dev/Share/VN7.3_hg3_dust_443/src
+version::UM::branch1   11858
+expsrc::UM::branch1    /
+repos::UM::branch2     fcm:um_br/dev/Share/VN7.3_hg3_ccw_precip/src
+version::UM::branch2   11857
+expsrc::UM::branch2    /
+repos::UM::branch3     fcm:um_br/dev/hadco/VN7.3_HG3_porting_lsp_fixes/src
+version::UM::branch3   12029
+expsrc::UM::branch3    /
+repos::UM::branch4     fcm:um_br/dev/hadco/VN7.3_pc2_qcl_gt_tiny/src
+version::UM::branch4   12142
+expsrc::UM::branch4    /
+repos::UM::branch5     fcm:um_br/dev/hadas/VN7.3_w_CAPE_diag/src
+version::UM::branch5   12012
+expsrc::UM::branch5    /
+repos::UM::branch7     fcm:um_br/dev/frlk/VN7.3_BLLEVS_fixes/src
+version::UM::branch7   13938
+expsrc::UM::branch7    /
+repos::UM::branch8     fcm:um_br/dev/frwm/VN7.3_Cu_Diag_Low_LCL/src
+version::UM::branch8   12011
+expsrc::UM::branch8    /
+repos::UM::branch9     fcm:um_br/dev/frwm/VN7.3_LimitCnvParcPert/src
+version::UM::branch9   12041
+expsrc::UM::branch9    /
+repos::UM::branch10     fcm:um_br/dev/hadip/VN7.3_ilp_moose/src
+version::UM::branch10   13314
+expsrc::UM::branch10    /
+repos::UM::branch11     fcm:um_br/dev/hadco/VN7.3_temp_fix_solver/src
+version::UM::branch11   12740
+expsrc::UM::branch11    /
+repos::UM::branch12     fcm:um_br/dev/hadng/VN7.3_wetlands_rothc/src
+version::UM::branch12   12603
+expsrc::UM::branch12    /
+repos::UM::branch13     fcm:um_br/dev/frtg/VN7.3_reconf_extern_ancil/src
+version::UM::branch13   13063
+expsrc::UM::branch13    /
+repos::UM::branch15     fcm:um_br/dev/frma/VN7.3_rad_dev/src
+version::UM::branch15   12732
+expsrc::UM::branch15    /
+repos::UM::branch16     fcm:um_br/dev/frlk/VN7.3_melt_fix/src
+version::UM::branch16   13822
+expsrc::UM::branch16    /
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_var.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_var.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_var.cfg	(revision 5095)
@@ -0,0 +1,232 @@
+# ------------------------------------------------------------------------------
+# File header
+# ------------------------------------------------------------------------------
+
+CFG::TYPE                                                               ext
+CFG::VERSION                                                            1.0
+
+# ------------------------------------------------------------------------------
+# Destination
+# ------------------------------------------------------------------------------
+
+DEST                                                                    $PWD
+
+# ------------------------------------------------------------------------------
+# Build declarations
+# ------------------------------------------------------------------------------
+
+bld::excl_dep                                                           USE::F90_UNIX_IO
+bld::excl_dep                                                           USE::XLFUTILITY
+bld::excl_dep                                                           INC::mpif.h
+bld::exe_dep                                                            gcom varadmin::src::code::VarMod_Lapack varadmin::src::code::VarMod_Blas
+bld::pp::gcom                                                           1
+bld::pp::var::src::code::PF_MPP                                         1
+bld::target                                                             VarScr_HelpCompile
+bld::tool::cc                                                           wrap_cc
+bld::tool::cflags                                                       
+bld::tool::cppkeys                                                      
+bld::tool::cppkeys::gen::src::code::GenMod_Platform                     LOWERCASE  UNDERSCORE FRL8       C_LONG_LONG_INT
+bld::tool::cppkeys::gen::src::code::UM_Platform                         VAROPSVER       C_LOW_U         FRL8            C_LONG_LONG_INT LINUX           LITTLE_END
+bld::tool::fc                                                           wrap_fc
+bld::tool::fflags                                                       -implicitnone -integer_size 64 -real_size 64 -ftrapuv
+bld::tool::fflags::gcom                                                 -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none
+bld::tool::fflags::gcom::build::mpl::mpl                                -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none -I$(OPSDIR)/mpi/mpich2-1.4-ukmo-v1/ifort-12/include
+bld::tool::fflags::gen                                                  -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn noerrors
+bld::tool::fflags::ops::src::code::Ops_RTTOV9                           -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none
+bld::tool::fflags::var::src::code::PF_Interpolation::Cubic_Lagrange_Adj -implicitnone -integer_size 64 -real_size 64 -ftrapuv -Wp,-P
+bld::tool::fflags::var::src::code::PF_MPP                               -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn noerrors
+bld::tool::fflags::var::src::code::VarProg_UMFileUtils                  -implicitnone -integer_size 64 -real_size 64
+bld::tool::fflags::varadmin::src::code::VarMod_Blas                     -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none
+bld::tool::fflags::varadmin::src::code::VarMod_Lapack                   -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none
+bld::tool::fppkeys                                                       IFORT_CDIRS
+bld::tool::fppkeys::gcom::build                                         GC_VERSION="'3.4+'" GC_BUILD_DATE="'15824'" PREC_64B GC__FLUSHUNIT6 GC__FORTERRUNIT=0 GC_DESCRIP="'MPP'" MPI_SRC MPILIB_32B
+bld::tool::fppkeys::gen::src::code::GenMod_Control                      GCOMHEADERS
+bld::tool::fppkeys::gen::src::code::GenMod_Utilities::Gen_FlushUnit     USE_FLUSH
+bld::tool::fppkeys::gen::src::code::UM_COEX                             VAROPSVER
+bld::tool::fppkeys::gen::src::code::UM_Platform                         VAROPSVER
+bld::tool::ldflags                                                      -L$(OPSDIR)/mpi/mpich2-1.4-ukmo-v1/ifort-12/lib -lmpich -lmpl -lpthread
+bld::tool::make                                                         gmake
+
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_direct            1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_ad                1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_tl                1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_k                 1
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_ad     _RTTOV_PARALLEL_AD
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_direct _RTTOV_PARALLEL_DIRECT
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_k      _RTTOV_PARALLEL_K
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_tl     _RTTOV_PARALLEL_TL
+
+BLD::TOOL::LD                           wrap_ld
+BLD::TOOL::AR                           wrap_ar
+BLD::TOOL::FPP                          wrap_pp
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+REPOS::var::base                                                       svn://fcm5/VAR_svn/VAR/trunk
+REVISION::var::base                                                    14844
+SRC::var::base                                                         src/code/NewDynamics
+SRC::var::base                                                         src/code/PFMod_Model
+SRC::var::base                                                         src/code/PFMod_SV
+SRC::var::base                                                         src/code/PF_Bdy_Layer
+SRC::var::base                                                         src/code/PF_Control
+SRC::var::base                                                         src/code/PF_EulerianAdv
+SRC::var::base                                                         src/code/PF_FirstGuessRhoW
+SRC::var::base                                                         src/code/PF_GcrSolver
+SRC::var::base                                                         src/code/PF_General
+SRC::var::base                                                         src/code/PF_Helmholtz
+SRC::var::base                                                         src/code/PF_IO
+SRC::var::base                                                         src/code/PF_Interpolation
+SRC::var::base                                                         src/code/PF_MPP
+SRC::var::base                                                         src/code/PF_MPPUtil
+SRC::var::base                                                         src/code/PF_MoistPhys
+SRC::var::base                                                         src/code/PF_SemiLagrangianTheta
+SRC::var::base                                                         src/code/PF_SemiLagrangianUV
+SRC::var::base                                                         src/code/PF_UVTransformation
+SRC::var::base                                                         src/code/PF_Update
+SRC::var::base                                                         src/code/PF_convec
+SRC::var::base                                                         src/code/SG_Interpolation
+SRC::var::base                                                         src/code/VarMod_ATOVS
+SRC::var::base                                                         src/code/VarMod_ATOVSRad
+SRC::var::base                                                         src/code/VarMod_Aircraft
+SRC::var::base                                                         src/code/VarMod_BalanceTerms
+SRC::var::base                                                         src/code/VarMod_BgPenAndGrad
+SRC::var::base                                                         src/code/VarMod_CovCheckTp
+SRC::var::base                                                         src/code/VarMod_CovCodes
+SRC::var::base                                                         src/code/VarMod_CovCond
+SRC::var::base                                                         src/code/VarMod_CovHorizontal
+SRC::var::base                                                         src/code/VarMod_CovOptions
+SRC::var::base                                                         src/code/VarMod_CovSample
+SRC::var::base                                                         src/code/VarMod_CovSpectral
+SRC::var::base                                                         src/code/VarMod_CovSpectralData
+SRC::var::base                                                         src/code/VarMod_CovStatsIO
+SRC::var::base                                                         src/code/VarMod_CovVariances
+SRC::var::base                                                         src/code/VarMod_CovVertical
+SRC::var::base                                                         src/code/VarMod_CovVerticalData
+SRC::var::base                                                         src/code/VarMod_DelSquaredFFT
+SRC::var::base                                                         src/code/VarMod_Diagnostics
+SRC::var::base                                                         src/code/VarMod_FieldOutput
+SRC::var::base                                                         src/code/VarMod_Fourier
+SRC::var::base                                                         src/code/VarMod_GPSRO
+SRC::var::base                                                         src/code/VarMod_GeneralOptions
+SRC::var::base                                                         src/code/VarMod_GroundGPS
+SRC::var::base                                                         src/code/VarMod_HorizontalInterp
+SRC::var::base                                                         src/code/VarMod_HorizontalInterp_Adj
+SRC::var::base                                                         src/code/VarMod_InterpColumns
+SRC::var::base                                                         src/code/VarMod_InterpColumns_Adj
+SRC::var::base                                                         src/code/VarMod_LS
+SRC::var::base                                                         src/code/VarMod_LTinterp
+SRC::var::base                                                         src/code/VarMod_MOPS
+SRC::var::base                                                         src/code/VarMod_MPP
+SRC::var::base                                                         src/code/VarMod_Minimise
+SRC::var::base                                                         src/code/VarMod_ModelIO
+SRC::var::base                                                         src/code/VarMod_ObsControl
+SRC::var::base                                                         src/code/VarMod_ObsIO
+SRC::var::base                                                         src/code/VarMod_ObsInfo
+SRC::var::base                                                         src/code/VarMod_ObsOptions
+SRC::var::base                                                         src/code/VarMod_ObsUtility
+SRC::var::base                                                         src/code/VarMod_ObsUtility_Adj
+SRC::var::base                                                         src/code/VarMod_PF
+SRC::var::base                                                         src/code/VarMod_PFInfo
+SRC::var::base                                                         src/code/VarMod_PF_Adj
+SRC::var::base                                                         src/code/VarMod_Platform
+SRC::var::base                                                         src/code/VarMod_Precip
+SRC::var::base                                                         src/code/VarMod_PseudoOb
+SRC::var::base                                                         src/code/VarMod_QC
+SRC::var::base                                                         src/code/VarMod_Radar
+SRC::var::base                                                         src/code/VarMod_SBUV
+SRC::var::base                                                         src/code/VarMod_SSMI
+SRC::var::base                                                         src/code/VarMod_SatRad
+SRC::var::base                                                         src/code/VarMod_Satwind
+SRC::var::base                                                         src/code/VarMod_Scatwind
+SRC::var::base                                                         src/code/VarMod_Sonde
+SRC::var::base                                                         src/code/VarMod_Stats
+SRC::var::base                                                         src/code/VarMod_Surface
+SRC::var::base                                                         src/code/VarMod_TestCovNL
+SRC::var::base                                                         src/code/VarMod_TotalPenAndGrad
+SRC::var::base                                                         src/code/VarMod_TpTransform
+SRC::var::base                                                         src/code/VarMod_TransSpecH
+SRC::var::base                                                         src/code/VarMod_TransformInfo
+SRC::var::base                                                         src/code/VarMod_Transform_g
+SRC::var::base                                                         src/code/VarMod_Transform_h
+SRC::var::base                                                         src/code/VarMod_Transform_p
+SRC::var::base                                                         src/code/VarMod_Transform_v
+SRC::var::base                                                         src/code/VarMod_Transforms
+SRC::var::base                                                         src/code/VarMod_Trig
+SRC::var::base                                                         src/code/VarMod_UpPF
+SRC::var::base                                                         src/code/VarMod_UpPF_Adj
+SRC::var::base                                                         src/code/VarMod_UpTransform
+SRC::var::base                                                         src/code/VarMod_UpTransform_Adj
+SRC::var::base                                                         src/code/VarMod_VerticalInterp
+SRC::var::base                                                         src/code/VarMod_VerticalInterp_Adj
+SRC::var::base                                                         src/code/VarMod_Vis
+SRC::var::base                                                         src/code/VarMod_Vp
+SRC::var::base                                                         src/code/VarProg_AnalysePF
+SRC::var::base                                                         src/code/VarProg_CovAccStats
+SRC::var::base                                                         src/code/VarProg_CovPFstats
+SRC::var::base                                                         src/code/VarProg_SV
+SRC::var::base                                                         src/code/VarProg_TestCov
+SRC::var::base                                                         src/code/VarProg_TestPFModel
+SRC::var::base                                                         src/code/VarProg_UMFileUtils
+SRC::var::base                                                         src/code/Var_DiffOperators
+SRC::var::base                                                         src/code/Var_General
+SRC::var::base                                                         src/code/Var_Initialization
+SRC::var::base                                                         src/code/Var_Jc
+SRC::var::base                                                         src/code/Var_LAPACK
+SRC::var::base                                                         src/scripts/Var_Scripts
+
+REPOS::ops::base                                                       svn://fcm4/OPS_svn/OPS/trunk
+REVISION::ops::base                                                    18341
+SRC::ops::base                                                         src/code/OpsMod_Constants
+SRC::ops::base                                                         src/code/OpsMod_Control
+SRC::ops::base                                                         src/code/OpsMod_GeoIR
+SRC::ops::base                                                         src/code/OpsMod_ObsInfo
+SRC::ops::base                                                         src/code/OpsMod_RTTOV
+SRC::ops::base                                                         src/code/OpsMod_Sort
+SRC::ops::base                                                         src/code/OpsMod_Utilities
+SRC::ops::base                                                         src/code/OpsMod_Varobs
+SRC::ops::base                                                         src/code/OpsMod_VerticalInterp
+SRC::ops::base                                                         src/code/OpsMod_VisControl
+SRC::ops::base                                                         src/code/OpsProg_RTTOV9
+SRC::ops::base                                                         src/code/Ops_AIRS_1DVar
+SRC::ops::base                                                         src/code/Ops_AIRS_Utilities
+SRC::ops::base                                                         src/code/Ops_RTTOV7
+SRC::ops::base                                                         src/code/Ops_RTTOV7_RTTOVCLD
+SRC::ops::base                                                         src/code/Ops_RTTOV9
+SRC::ops::base                                                         src/code/Ops_SatRad_Info
+SRC::ops::base                                                         src/code/Ops_SatRad_Process
+SRC::ops::base                                                         src/code/Ops_SatRad_SetUp
+SRC::ops::base                                                         src/code/Ops_SatRad_Utilities
+
+REPOS::gen::base                                                       svn://fcm1/GEN_svn/GEN/trunk
+REVISION::gen::base                                                    3073
+SRC::gen::base                                                         src/code/GenMod_Constants
+SRC::gen::base                                                         src/code/GenMod_Control
+SRC::gen::base                                                         src/code/GenMod_FortranIO
+SRC::gen::base                                                         src/code/GenMod_GetEnv
+SRC::gen::base                                                         src/code/GenMod_ModelIO
+SRC::gen::base                                                         src/code/GenMod_Platform
+SRC::gen::base                                                         src/code/GenMod_Reporting
+SRC::gen::base                                                         src/code/GenMod_Trace
+SRC::gen::base                                                         src/code/GenMod_UMConstants
+SRC::gen::base                                                         src/code/GenMod_Utilities
+SRC::gen::base                                                         src/code/Reconfiguration
+SRC::gen::base                                                         src/code/UM_COEX
+SRC::gen::base                                                         src/code/UM_General
+SRC::gen::base                                                         src/code/UM_Platform
+
+REPOS::da::base                                                        svn://fcm5/DA_svn/DA/trunk
+REVISION::da::base                                                     258
+
+REPOS::varadmin::base                                                  svn://fcm5/VAR_svn/Admin/trunk
+REVISION::varadmin::base                                               14851
+SRC::varadmin::base                                                    src/code/VarMod_Blas
+SRC::varadmin::base                                                    src/code/VarMod_Lapack
+
+REPOS::gcom::base                                                      svn://fcm2/UM_svn/GCOM/branches/dev/ibmjb/r12957_2194_ralltoalle_out_of_order
+REVISION::gcom::base                                                   15824
+SRC::gcom::base                                                        build/gc
+SRC::gcom::base                                                        build/gcg
+SRC::gcom::base                                                        build/include
+SRC::gcom::base                                                        build/mpl
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_var_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_var_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm1_var_hpc.cfg	(revision 5095)
@@ -0,0 +1,238 @@
+# ------------------------------------------------------------------------------
+# File header
+# ------------------------------------------------------------------------------
+
+CFG::TYPE                                                              ext
+CFG::VERSION                                                           1.0
+
+# ------------------------------------------------------------------------------
+# Destination
+# ------------------------------------------------------------------------------
+
+DEST                                                                   $PWD
+RDEST                                                                  $THIS_RUN_DIR_HPC
+RDEST::MACHINE                                                         $HPC
+
+# ------------------------------------------------------------------------------
+# Build declarations
+# ------------------------------------------------------------------------------
+
+bld::excl_dep                                                          USE::F90_UNIX_IO
+bld::excl_dep                                                          USE::XLFUTILITY
+bld::excl_dep                                                          INC::mpif.h
+bld::exe_dep                                                           gcom varadmin::src::code::VarMod_Lapack varadmin::src::code::VarMod_Blas
+bld::pp::gcom                                                          1
+bld::target                                                            VarScr_HelpCompile
+bld::tool::cc                                                          xlc
+bld::tool::cppkeys                                                     LOWERCASE
+bld::tool::cppkeys::gen::src::code::GenMod_Platform                    LOWERCASE  FRL8       C_LONG_INT
+bld::tool::cppkeys::gen::src::code::UM_Platform                        VAROPSVER C_LOW     FRL8      C_LONG_INT
+bld::tool::fc                                                          mpxlf95_r
+bld::tool::fc_define                                                   -WF,-D
+bld::tool::fflags                                                      -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1
+bld::tool::fflags::var::src::code::PFMod_Model                         -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -NS1024
+bld::tool::fflags::gcom::build::gc                                     -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed
+bld::tool::fflags::gcom::build::gcg                                    -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed
+bld::tool::fflags::gcom::build::include                                -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed
+bld::tool::fflags::gen::src::code::GenMod_Reporting::GenMod_Reporting  -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -WF,-qfpp
+bld::tool::fflags::gen::src::code::GenMod_Utilities::Gen_FlushUnit     -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -WF,-qfpp
+bld::tool::fflags::gen::src::code::UM_COEX                             -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -WF,-qfpp
+bld::tool::fflags::gen::src::code::UM_General                          -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed
+bld::tool::fflags::gen::src::code::UM_Platform                         -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -WF,-qfpp
+bld::tool::fflags::gen::src::code::UM_Platform::IOERROR                -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -WF,-qfpp -qfree=f90
+bld::tool::fflags::var::src::code::VarMod_CovVertical                  -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qnoessl
+bld::tool::fflags::var::src::code::VarMod_CovVerticalData              -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qnoessl
+bld::tool::fflags::varadmin::src::code::VarMod_Blas                    -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -qrealsize=4 -qstrict
+bld::tool::fflags::varadmin::src::code::VarMod_Lapack                  -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -qrealsize=4 -qstrict
+bld::tool::fppkeys                                                     
+bld::tool::fppkeys::gcom::build                                        GC_VERSION="'3.4+'" GC_BUILD_DATE="'15824'" PREC_64B GC__FLUSHUNIT6 GC__FORTERRUNIT=0 MPI_SRC MPILIB_32B GC_DESCRIP="'MPP'" MPI_BSEND_BUFFER_SIZE=10240000 IBM
+bld::tool::fppkeys::gen::src::code::GenMod_Control                     GCOMHEADERS
+bld::tool::fppkeys::gen::src::code::GenMod_Control::Gen_SetupControl   USE_CUSTOM_SIGNAL_HANDLER AIX
+bld::tool::fppkeys::gen::src::code::GenMod_Reporting::GenMod_Reporting GEN_LEN_ERROR_OUT=134
+bld::tool::fppkeys::gen::src::code::GenMod_Utilities::Gen_FlushUnit    USE_FLUSH AIX
+bld::tool::fppkeys::gen::src::code::UM_COEX                            VAROPSVER
+bld::tool::fppkeys::gen::src::code::UM_Platform                        VAROPSVER
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9                         RTTOV_ARCH_VECTOR
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parkind1        DEFAULT_INTEGER_32BIT
+bld::tool::ldflags                                                     -L$(UMDIR)/lib -lsig -lessl -lmassvp6 -lmass
+bld::tool::make                                                        gmake
+
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_direct            1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_ad                1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_tl                1
+bld::pp::ops::src::code::Ops_RTTOV9::rttov9_parallel_k                 1
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_ad     _RTTOV_PARALLEL_AD
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_direct _RTTOV_PARALLEL_DIRECT
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_k      _RTTOV_PARALLEL_K
+bld::tool::fppkeys::ops::src::code::Ops_RTTOV9::rttov9_parallel_tl     _RTTOV_PARALLEL_TL
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+REPOS::var::base                                                       svn://fcm5/VAR_svn/VAR/trunk
+REVISION::var::base                                                    14844
+SRC::var::base                                                         src/code/NewDynamics
+SRC::var::base                                                         src/code/PFMod_Model
+SRC::var::base                                                         src/code/PFMod_SV
+SRC::var::base                                                         src/code/PF_Bdy_Layer
+SRC::var::base                                                         src/code/PF_Control
+SRC::var::base                                                         src/code/PF_EulerianAdv
+SRC::var::base                                                         src/code/PF_FirstGuessRhoW
+SRC::var::base                                                         src/code/PF_GcrSolver
+SRC::var::base                                                         src/code/PF_General
+SRC::var::base                                                         src/code/PF_Helmholtz
+SRC::var::base                                                         src/code/PF_IO
+SRC::var::base                                                         src/code/PF_Interpolation
+SRC::var::base                                                         src/code/PF_MPP
+SRC::var::base                                                         src/code/PF_MPPUtil
+SRC::var::base                                                         src/code/PF_MoistPhys
+SRC::var::base                                                         src/code/PF_SemiLagrangianTheta
+SRC::var::base                                                         src/code/PF_SemiLagrangianUV
+SRC::var::base                                                         src/code/PF_UVTransformation
+SRC::var::base                                                         src/code/PF_Update
+SRC::var::base                                                         src/code/PF_convec
+SRC::var::base                                                         src/code/SG_Interpolation
+SRC::var::base                                                         src/code/VarMod_ATOVS
+SRC::var::base                                                         src/code/VarMod_ATOVSRad
+SRC::var::base                                                         src/code/VarMod_Aircraft
+SRC::var::base                                                         src/code/VarMod_BalanceTerms
+SRC::var::base                                                         src/code/VarMod_BgPenAndGrad
+SRC::var::base                                                         src/code/VarMod_CovCheckTp
+SRC::var::base                                                         src/code/VarMod_CovCodes
+SRC::var::base                                                         src/code/VarMod_CovCond
+SRC::var::base                                                         src/code/VarMod_CovHorizontal
+SRC::var::base                                                         src/code/VarMod_CovOptions
+SRC::var::base                                                         src/code/VarMod_CovSample
+SRC::var::base                                                         src/code/VarMod_CovSpectral
+SRC::var::base                                                         src/code/VarMod_CovSpectralData
+SRC::var::base                                                         src/code/VarMod_CovStatsIO
+SRC::var::base                                                         src/code/VarMod_CovVariances
+SRC::var::base                                                         src/code/VarMod_CovVertical
+SRC::var::base                                                         src/code/VarMod_CovVerticalData
+SRC::var::base                                                         src/code/VarMod_DelSquaredFFT
+SRC::var::base                                                         src/code/VarMod_Diagnostics
+SRC::var::base                                                         src/code/VarMod_FieldOutput
+SRC::var::base                                                         src/code/VarMod_Fourier
+SRC::var::base                                                         src/code/VarMod_GPSRO
+SRC::var::base                                                         src/code/VarMod_GeneralOptions
+SRC::var::base                                                         src/code/VarMod_GroundGPS
+SRC::var::base                                                         src/code/VarMod_HorizontalInterp
+SRC::var::base                                                         src/code/VarMod_HorizontalInterp_Adj
+SRC::var::base                                                         src/code/VarMod_InterpColumns
+SRC::var::base                                                         src/code/VarMod_InterpColumns_Adj
+SRC::var::base                                                         src/code/VarMod_LS
+SRC::var::base                                                         src/code/VarMod_LTinterp
+SRC::var::base                                                         src/code/VarMod_MOPS
+SRC::var::base                                                         src/code/VarMod_MPP
+SRC::var::base                                                         src/code/VarMod_Minimise
+SRC::var::base                                                         src/code/VarMod_ModelIO
+SRC::var::base                                                         src/code/VarMod_ObsControl
+SRC::var::base                                                         src/code/VarMod_ObsIO
+SRC::var::base                                                         src/code/VarMod_ObsInfo
+SRC::var::base                                                         src/code/VarMod_ObsOptions
+SRC::var::base                                                         src/code/VarMod_ObsUtility
+SRC::var::base                                                         src/code/VarMod_ObsUtility_Adj
+SRC::var::base                                                         src/code/VarMod_PF
+SRC::var::base                                                         src/code/VarMod_PFInfo
+SRC::var::base                                                         src/code/VarMod_PF_Adj
+SRC::var::base                                                         src/code/VarMod_Platform
+SRC::var::base                                                         src/code/VarMod_Precip
+SRC::var::base                                                         src/code/VarMod_PseudoOb
+SRC::var::base                                                         src/code/VarMod_QC
+SRC::var::base                                                         src/code/VarMod_Radar
+SRC::var::base                                                         src/code/VarMod_SBUV
+SRC::var::base                                                         src/code/VarMod_SSMI
+SRC::var::base                                                         src/code/VarMod_SatRad
+SRC::var::base                                                         src/code/VarMod_Satwind
+SRC::var::base                                                         src/code/VarMod_Scatwind
+SRC::var::base                                                         src/code/VarMod_Sonde
+SRC::var::base                                                         src/code/VarMod_Stats
+SRC::var::base                                                         src/code/VarMod_Surface
+SRC::var::base                                                         src/code/VarMod_TestCovNL
+SRC::var::base                                                         src/code/VarMod_TotalPenAndGrad
+SRC::var::base                                                         src/code/VarMod_TpTransform
+SRC::var::base                                                         src/code/VarMod_TransSpecH
+SRC::var::base                                                         src/code/VarMod_TransformInfo
+SRC::var::base                                                         src/code/VarMod_Transform_g
+SRC::var::base                                                         src/code/VarMod_Transform_h
+SRC::var::base                                                         src/code/VarMod_Transform_p
+SRC::var::base                                                         src/code/VarMod_Transform_v
+SRC::var::base                                                         src/code/VarMod_Transforms
+SRC::var::base                                                         src/code/VarMod_Trig
+SRC::var::base                                                         src/code/VarMod_UpPF
+SRC::var::base                                                         src/code/VarMod_UpPF_Adj
+SRC::var::base                                                         src/code/VarMod_UpTransform
+SRC::var::base                                                         src/code/VarMod_UpTransform_Adj
+SRC::var::base                                                         src/code/VarMod_VerticalInterp
+SRC::var::base                                                         src/code/VarMod_VerticalInterp_Adj
+SRC::var::base                                                         src/code/VarMod_Vis
+SRC::var::base                                                         src/code/VarMod_Vp
+SRC::var::base                                                         src/code/VarProg_AnalysePF
+SRC::var::base                                                         src/code/VarProg_CovAccStats
+SRC::var::base                                                         src/code/VarProg_CovPFstats
+SRC::var::base                                                         src/code/VarProg_SV
+SRC::var::base                                                         src/code/VarProg_TestCov
+SRC::var::base                                                         src/code/VarProg_TestPFModel
+SRC::var::base                                                         src/code/VarProg_UMFileUtils
+SRC::var::base                                                         src/code/Var_DiffOperators
+SRC::var::base                                                         src/code/Var_General
+SRC::var::base                                                         src/code/Var_Initialization
+SRC::var::base                                                         src/code/Var_Jc
+SRC::var::base                                                         src/code/Var_LAPACK
+SRC::var::base                                                         src/scripts/Var_Scripts
+
+REPOS::ops::base                                                       svn://fcm4/OPS_svn/OPS/trunk
+REVISION::ops::base                                                    18341
+SRC::ops::base                                                         src/code/OpsMod_Constants
+SRC::ops::base                                                         src/code/OpsMod_Control
+SRC::ops::base                                                         src/code/OpsMod_GeoIR
+SRC::ops::base                                                         src/code/OpsMod_ObsInfo
+SRC::ops::base                                                         src/code/OpsMod_RTTOV
+SRC::ops::base                                                         src/code/OpsMod_Sort
+SRC::ops::base                                                         src/code/OpsMod_Utilities
+SRC::ops::base                                                         src/code/OpsMod_Varobs
+SRC::ops::base                                                         src/code/OpsMod_VerticalInterp
+SRC::ops::base                                                         src/code/OpsMod_VisControl
+SRC::ops::base                                                         src/code/OpsProg_RTTOV9
+SRC::ops::base                                                         src/code/Ops_AIRS_1DVar
+SRC::ops::base                                                         src/code/Ops_AIRS_Utilities
+SRC::ops::base                                                         src/code/Ops_RTTOV7
+SRC::ops::base                                                         src/code/Ops_RTTOV7_RTTOVCLD
+SRC::ops::base                                                         src/code/Ops_RTTOV9
+SRC::ops::base                                                         src/code/Ops_SatRad_Info
+SRC::ops::base                                                         src/code/Ops_SatRad_Process
+SRC::ops::base                                                         src/code/Ops_SatRad_SetUp
+SRC::ops::base                                                         src/code/Ops_SatRad_Utilities
+
+REPOS::gen::base                                                       svn://fcm1/GEN_svn/GEN/trunk
+REVISION::gen::base                                                    3073
+SRC::gen::base                                                         src/code/GenMod_Constants
+SRC::gen::base                                                         src/code/GenMod_Control
+SRC::gen::base                                                         src/code/GenMod_FortranIO
+SRC::gen::base                                                         src/code/GenMod_GetEnv
+SRC::gen::base                                                         src/code/GenMod_ModelIO
+SRC::gen::base                                                         src/code/GenMod_Platform
+SRC::gen::base                                                         src/code/GenMod_Reporting
+SRC::gen::base                                                         src/code/GenMod_Trace
+SRC::gen::base                                                         src/code/GenMod_UMConstants
+SRC::gen::base                                                         src/code/GenMod_Utilities
+SRC::gen::base                                                         src/code/Reconfiguration
+SRC::gen::base                                                         src/code/UM_COEX
+SRC::gen::base                                                         src/code/UM_General
+SRC::gen::base                                                         src/code/UM_Platform
+
+REPOS::da::base                                                        svn://fcm5/DA_svn/DA/trunk
+REVISION::da::base                                                     258
+
+REPOS::varadmin::base                                                  svn://fcm5/VAR_svn/Admin/trunk
+REVISION::varadmin::base                                               14851
+SRC::varadmin::base                                                    src/code/VarMod_Blas
+SRC::varadmin::base                                                    src/code/VarMod_Lapack
+
+REPOS::gcom::base                                                      svn://fcm2/UM_svn/GCOM/branches/dev/ibmjb/r12957_2194_ralltoalle_out_of_order
+REVISION::gcom::base                                                   15824
+SRC::gcom::base                                                        build/gc
+SRC::gcom::base                                                        build/gcg
+SRC::gcom::base                                                        build/include
+SRC::gcom::base                                                        build/mpl
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_directory_expsrc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_directory_expsrc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_directory_expsrc.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/add_directory
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_file.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/add_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_add_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/add_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base_inc.cfg
+
+build.target = hello.sh test_suite/namelist/.etc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base_inc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base_inc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base_inc.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+steps = extract preprocess build
+
+extract.ns = test_suite
+extract.path-excl[test_suite] = cfg
+
+include = $HERE/fcm2_base_inc2.cfg
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base_inc2.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base_inc2.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_base_inc2.cfg	(revision 5095)
@@ -0,0 +1,15 @@
+preprocess.ns-excl = / test_suite/subroutine/hello_c.c
+preprocess.ns-incl = test_suite/subroutine test_suite/program
+preprocess.prop{fpp} = wrap_pp
+preprocess.prop{fpp.defs}[test_suite/subroutine/hello_sub.F90] = HELLO_SUB
+preprocess.prop{fpp.defs}[test_suite/program/hello.F90] = CALL_HELLO_SUB
+
+build.prop{file-ext.script} = .pro
+build.prop{fc} = wrap_fc
+$fcflags{?} = -assume nosource_include
+build.prop{ fc.flags } = $fcflags
+build.prop{fc.flags}[ test_suite/subroutine ] = $fcflags -O3
+build.prop {cc} = wrap_cc
+build.prop{cc.flags}=-O3
+build.prop{ar} = wrap_ar
+build.prop{dep.o.special} [test_suite/program] = hello_blockdata.o
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_clash.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_clash.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_clash.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = \
+    branches/dev/Share/modify_files_base \
+    branches/dev/Share/modify_files_clash
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/modify_files_merge2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_duplicate.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_duplicate.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_duplicate.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = \
+    branches/dev/Share/add_file      \
+    branches/dev/Share/add_lines     \
+    branches/dev/Share/add_lines
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_inherit.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/modify_files_merge2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_inherit_wrong_include.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_inherit_wrong_include.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_inherit_wrong_include.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/modify_files_merge2
+
+build.prop{fc.flags}[test_suite/module] = -O3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_wcopies.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_wcopies.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_wcopies.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = \
+    $BASE_DIR/work/b1 \
+    $BASE_DIR/work/b2 \
+    $BASE_DIR/work/b3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_wcopy.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_wcopy.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_branches_merge_wcopy.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    $BASE_DIR/work
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cflags.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cflags.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cflags.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{cc.flags}[test_suite/subroutine] = -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_change_variable.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_change_variable.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_change_variable.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+include = $HERE/fcm2_base.cfg
+
+$fcflags = -assume nosource_include -g
+build.prop{fc.flags}[test_suite/subroutine/hello_sub.F90] = $fcflags -O2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cyclic_dep_fail.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cyclic_dep_fail.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cyclic_dep_fail.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/cyclic_dep_fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cyclic_dep_ok.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cyclic_dep_ok.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_cyclic_dep_ok.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/cyclic_dep_ok
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_directory.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_directory.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_directory.cfg	(revision 5095)
@@ -0,0 +1,21 @@
+steps = extract preprocess build
+
+extract.ns = test_suite
+extract.path-excl[test_suite] = cfg
+extract.location{diff}[test_suite] =     \
+    branches/dev/Share/modify_files_base \
+    branches/dev/Share/delete_directory
+
+preprocess.ns-excl = / test_suite/subroutine/hello_c.c
+preprocess.ns-incl = test_suite/subroutine test_suite/program
+preprocess.prop{fpp} = wrap_pp
+
+build.prop{file-ext.script} = .pro
+build.prop{fc} = wrap_fc
+$fcflags{?} = -assume nosource_include
+build.prop{fc.flags} = $fcflags
+build.prop{cc} = wrap_cc
+build.prop{cc.flags} = -O3
+build.prop{ar} = wrap_ar
+build.prop{dep.o.special} = hello_blockdata.o
+build.target = hello.sh test_suite/namelist/.etc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_directory_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_directory_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_directory_inherit.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+use = $RUN_DIR/fcm2_base
+
+preprocess.prop{fpp.defs}[test_suite/program/hello.F90] =
+
+extract.location{diff}[test_suite] =     \
+    branches/dev/Share/modify_files_base \
+    branches/dev/Share/delete_directory
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_file.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/delete_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/delete_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/delete_inc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/delete_inc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file_inherit_force.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file_inherit_force.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_inc_file_inherit_force.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/delete_inc_file
+
+build.prop{fc.flags}[test_suite/module] = -assume nosource_include -O3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_pp_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_pp_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_pp_file.cfg	(revision 5095)
@@ -0,0 +1,24 @@
+steps = extract preprocess build
+
+extract.ns = test_suite
+extract.path-excl[test_suite] = cfg
+
+extract.location{diff}[test_suite] =     \
+    branches/dev/Share/modify_files_base \
+    branches/dev/Share/modify_pp_include \
+    branches/dev/Share/delete_pp_file
+
+preprocess.ns-excl = / test_suite/subroutine/hello_c.c
+preprocess.ns-incl = test_suite/subroutine test_suite/program
+preprocess.prop{fpp} = wrap_pp
+preprocess.prop{fpp.defs}[test_suite/program/hello.F90] = CALL_HELLO_SUB
+
+build.target{task} = link
+build.prop{fc} = wrap_fc
+$fcflags{?} = -assume nosource_include
+build.prop{fc.flags} = $fcflags
+build.prop{fc.flags}[test_suite/subroutine] = $fcflags -O3
+build.prop{cc} = wrap_cc
+build.prop{cc.flags} = -O3
+build.prop{ar} = wrap_ar
+build.prop{dep.o.special} = hello_blockdata.o
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_pp_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_pp_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_pp_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] =     \
+    branches/dev/Share/modify_files_base \
+    branches/dev/Share/modify_pp_include \
+    branches/dev/Share/delete_pp_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/delete_ppinc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/delete_ppinc_file
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file_inherit_force.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file_inherit_force.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_delete_ppinc_file_inherit_force.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/delete_ppinc_file
+
+preprocess.prop{fpp.defs}[test_suite/subroutine/hello_sub.F90] = HELLO_SUB DUMMY
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/f77_dep
+
+build.prop{dep.o} [test_suite/program] = hello_sub.o
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o_all.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o_all.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o_all.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/f77_dep
+
+build.prop{dep.o} = hello_sub.o
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o_invalid.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o_invalid.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_dep_o_invalid.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/f77_dep
+
+build.prop{dep.o} [test_suite/program] = invalid.o
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_duplicate_target.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_duplicate_target.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_duplicate_target.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/add_duplicate
+preprocess.prop{fpp.defs}[test_suite/subroutine/hello_sub2.F90] = HELLO_SUB
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exclude_dependency.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exclude_dependency.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exclude_dependency.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{no-dep.f.module} = hello_constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exe_permissions.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exe_permissions.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exe_permissions.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base_inc.cfg
+
+build.target = hello.sh test_suite/namelist/.etc
+build.target-rename = hello.exe:hello_world.exe
+
+extract.location{diff}[test_suite] = $BASE_DIR/work
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exe_rename.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exe_rename.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_exe_rename.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base_inc.cfg
+
+build.target = hello.sh test_suite/namelist/.etc
+build.target-rename = hello.exe:hello_world.exe
+
+extract.location{diff}[test_suite] = branches/dev/Share/exe_rename
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_extract_path_excl_no_ns.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_extract_path_excl_no_ns.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_extract_path_excl_no_ns.cfg	(revision 5095)
@@ -0,0 +1,9 @@
+steps = extract preprocess build
+
+extract.ns = test_suite
+extract.path-excl = cfg
+extract.path-incl[test_suite] = cfg/fcm2_base.cfg
+
+build.target = hello.sh test_suite/namelist/.etc
+
+include = $HERE/fcm2_base_inc2.cfg
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fc.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc} = wrap_fc2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags1.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags1.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags1.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.flags}[test_suite] = $fcflags -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags2.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags2.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags2.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.flags}[test_suite/subroutine/hello_sub.F90] = -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_fflags_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+build.prop{fc.flags}[test_suite/subroutine/hello_sub.F90] = -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_flag-output.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_flag-output.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_flag-output.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.flag-output} = -o %s
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inc_devnull.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inc_devnull.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inc_devnull.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base_inc.cfg
+
+# The UM makes use of the following so we have to support it
+include = /dev/null
+
+build.target = hello.sh test_suite/namelist/.etc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_invalid_path.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_invalid_path.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_invalid_path.cfg	(revision 5095)
@@ -0,0 +1,1 @@
+use = /invalid/path
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_redefine_fail.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_redefine_fail.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_redefine_fail.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+use = $RUN_DIR/fcm2_base
+
+include = $HERE/fcm2_base.cfg
+extract.path-excl[test_suite] = cfg namelist
+build.target = hello.sh
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_redefine_ok.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_redefine_ok.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_inherit_redefine_ok.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+include = $HERE/fcm2_base.cfg
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_base_url.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_base_url.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_base_url.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+include = $HERE/fcm2_base.cfg
+steps = extract
+
+extract.location{primary}[test_suite] = fcm:test_suite/invalid
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_branch_url.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_branch_url.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_branch_url.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+include = $HERE/fcm2_base.cfg
+steps = extract
+
+extract.location{diff}[test_suite] = branches/dev/Share/invalid
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_branch_url2.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_branch_url2.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_branch_url2.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+include = $HERE/fcm2_base.cfg
+steps = extract
+
+extract.location{diff}[test_suite] = /invalid/path
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_inc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_inc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_inc.cfg	(revision 5095)
@@ -0,0 +1,1 @@
+include = $HERE/invalid.cfg
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_label.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_label.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_label.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prep{fc.flags}[test_suite/subroutine/hello_sub.f90] = -O2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_modifier.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_modifier.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_modifier.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.flegs}[test_suite/subroutine/hello_sub.f90] = -O2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_modifiers.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_modifiers.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_modifiers.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.flags cc.flags}[test_suite/subroutine/hello_sub.f90] = -O2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_namespace.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_namespace.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_namespace.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.flags}[test_suite/invalid] = -O2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_namespace2.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_namespace2.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_namespace2.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+preprocess.ns-excl[test_suite] = /
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_target.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_target.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_target.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base_inc.cfg
+
+build.target = hello.sh invalid.sh
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_variable.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_variable.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_invalid_variable.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.flags}[test_suite/subroutine/hello_sub.f90] = $INVALID
+
+$INVALID = too late
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_library.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_library.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_library.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base_inc.cfg
+
+build.target = test_suite/libo.a
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_library_rename.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_library_rename.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_library_rename.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+include = $HERE/fcm2_base_inc.cfg
+
+build.target = mylib.a
+build.target-rename = test_suite/module/libo.a:mylib.a
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base.cfg
+
+steps = extract mirror
+
+mirror.target = localhost:${THIS_RUN_DIR}_mirror
+mirror.prop{config-file.steps} =  preprocess build
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_after_pp.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_after_pp.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_after_pp.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+include = $HERE/fcm2_base.cfg
+
+steps = extract preprocess mirror
+
+mirror.target = localhost:${THIS_RUN_DIR}_mirror
+mirror.prop{config-file.steps} =  build
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit.cfg	(revision 5095)
@@ -0,0 +1,8 @@
+use = $RUN_DIR/fcm2_mirror
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/modify_files_merge2
+
+mirror.target = localhost:${THIS_RUN_DIR}_mirror
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit_fflags.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit_fflags.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit_fflags.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+use = $RUN_DIR/fcm2_mirror
+
+mirror.target = localhost:${THIS_RUN_DIR}_mirror
+
+include = $HERE/fcm2_base_inc2.cfg
+build.prop{fc.flags}[test_suite] = $fcflags -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit_notarget.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit_notarget.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_mirror_inherit_notarget.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+use = $RUN_DIR/fcm2_mirror
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/modify_files_merge2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_modify_subroutine_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_modify_subroutine_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_modify_subroutine_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/modify_subroutine
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_modify_subroutine_interface_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_modify_subroutine_interface_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_modify_subroutine_interface_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/modify_subroutine_interface
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multi_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multi_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multi_inherit.cfg	(revision 5095)
@@ -0,0 +1,4 @@
+use = $RUN_DIR/fcm2_fflags_inherit
+use = $RUN_DIR/fcm2_modify_subroutine_inherit
+
+build.prop{fc.flags}[test_suite/module] = -O2 -g
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_build.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_build.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_build.cfg	(revision 5095)
@@ -0,0 +1,16 @@
+include = $HERE/fcm2_base.cfg
+
+step.class[build2]      = build
+
+steps = extract preprocess build build2
+
+build2.prop{file-ext.script} = .pro
+build2.prop{fc} = wrap_fc
+build2.prop{fc.flags} = $fcflags
+build2.prop{fc.flags}[test_suite/subroutine] = $fcflags -O3
+build2.prop{cc} = wrap_cc
+build2.prop{cc.flags} = -O3
+build2.prop{ar} = wrap_ar
+build2.prop{dep.o.special}[test_suite/program] = hello_blockdata.o
+build2.prop{fc.defs}[test_suite/blockdata] = ODD
+build2.target = hello.sh
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_build_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_build_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_build_inherit.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+use = $RUN_DIR/fcm2_multiple_build
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/modify_files_merge2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_pp-build.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_pp-build.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_pp-build.cfg	(revision 5095)
@@ -0,0 +1,25 @@
+include = $HERE/fcm2_base.cfg
+
+step.class[preprocess2] = preprocess
+step.class[build2]      = build
+
+steps = extract preprocess build preprocess2 build2
+
+preprocess2.prop{no-step-source} = preprocess
+preprocess2.ns-excl = / test_suite/subroutine/hello_c.c
+preprocess2.ns-incl = test_suite/subroutine test_suite/program
+preprocess2.prop{fpp} = wrap_pp
+preprocess2.prop{fpp.defs}[test_suite/subroutine/hello_sub.F90] = HELLO_SUB
+preprocess2.prop{fpp.defs}[test_suite/program/hello.F90] = CALL_HELLO_SUB
+
+build2.prop{no-step-source} = preprocess
+build2.prop{file-ext.script} = .pro
+build2.prop{fc} = wrap_fc
+build2.prop{fc.flags} = $fcflags
+build2.prop{fc.flags}[test_suite/subroutine] = $fcflags -O3
+build2.prop{cc} = wrap_cc
+build2.prop{cc.flags} = -O3
+build2.prop{ar} = wrap_ar
+build2.prop{dep.o.special}[test_suite/program] = hello_blockdata.o
+build2.prop{fc.defs}[test_suite/blockdata] = ODD
+build2.target = hello.sh
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_pp-build_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_pp-build_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_multiple_pp-build_inherit.cfg	(revision 5095)
@@ -0,0 +1,6 @@
+use = $RUN_DIR/fcm2_multiple_pp-build
+
+extract.location{diff}[test_suite] =       \
+    branches/dev/Share/modify_files_base   \
+    branches/dev/Share/modify_files_merge1 \
+    branches/dev/Share/modify_files_merge2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_no_dep.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_no_dep.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_no_dep.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{no-dep.bin} = *
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/f77_dep
+
+build.prop{ns-dep.o} = test_suite
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_all.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_all.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_all.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/f77_dep
+
+build.prop{ns-dep.o} = /
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_file.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/f77_dep
+
+build.prop{ns-dep.o} = test_suite/subroutine/hello_sub.F90
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_invalid.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_invalid.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ns-dep_o_invalid.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/f77_dep
+
+build.prop{ns-dep.o} = invalid test_suite
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ops.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ops.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_ops.cfg	(revision 5095)
@@ -0,0 +1,82 @@
+steps = extract preprocess build
+
+extract.ns = ens gcom gen ops ops_admin um_admin
+extract.location[ops] = trunk@19069
+extract.path-root[ops] = src
+extract.path-excl[ops] = config scripts/Ops_SatRad_IDL code/OpsMod_ODB
+
+extract.location[gen] = trunk@3194
+extract.path-root[gen] = src/code
+
+extract.location[ops_admin] = trunk@19069
+extract.path-root[ops_admin] = src/code
+
+extract.location[um_admin] = trunk@11210
+extract.path-root[um_admin] = utilities
+extract.path-excl[um_admin] = /
+extract.path-incl[um_admin] = IBM_signal_hander
+
+extract.location[gcom] = trunk@17285
+extract.path-root[gcom] = build
+extract.path-excl[gcom] = configs ext_scripts
+
+extract.location[ens] = trunk@1460
+extract.path-root[ens] = forecast/code
+extract.path-excl[ens] = ext.cfg ibm.ext.cfg lapack_eigen
+
+preprocess.ns-excl = ops gen ops_admin um_admin
+preprocess.ns-incl = var/code/PF_MPP \
+                   \ ops/code/OpsMod_Altimeter \
+                   \ ops/code/OpsMod_OceanSound \
+                   \ ops/code/OpsMod_Radar \
+                   \ ops/code/OpsMod_RadarZ \
+                   \ ops/code/OpsMod_SeaIce \
+                   \ ops/code/OpsMod_SurfaceSST \
+                   \ ops/code/OpsProg_ExtractAndProcess \
+                   \ ops/code/Ops_RTTOV9/rttov9_parallel_ad.F90 \
+                   \ ops/code/Ops_RTTOV9/rttov9_parallel_direct.F90 \
+                   \ ops/code/Ops_RTTOV9/rttov9_parallel_k.F90 \
+                   \ ops/code/Ops_RTTOV9/rttov9_parallel_tl.F90
+preprocess.prop{fpp} = wrap_pp
+preprocess.prop{fpp.defs}[gcom] = GC_VERSION="'3.4'" GC_DESCRIP="'MPP'" GC_BUILD_DATE="'17285'" MPI_SRC MPILIB_32B PREC_64B GC__FORTERRUNIT=0 GC__FLUSHUNIT6 MPI_BSEND_BUFFER_SIZE=2560000
+preprocess.prop{fpp.defs}[ens]  = IBM
+preprocess.prop{fpp.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_ad.F90]     = _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_AD
+preprocess.prop{fpp.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_direct.F90] = _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_DIRECT
+preprocess.prop{fpp.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_k.F90]      = _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_K
+preprocess.prop{fpp.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_tl.F90]     = _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR _RTTOV_PARALLEL_TL
+preprocess.prop{cpp.defs}[ens]  = LOWERCASE
+
+$OPSDIR{?} = /home/h04/opsrc/ops0
+$mpich2 = $OPSDIR/mpi/mpich2-1.4-ukmo-v1/ifort-12
+build.target = OpsScr_Build EnsProg_ETKF.exe EnsProg_TrimObstore.exe
+build.prop{file-ext.script} = .sh
+build.prop{cc} = wrap_cc
+build.prop{cc.defs}[gen/GenMod_Platform]         = UNDERSCORE LOWERCASE C_LONG_LONG_INT FRL8
+build.prop{cc.defs}[gen/UM_Platform]             = VAROPSVER C_LOW_U LINUX LITTLE_END C_LONG_LONG_INT FRL8
+build.prop{cc.defs}[ops/code/MetDB_ClientServer] = hpux DEBUG LL64 UNDERSCORE
+build.prop{fc} = wrap_fc
+build.prop{fc.flags}                      = -implicitnone -stand f95 -warn all -warn nointerfaces -i8 -r8 -i-static
+build.prop{fc.flags}[gcom]                = -implicitnone -stand f95 -warn all -i8 -r8 -i-static -warn none -I$mpich2/include
+build.prop{fc.flags}[ops/code/Ops_RTTOV9] = -implicitnone -stand f95 -warn all -warn nointerfaces -i-static -O3
+build.prop{fc.defs}[gen/GenMod_Utilities]    = USE_FLUSH
+build.prop{fc.defs}[gen/UM_COEX]             = VAROPSVER
+build.prop{fc.defs}[gen/UM_Platform]         = VAROPSVER
+build.prop{fc.defs}[ops/code/MetDB_GRIB]     = SX6
+build.prop{fc.defs}[ops/code/OpsMod_Extract] = LITTLE_END
+build.prop{fc.defs}[ops_admin/MetDB_Bufr]    = BPATH
+build.prop{fc.defs}[ops/code/Ops_RTTOV9]     = _RTTOV_TSTRAD_TEMP RTTOV_ARCH_VECTOR
+build.prop{fc.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_ad.F90]     =
+build.prop{fc.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_direct.F90] =
+build.prop{fc.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_k.F90]      =
+build.prop{fc.defs}[ops/code/Ops_RTTOV9/rttov9_parallel_tl.F90]     =
+build.prop{fc.flags-ld}      = -i-static -L$mpich2/lib -lmpich -lmpl -lpthread
+build.prop{fc.flags-ld}[ens] = -i-static -L$mpich2/lib -lmpich -lmpl -lpthread -llapack
+build.prop{ns-dep.o}[ops/code/OpsProg_BackErrCreate]     = gcom
+build.prop{ns-dep.o}[ops/code/OpsProg_ExtractAndProcess] = ops_admin/MetDB_BUFR_RETRIEVAL/source ops_admin/MetDB_Bufr ops_admin/lapack ops_admin/blas gcom
+build.prop{ns-dep.o}[ops/code/OpsProg_MOPS]              = ops_admin/MetDB_BUFR_RETRIEVAL/source ops_admin/MetDB_Bufr gcom
+build.prop{ns-dep.o}[ops/code/OpsProg_KillRPC]           = gcom
+build.prop{ns-dep.o}[ops/code/Ops_SatRad_Stats]          = gcom
+build.prop{ns-dep.o}[ens/EnsProg_ETKF]                   = gcom
+build.prop{ns-dep.o}[ens/EnsProg_TrimObstore]            = gcom
+build.prop{no-dep.f.module} = f90_unix_io xlfutility netcdf yomlun
+build.prop{no-dep.include}  = mpif.h
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_postproc_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_postproc_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_postproc_hpc.cfg	(revision 5095)
@@ -0,0 +1,171 @@
+steps = extract mirror
+
+mirror.target = $HPC:$THIS_RUN_DIR_HPC
+mirror.prop{config-file.steps} = preprocess build
+mirror.prop{rsync.flags} = -a --exclude='.*' --delete-excluded --timeout=900 --rsh='ssh -oBatchMode=yes' -v
+
+extract.ns = gen ops pp ppancil rst site_ext ver
+extract.location[pp] = trunk@2728
+extract.path-excl[pp] = /
+extract.path-incl[pp] = CDP \
+                      \ SCW \
+                      \ cloud \
+                      \ frasia \
+                      \ get_metdb_obs \
+                      \ model_processing \
+                      \ moses-pdm-rfm \
+                      \ precip \
+                      \ precip_fcst \
+                      \ pressure_wind \
+                      \ product_gen \
+                      \ scripts \
+                      \ steps \
+                      \ utilities \
+                      \ verification \
+                      \ visibility
+
+extract.location[ppancil] = trunk@2524
+extract.path-excl[ppancil] = web
+
+extract.location{primary}[site_ext] = svn://fcm9/PostProc_svn/SiteExtract
+extract.location[site_ext] = trunk@2481
+extract.path-root[site_ext] = FssMod_DMO
+
+extract.location[gen] = trunk@3015
+extract.path-root[gen] = src/code
+extract.path-excl[gen] = GenMod_ModelIO
+
+extract.location{primary}[rst] = svn://fcm9/PostProc_svn/RoadTemp
+extract.location[rst] = trunk@2416
+extract.path-excl[rst] = /
+extract.path-incl[rst] = src scripts
+
+extract.location[ver] = trunk@4739
+extract.path-root[ver] = src/code
+extract.path-excl[ver] = /
+extract.path-incl[ver] = VerMod_FieldsIO VerMod_General VerMod_Grid
+
+extract.location[ops] = trunk@18088
+extract.path-root[ops] = src/code
+extract.path-excl[ops] = /
+extract.path-incl[ops] = OpsMod_MOPS OpsMod_ObsInfo OpsMod_Constants
+
+preprocess.ns-excl = pp ppancil site_ext rst ops
+preprocess.ns-incl = pp/utilities pp/steps pp/get_metdb_obs/MetDB_source rst/src
+preprocess.prop{cpp} = xlC_r
+preprocess.prop{cpp.flags} = -E -C
+preprocess.prop{cpp.defs}[pp/get_metdb_obs/MetDB_source] = LOWERCASE L64 UNDERSCORE
+preprocess.prop{cpp.defs}[gen] = C_LOW_U FRL8 C_LONG_INT NEC VAROPSVER UTILIO UNDERSCORE LOWERCASE
+preprocess.prop{fpp.flags} = -E -P -traditional
+preprocess.prop{fpp.defs}[gen] = C_LOW_U FRL8 C_LONG_INT NEC VAROPSVER UTILIO UNDERSCORE UPPERCASE
+preprocess.prop{fpp.defs}[rst] = NEC
+preprocess.prop{no-dep.include}  = gc_constants.h gc_kinds.h gc_options.h
+
+build.target = PPQYINTERP.ksh run_nae_pp.ksh run_qv_downscaling.ksh run_glo_pp.ksh run_EuroPP.ksh first_start.ksh PP4KOPER.ksh
+build.prop{cc} = xlC_r
+build.prop{fc} = xlf90_r
+build.prop{no-dep.f.module} = f90_unix_io
+build.prop{file-ext.script} = .ksh
+build.prop{dep.o}[pp/get_metdb_obs/get_metdb_obs.f90] = pout_i8.o
+build.prop{dep.o}[pp/precip/lightning_merge.f] = get_free_lun.o
+build.prop{dep.o}[pp/precip/mask_radar.f90] = nimrod_open.o nimrod_header.o
+build.prop{cc.flags-ld}[pp/steps]     = -L/home/nwp/fr/ihab/fftw_opt/lib/ -lfftw3 -I/home/nwp/fr/ihab/fftw_opt/include/
+build.prop{cc.flags}                  = -qarch=pwr6 -qtune=pwr6 -O0 -qhot
+build.prop{cc.flags}[gen]             = -qarch=pwr6 -qtune=pwr6 -O0 -qhot -qrealsize=8 -qintsize=8
+build.prop{cc.flags}[pp/get_metdb_obs/MetDB_source] =
+build.prop{cc.flags}[pp/steps]        = -O0 -qarch=pwr6 -qtune=pwr6 -q64 -I/home/nwp/fr/ihab/fftw_opt/include/
+build.prop{fc.flags-ld}[rst]          = -L/projects/um1/gcom/gcom3.2/meto_ibm_pwr6_serial/lib -lgcom -bnoquiet -L/projects/um1/lib -lsig -L/usr/lib -lmass -lessl -qsmp
+build.prop{fc.flags-ld}[pp/model_processing/format.f90] = -L/projects/um1/gcom/gcom3.2/meto_ibm_pwr6_serial/lib -lgcom
+build.prop{fc.flags}                  = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[rst ver ops]     = -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname -qrealsize=8 -qintsize=8
+build.prop{fc.flags}[gen]             = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qrealsize=8 -qintsize=8
+build.prop{fc.flags}[gen/UM_COEX]     = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qrealsize=8 -qintsize=8 -qfixed=132
+build.prop{fc.flags}[gen/UM_General]  = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qrealsize=8 -qintsize=8 -qfixed=132
+build.prop{fc.flags}[gen/UM_Platform] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qrealsize=8 -qintsize=8 -qfixed=132
+build.prop{fc.flags}[gen/UM_Platform/IOERROR.F90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qrealsize=8 -qintsize=8
+build.prop{fc.flags}[pp/get_metdb_obs/get_metdb_obs.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/model_processing/get_um_info.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict
+build.prop{fc.flags}[pp/moses-pdm-rfm/daynumber.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/dlongrad.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/down_rad_calc.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/dsolrad.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/get_row_and_column.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/initialise_routing.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/morangstrom.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/morloc.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/moses_cloud_cover.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/moses_qs_from_t.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nearest_real.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nextpoint.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nimrod_3d_idata_read.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nimrod_3dextr_comp.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nimrod_extr_comp.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nimrod_extr_wind.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nimrod_hdr_read.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/nimrod_idata_read.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/q_vp_from_t_td.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/read_ancil.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/regrid_real.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/route_runoff.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/routing.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/sam.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/ssdm.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/ssdm_var_generator.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/sun_angles.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/um_solpos.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/moses-pdm-rfm/wavespeed.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qstrict -qfixed=132
+build.prop{fc.flags}[pp/precip/get_surface_obs.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/precip/lightning_forecast.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/precip/lightning_merge.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/precip/metar_to_synop_weather.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/precip/read_adv_fc.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/precip/read_rad.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/precip_fcst] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/precip_fcst/accmerge.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/precip_fcst/object_motion.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/precip_fcst/scale.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/precip_fcst/wind_forecast_precip.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/pressure_wind] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/pressure_wind/an_smear.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/pressure_wind/bilin_mdi.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/pressure_wind/convert_winduv.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/pressure_wind/gust_adjust.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/pressure_wind/gust_analysis.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/pressure_wind/pwindanal.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname
+build.prop{fc.flags}[pp/utilities/beammap_ascii_to_nimrod.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/ccitt.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/datetime_c_to_i_secs.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/def_head.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/domain_to_ng.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/european_observations_area.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/get_free_lun.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/icutout.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/ll_to_ng.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/ll_to_ps.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/locate_FCST_string.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/nearest.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/nearest_file.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/ng_to_ll.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/ng_to_ll_array.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/nimrod_i4read.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/nimrod_open2.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/nimrod_open_i4read.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/nimrod_open_i4write.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/nimrod_regrid.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/observations_area_metdb.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/ps_to_ll.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/regrid.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/round_cycle_string.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/subtract_time.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/time_diff.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/time_difference_prog.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/total_accum.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/trim.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/utilities/zpdate.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[pp/verification] = -qarch=pwr6 -qtune=pwr6 -O0 -qextname -qfixed=132
+build.prop{fc.flags}[rst/src] = -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname -qrealsize=8 -qintsize=8  -qfixed=132
+build.prop{fc.flags}[rst/src/MORST_main.F] = -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname -qrealsize=8 -qintsize=8 -qfixed=132 -qsmp=omp
+build.prop{fc.flags}[rst/src/ReadFrcData.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qstrict -qfullpath -qextname -qrealsize=8 -qintsize=8
+build.prop{fc.flags}[rst/src/output_status.f90] = -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname
+build.prop{fc.flags}[rst/src/profile.f] = -qarch=pwr6 -qtune=pwr6 -O0 -qfullpath -qextname -qrealsize=8 -qintsize=8 -qfixed=132 -qsmp=auto
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_blockdata.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_blockdata.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_blockdata.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+build.prop{fc.defs}[test_suite/blockdata] = ODD
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_dependency.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_dependency.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_dependency.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+preprocess.prop{fpp.defs}[test_suite/program/hello.F90] =
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_include.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_include.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_include.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/modify_pp_include
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_include_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_include_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_change_include_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+extract.location{diff}[test_suite] = branches/dev/Share/modify_pp_include
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+preprocess.prop{fpp.defs}[test_suite/subroutine/hello_sub.F90] =
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine_inherit.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+use = $RUN_DIR/fcm2_base
+
+preprocess.prop{fpp.defs}[test_suite/subroutine/hello_sub.F90] =
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine_inherit_force.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine_inherit_force.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_pp_empty_subroutine_inherit_force.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+use = $RUN_DIR/fcm2_base
+
+preprocess.prop{fpp.defs}[test_suite/subroutine/hello_sub.F90] =
+
+build.prop{fc.flags}[test_suite/program] = -assume nosource_include -O2
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_revmatch_false.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_revmatch_false.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_revmatch_false.cfg	(revision 5095)
@@ -0,0 +1,7 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location[test_suite] = trunk@21
+extract.location{diff}[test_suite] =          \
+    branches/dev/Share/modify_files_base@21   \
+    branches/dev/Share/modify_files_merge1@21 \
+    branches/dev/Share/modify_files_merge2@21
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_single_file.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_single_file.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_single_file.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+preprocess.prop{fpp.defs}[test_suite/program/hello.F90] = LOCAL_STRING
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_space_in_name.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_space_in_name.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_space_in_name.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/space_in_name
+
+build.prop{fc.defs}["test_suite/block data/hello blockdata.F90"] = ODD
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_sps.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_sps.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_sps.cfg	(revision 5095)
@@ -0,0 +1,46 @@
+steps = extract build
+
+extract.ns = gen sps
+extract.location[sps] = trunk@4964
+extract.path-excl[sps] = doc src/config src/simim \
+                         src/code/SpsMod_ModelIngest \
+                         src/code/SpsProg_ConvertFieldsfile \
+                         src/code/SpsTask_ConvectiveInitiation \
+                         src/code/old \
+                         src/scripts/Sps_System \
+                         src/scripts/old
+
+extract.location[gen] = trunk@3953
+extract.path-root[gen] = src/code
+extract.path-excl[gen] = /
+extract.path-incl[gen] = GenMod_UMConstants
+
+build.target = SpsScr_Install sps/data/Sps_Fire/.etc sps/data/coeffs/.etc sps/data/palettes/.etc \
+               sps/data/products/.etc sps/data/sad/.etc sps/data/slotstore/.etc
+build.target-rename = h5admin.exe:h5admin h5getatt.exe:h5getatt SpsProg_GetCoords.exe:sps_get_coords
+build.prop{fc} = wrap_fc
+$FOPT = -CB -traceback -u -convert big_endian
+$SPS_LIBDIR = /home/h04/cfsa/SPS/libraries/RHEL6
+build.prop{fc.flags} = $FOPT -I${SPS_LIBDIR}/grib_api/include
+build.prop{fc.flags}[gen/GenMod_UMConstants] = $FOPT -w -132
+build.prop{fc.flags}[sps/src/code/SpsMod_Utilities]              = $FOPT -Duse_f90_unix=''
+build.prop{fc.flags}[sps/src/code/SpsTask_HDFReader]             = $FOPT -auto -assume byterecl
+build.prop{fc.flags}[sps/src/code/SpsProg_ImageGrib]             = $FOPT -auto -assume byterecl
+build.prop{fc.flags}[sps/src/code/SpsProg_GlobalComposite]       = $FOPT -auto -assume byterecl
+build.prop{fc.flags}[sps/src/code/rttov10]                       = $FOPT -openmp
+build.prop{fc.flags}[sps/src/code/rttov10/main/rttov_locpat_k.F90] = $FOPT
+build.prop{cc} = wrap_cc
+build.prop{cc.flags} = -Wall -O2 -DLOWERCASE -I${SPS_LIBDIR}/hdf5/include
+build.prop{cc.flags}[sps/src/code/SpsMod_Image]                  = -Wall -DLOWERCASE -I${SPS_LIBDIR}/hdf5/include
+build.prop{fc.flags-ld} = -openmp -L${SPS_LIBDIR}/hdf5/lib -lhdf5 -lhdf5_hl \
+                        \ -L${SPS_LIBDIR}/bufr_ifort -lbufr \
+                        \ -L${SPS_LIBDIR}/grib_ifort -lgrib_ifort \
+                        \ -L${SPS_LIBDIR}/g2lib -lg2 \
+                        \ -L${SPS_LIBDIR}/grib_api/lib -lgrib_api_f90 -lgrib_api \
+                        \ -L${SPS_LIBDIR}/jasper/lib -ljasper \
+		        \ -L/usr/lib -llapack \
+                        \ -ljpeg -lpng
+build.prop{dep.o}[sps/src/code/SpsMod_Store/SpsMod_Hdf5F90Support.c] = spsmod_storec.o
+build.prop{dep.include}[sps/src/code/SpsMod_Store/Sps_H5Fopen_auto.c] = Sps_H5Fopen_auto.h
+build.prop{no-dep.f.module} = grib_api
+build.prop{file-ext.script} = .ksh .pl .pm .pro
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_symbolic_link.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_symbolic_link.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_symbolic_link.cfg	(revision 5095)
@@ -0,0 +1,3 @@
+include = $HERE/fcm2_base.cfg
+
+extract.location{diff}[test_suite] = branches/dev/Share/symbolic_link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um.cfg	(revision 5095)
@@ -0,0 +1,36 @@
+steps = extract preprocess build
+
+extract.ns = um
+extract.location[um] = trunk@vn7.3
+extract.path-root[um] = src
+
+$keys_model = C_LONG_LONG_INT=c_long_long_int MPP=mpp C_LOW_U=c_low_u \
+    \ FRL8=frl8 LINUX=linux BUFRD_IO=bufrd_io LITTLE_END=little_end \
+    \ LINUX_INTEL_COMPILER=linux_intel_compiler CONTROL=control \
+    \ REPROD=reprod ATMOS=atmos GLOBAL=global A04_ALL=a04_all \
+    \ A01_3C=a01_3c A02_3C=a02_3c A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a \
+    \ A06_4A=a06_4a A08_7A=a08_7a A09_2A=a09_2a A10_2A=a10_2a A11_2A=a11_2a \
+    \ A12_2A=a12_2a A13_2A=a13_2a A14_1B=a14_1b A15_1A=a15_1a A16_1A=a16_1a \
+    \ A17_2B=a17_2b A18_0A=a18_0a A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a \
+    \ A30_1A=a30_1a A31_0A=a31_0a A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a \
+    \ A35_0A=a35_0a A38_0A=a38_0a A70_1C=a70_1c A71_1A=a71_1a C70_1A=c70_1a \
+    \ C72_0A=c72_0a C80_1A=c80_1a C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a \
+    \ C94_1A=c94_1a C95_2A=c95_2a C96_1C=c96_1c C97_3A=c97_3a
+preprocess.prop{fpp.defs} = $keys_model
+preprocess.prop{cpp.defs} = $keys_model
+preprocess.prop{fpp} = wrap_pp
+preprocess.prop{cpp} = wrap_mpicc
+preprocess.prop{cpp.flags} = -E
+preprocess.prop{fpp.flags} = -E -P -traditional -I /home/h04/opsrc/ops0/mpi/mpich2-1.4-ukmo-v1/ifort-12/include
+
+build.target  = um.exe
+build.target-rename  = flumeMain.exe:um.exe
+build.prop{cc} = wrap_cc
+build.prop{fc} = wrap_mpif90
+build.prop{fc.flags}      = -i8 -r8 -w -I /home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/inc -O0
+build.prop{fc.flags-ld}   = -L/home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/lib -lgcom -Wl,--noinhibit-exec -Vaxlib
+build.prop{dep.o.special} = blkdata.o
+build.prop{ns-dep.o} = um/control/c_code
+build.prop{no-dep.f.module} = netcdf mpl mod_prism_proto mod_prism_grids_writing mod_prism_def_partition_proto mod_prism_put_proto mod_prism_get_proto
+build.prop{no-dep.include}  = netcdf.inc mpif.h
+build.prop{no-dep.bin}[um/script] = *
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77.cfg	(revision 5095)
@@ -0,0 +1,74 @@
+step.class[preprocess-recon]          = preprocess
+step.class[build-recon build-scripts] = build
+steps = extract build-scripts preprocess build preprocess-recon build-recon
+
+extract.ns = um
+extract.location[um] = trunk@vn7.7
+extract.path-root[um] = src
+extract.path-excl[um] = configs scm utility
+extract.path-incl[um] = utility/makebc utility/qxreconf
+
+build-scripts.prop{no-dep.bin} = *
+build-scripts.ns-excl = /
+build-scripts.ns-incl = um/script
+build-scripts.target = archfail autopp_tidyup getfile make_parexe.pl nextGenid \
+    OASIS3_ctl OASIS3_conf qscasedisp qscicerun qscicesetup qscombine \
+    qsexecute qsfinal qshistprint qshistreset qsmass qsmaster qsnemorun \
+    qsnemosetup NEMO_nl_ctl qspickup qsresubmit qsserver qssetup restartinfo \
+    submitchk UMScr_TopLevel qsmoose
+
+$keys_model = C_LONG_LONG_INT=c_long_long_int C_LOW_U=c_low_u \
+    \ FRL8=frl8 LINUX=linux BUFRD_IO=bufrd_io LITTLE_END=little_end \
+    \ LFS=lfs _LARGEFILE_SOURCE=_largefile_source _FILE_OFFSET_BITS=64 \
+    \ CONTROL=control ATMOS=atmos GLOBAL=global A04_ALL=a04_all \
+    \ A01_3C=a01_3c A02_3C=a02_3c A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a \
+    \ A06_4A=a06_4a A08_7A=a08_7a A09_2A=a09_2a A10_2A=a10_2a A11_0A=a11_0a \
+    \ A12_2A=a12_2a A13_2A=a13_2a A14_0A=a14_0a A15_1A=a15_1a A16_1A=a16_1a \
+    \ A17_0A=a17_0a A18_2A=a18_2a A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a \
+    \ A30_1A=a30_1a A31_0A=a31_0a A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a \
+    \ A35_0A=a35_0a A36_0A=a36_0a A37_0A=a37_0a A38_0A=a38_0a A39_0A=a39_0a \
+    \ A70_1C=a70_1c A71_1A=a71_1a C70_1A=c70_1a C72_0A=c72_0a C80_1A=c80_1a \
+    \ C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a C94_1A=c94_1a C95_2A=c95_2a \
+    \ C96_1C=c96_1c C97_3A=c97_3a
+preprocess.prop{fpp.defs} = $keys_model
+preprocess.prop{cpp.defs} = $keys_model
+preprocess.prop{fpp} = wrap_pp
+preprocess.prop{cpp} = wrap_mpicc
+preprocess.prop{cpp.flags} = -E
+preprocess.prop{fpp.flags} = -E -P -traditional -I /home/h04/opsrc/ops0/mpi/mpich2-1.4-ukmo-v1/ifort-12/include
+preprocess.ns-excl = um/script um/utility/qxreconf
+
+build.target  = um.exe
+build.target-rename  = flumeMain.exe:um.exe
+build.prop{cc} = wrap_cc
+build.prop{fc} = wrap_mpif90
+build.prop{fc.flags}      = -i8 -r8 -w -I /home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/inc -O0
+build.prop{fc.flags-ld}   = -L/home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/lib -lgcom -Vaxlib
+build.prop{dep.o.special} = blkdata.o
+build.prop{ns-dep.o} = um/control/c_code
+build.prop{no-dep.f.module} = mpl
+build.ns-excl = um/script um/utility/qxreconf
+
+$keys_recon = C_LONG_LONG_INT=c_long_long_int C_LOW_U=c_low_u \
+    \ FRL8=frl8 LINUX=linux BUFRD_IO=bufrd_io LITTLE_END=little_end \
+    \ LFS=lfs _LARGEFILE_SOURCE=_largefile_source _FILE_OFFSET_BITS=64 \
+    \ RECON=recon
+preprocess-recon.prop{no-step-source} = preprocess
+preprocess-recon.prop{fpp.defs} = $keys_recon
+preprocess-recon.prop{cpp.defs} = $keys_recon
+preprocess-recon.prop{fpp} = wrap_pp
+preprocess-recon.prop{cpp} = wrap_mpicc
+preprocess-recon.prop{cpp.flags} = -E
+preprocess-recon.prop{fpp.flags} = -E -P -traditional -I /home/h04/opsrc/ops0/mpi/mpich2-1.0.8p1-ukmo-v2/ifort-10/include
+preprocess-recon.ns-excl = um/script um/utility/makebc
+
+build-recon.prop{no-step-source} = preprocess
+build-recon.target        = qxreconf
+build-recon.target-rename = reconfigure.exe:qxreconf
+build-recon.prop{cc} = wrap_cc
+build-recon.prop{fc} = wrap_mpif90
+build-recon.prop{fc.flags}      = -i8 -r8 -w -I /home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/inc -O0
+build-recon.prop{fc.flags-ld}   = -L/home/h01/frum/gcom/gcom4.1/linux_ifort_mpich2/lib -lgcom -Vaxlib
+build-recon.prop{ns-dep.o} = um/control/c_code
+build-recon.prop{no-dep.f.module} = mpl
+build-recon.ns-excl = um/script um/utility/makebc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_hpc.cfg	(revision 5095)
@@ -0,0 +1,89 @@
+step.class[preprocess-recon]          = preprocess
+step.class[build-recon build-scripts] = build
+steps = extract mirror
+
+mirror.target = $HPC:$THIS_RUN_DIR_HPC
+mirror.prop{config-file.steps} =  build-scripts preprocess build preprocess-recon build-recon
+
+extract.ns = um
+extract.location[um] = trunk@vn7.7
+extract.path-root[um] = src
+extract.path-excl[um] = configs scm utility
+extract.path-incl[um] = utility/makebc utility/qxreconf
+
+build-scripts.prop{no-dep.bin} = *
+build-scripts.ns-excl = /
+build-scripts.ns-incl = um/script
+build-scripts.target = archfail autopp_tidyup getfile make_parexe.pl nextGenid \
+    OASIS3_ctl OASIS3_conf qscasedisp qscicerun qscicesetup qscombine \
+    qsexecute qsfinal qshistprint qshistreset qsmass qsmaster qsnemorun \
+    qsnemosetup NEMO_nl_ctl qspickup qsresubmit qsserver qssetup restartinfo \
+    submitchk UMScr_TopLevel qsmoose
+
+$keys_model = C_LONG_INT=c_long_int C_LOW_U=c_low_u FRL8=frl8 \
+    \ BUFRD_IO=bufrd_io VECTLIB=vectlib IBM=ibm C98_1A=c98_1a MO_GRIB=mo_grib \
+    \ CONTROL=control ATMOS=atmos \
+    \ GLOBAL=global A04_ALL=a04_all A01_3C=a01_3c A02_3C=a02_3c \
+    \ A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a A06_4A=a06_4a A08_7A=a08_7a \
+    \ A09_2A=a09_2a A10_2A=a10_2a A11_0A=a11_0a A12_2A=a12_2a A13_2A=a13_2a \
+    \ A14_0A=a14_0a A15_1A=a15_1a A16_1A=a16_1a A17_0A=a17_0a A18_2A=a18_2a \
+    \ A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a A30_1A=a30_1a A31_0A=a31_0a \
+    \ A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a A35_0A=a35_0a A36_0A=a36_0a \
+    \ A37_0A=a37_0a A38_0A=a38_0a A39_0A=a39_0a A70_1C=a70_1c A71_1A=a71_1a \
+    \ C70_1A=c70_1a C72_0A=c72_0a C80_1A=c80_1a \
+    \ C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a C94_1A=c94_1a C95_2A=c95_2a \
+    \ C96_1C=c96_1c C97_3A=c97_3a
+preprocess.prop{fpp.defs} = $keys_model
+preprocess.prop{cpp.defs} = $keys_model
+preprocess.prop{fpp} = cpp
+preprocess.prop{cpp} = xlc
+preprocess.prop{cpp.flags} = -E -C
+preprocess.prop{fpp.flags} = -E -P -traditional
+preprocess.ns-excl = um/script um/utility/qxreconf
+
+build.target  = um.exe
+build.target-rename  = flumeMain.exe:um.exe
+build.prop{cc} = xlc_r
+build.prop{fc} = mpxlf90_r
+$flags_base_model = -qrealsize=8 -qintsize=8 -qextname -qsuffix=f=f90 \
+    \ -qarch=pwr6 -qtune=pwr6 -qxflag=p6div -NS32768 -g -O0 \
+    \ -qstrict -I/projects/um1/gcom/gcom3.6/meto_ibm_pwr6_mpp/inc \
+    \ -I/projects/um1/lib/netcdf3.20090102/include
+build.prop{fc.flags} = $flags_base_model 
+build.prop{fc.flags}[um/io_services] = $flags_base_model -qsmp=omp
+build.prop{fc.flags-ld} = -lmass -lmassvp6 -qsmp=omp \
+    \ -L/projects/um1/gcom/gcom3.6/meto_ibm_pwr6_mpp/lib -lgcom \
+    \ -L/projects/um1/lib/netcdf3.20090102/lib64 -lnetcdf \
+    \ -L/projects/um1/lib -lsig -lgrib
+build.prop{dep.o.special} = blkdata.o
+build.prop{ns-dep.o} = um/control/c_code
+build.prop{dep.o}[um/control/c_code] = print_from_c.o
+build.prop{no-dep.f.module} = mpl
+build.ns-excl = um/script um/utility/qxreconf
+
+$keys_recon = C_LONG_INT=c_long_int C_LOW_U=c_low_u FRL8=frl8 \
+    \ BUFRD_IO=bufrd_io VECTLIB=vectlib IBM=ibm C98_1A=c98_1a MO_GRIB=mo_grib \
+    \ RECON=recon
+preprocess-recon.prop{no-step-source} = preprocess
+preprocess-recon.prop{fpp.defs} = $keys_recon
+preprocess-recon.prop{cpp.defs} = $keys_recon
+preprocess-recon.prop{fpp} = cpp
+preprocess-recon.prop{cpp} = xlc
+preprocess-recon.prop{cpp.flags} = -E -C
+preprocess-recon.prop{fpp.flags} = -E -P -traditional
+preprocess-recon.ns-excl = um/script um/utility/makebc
+
+build-recon.prop{no-step-source} = preprocess
+build-recon.target        = qxreconf
+build-recon.target-rename = reconfigure.exe:qxreconf
+build-recon.prop{cc} = xlc_r
+build-recon.prop{fc} = mpxlf90_r
+build-recon.prop{fc.flags} = $flags_base_model 
+build-recon.prop{fc.flags-ld} = -lmass -lmassvp6 -qsmp=omp \
+    \ -L/projects/um1/gcom/gcom3.6/meto_ibm_pwr6_mpp/lib -lgcom \
+    \ -L/projects/um1/lib/netcdf3.20090102/lib64 -lnetcdf \
+    \ -L/projects/um1/lib -lsig -lgrib
+build-recon.prop{ns-dep.o} = um/control/c_code
+build-recon.prop{dep.o}[um/control/c_code] = print_from_c.o
+build-recon.prop{no-dep.f.module} = mpl
+build-recon.ns-excl = um/script um/utility/makebc
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_inherit.cfg	(revision 5095)
@@ -0,0 +1,15 @@
+use = $RUN_DIR/fcm2_um77
+
+extract.location{diff}[um] = \
+    branches/dev/frps/VN7.7_qpos_opt@28576 \
+    branches/dev/frpe/VN7.7_extra_ts@28576 \
+    branches/dev/hadco/VN7.7_incrCLO@25591 \
+    branches/dev/frml/VN7.7_new_pmsl@26275 \
+    branches/dev/frhi/VN7.7_bugfix@27020 \
+    branches/dev/frgr/VN7.7_JULES_fixes@26312 \
+    branches/dev/haddb/VN7.7_JULES_decpl@27034 \
+    branches/dev/frme/VN7.7_cloud_scheme_bugfixes@26531 \
+    branches/dev/frwm/VN7.7_simple_conv_diag@26841 \
+    branches/dev/frjw/VN7.7_mphys_pert_sens@26510 \
+    branches/dev/frjw/VN7.7_microphys_iterations_plumbing@27035 \
+    branches/dev/haddb/VN7.7_CoastConvG@27374
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_inherit_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_inherit_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um77_inherit_hpc.cfg	(revision 5095)
@@ -0,0 +1,17 @@
+use = $RUN_DIR/fcm2_um77_hpc
+
+extract.location{diff}[um] = \
+    branches/dev/frps/VN7.7_qpos_opt@28576 \
+    branches/dev/frpe/VN7.7_extra_ts@28576 \
+    branches/dev/hadco/VN7.7_incrCLO@25591 \
+    branches/dev/frml/VN7.7_new_pmsl@26275 \
+    branches/dev/frhi/VN7.7_bugfix@27020 \
+    branches/dev/frgr/VN7.7_JULES_fixes@26312 \
+    branches/dev/haddb/VN7.7_JULES_decpl@27034 \
+    branches/dev/frme/VN7.7_cloud_scheme_bugfixes@26531 \
+    branches/dev/frwm/VN7.7_simple_conv_diag@26841 \
+    branches/dev/frjw/VN7.7_mphys_pert_sens@26510 \
+    branches/dev/frjw/VN7.7_microphys_iterations_plumbing@27035 \
+    branches/dev/haddb/VN7.7_CoastConvG@27374
+
+mirror.target = $HPC:$THIS_RUN_DIR_HPC
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_hpc.cfg	(revision 5095)
@@ -0,0 +1,42 @@
+steps = extract mirror
+
+mirror.target = $HPC:$THIS_RUN_DIR_HPC
+mirror.prop{config-file.steps} =  preprocess build
+
+extract.ns = um
+extract.location[um] = trunk@vn7.3
+extract.path-root[um] = src
+
+$keys_model = C_LONG_INT=c_long_int MPP=mpp C_LOW_U=c_low_u \
+    \ FRL8=frl8 BUFRD_IO=bufrd_io VECTLIB=vectlib IBM=ibm CONTROL=control \
+    \ REPROD=reprod MPP=mpp ATMOS=atmos GLOBAL=global A04_ALL=a04_all \
+    \ A01_3C=a01_3c A02_3C=a02_3c A03_8C=a03_8c A04_3D=a04_3d A05_4A=a05_4a \
+    \ A06_4A=a06_4a A08_7A=a08_7a A09_2A=a09_2a A10_2A=a10_2a A11_2A=a11_2a \
+    \ A12_2A=a12_2a A13_2A=a13_2a A14_1B=a14_1b A15_1A=a15_1a A16_1A=a16_1a \
+    \ A17_2B=a17_2b A18_0A=a18_0a A19_1A=a19_1a A25_0A=a25_0a A26_0A=a26_0a \
+    \ A30_1A=a30_1a A31_0A=a31_0a A32_1A=a32_1a A33_0A=a33_0a A34_0A=a34_0a \
+    \ A35_0A=a35_0a A38_0A=a38_0a A70_1C=a70_1c A71_1A=a71_1a C70_1A=c70_1a \
+    \ C72_0A=c72_0a C80_1A=c80_1a C82_1A=c82_1a C84_1A=c84_1a C92_2A=c92_2a \
+    \ C94_1A=c94_1a C95_2A=c95_2a C96_1C=c96_1c C97_3A=c97_3a
+preprocess.prop{fpp.defs} = $keys_model
+preprocess.prop{cpp.defs} = $keys_model
+preprocess.prop{fpp} = cpp
+preprocess.prop{cpp} = xlc
+preprocess.prop{cpp.flags} = -E -C
+preprocess.prop{fpp.flags} = -E -P -traditional
+
+build.target  = um.exe
+build.target-rename  = flumeMain.exe:um.exe
+build.prop{cc} = xlc_r
+build.prop{fc} = mpxlf90_r
+build.prop{fc.flags}      = -I/projects/um1/gcom/gcom3.3/meto_ibm_pwr6_mpp/inc -I/projects/um1/lib/netcdf3.20090102/include -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8 -NS32768 -O0
+build.prop{fc.flags}[um/atmosphere/dynamics_advection/eta_vert_weights_e.F90] = -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8 -O0 -NS32768
+build.prop{fc.flags}[um/control/top_level/atm_step.F90]                       = -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8 -O0 -NS32768
+build.prop{fc.flags}[um/control/top_level/u_model.F90]                        = -qextname -qsuffix=f=f90 -qarch=pwr6 -qtune=pwr6 -qrealsize=8 -qintsize=8 -O0 -NS32768
+build.prop{fc.flags-ld}  = -lmass -lmassvp6 -L/projects/um1/gcom/gcom3.3/meto_ibm_pwr6_mpp/lib -lgcom -L/projects/um1/lib -lgrib -lsig -L/projects/um1/lib/netcdf3.20090102/lib64 -lnetcdf
+build.prop{dep.o.special} = blkdata.o
+build.prop{ns-dep.o} = um/control/c_code
+build.prop{dep.o}[um/control/c_code] = print_from_c.o
+build.prop{no-dep.f.module} = netcdf mpl mod_prism_proto mod_prism_grids_writing mod_prism_def_partition_proto mod_prism_put_proto mod_prism_get_proto
+build.prop{no-dep.include}  = netcdf.inc mpif.h
+build.prop{no-dep.bin}[um/script] = *
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_inherit.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_inherit.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_inherit.cfg	(revision 5095)
@@ -0,0 +1,17 @@
+use = $RUN_DIR/fcm2_um
+
+extract.location{diff}[um] = \
+    branches/dev/Share/VN7.3_hg3_dust_443@11858 \
+    branches/dev/Share/VN7.3_hg3_ccw_precip@11857 \
+    branches/dev/hadco/VN7.3_HG3_porting_lsp_fixes@12029 \
+    branches/dev/hadco/VN7.3_pc2_qcl_gt_tiny@12142 \
+    branches/dev/hadas/VN7.3_w_CAPE_diag@12012 \
+    branches/dev/frlk/VN7.3_BLLEVS_fixes@13938 \
+    branches/dev/frwm/VN7.3_Cu_Diag_Low_LCL@12011 \
+    branches/dev/frwm/VN7.3_LimitCnvParcPert@12041 \
+    branches/dev/hadip/VN7.3_ilp_moose@13314 \
+    branches/dev/hadco/VN7.3_temp_fix_solver@12740 \
+    branches/dev/hadng/VN7.3_wetlands_rothc@12603 \
+    branches/dev/frtg/VN7.3_reconf_extern_ancil@13063 \
+    branches/dev/frma/VN7.3_rad_dev@12732 \
+    branches/dev/frlk/VN7.3_melt_fix@13822
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_inherit_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_inherit_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_um_inherit_hpc.cfg	(revision 5095)
@@ -0,0 +1,19 @@
+use = $RUN_DIR/fcm2_um_hpc
+
+extract.location{diff}[um] = \
+    branches/dev/Share/VN7.3_hg3_dust_443@11858 \
+    branches/dev/Share/VN7.3_hg3_ccw_precip@11857 \
+    branches/dev/hadco/VN7.3_HG3_porting_lsp_fixes@12029 \
+    branches/dev/hadco/VN7.3_pc2_qcl_gt_tiny@12142 \
+    branches/dev/hadas/VN7.3_w_CAPE_diag@12012 \
+    branches/dev/frlk/VN7.3_BLLEVS_fixes@13938 \
+    branches/dev/frwm/VN7.3_Cu_Diag_Low_LCL@12011 \
+    branches/dev/frwm/VN7.3_LimitCnvParcPert@12041 \
+    branches/dev/hadip/VN7.3_ilp_moose@13314 \
+    branches/dev/hadco/VN7.3_temp_fix_solver@12740 \
+    branches/dev/hadng/VN7.3_wetlands_rothc@12603 \
+    branches/dev/frtg/VN7.3_reconf_extern_ancil@13063 \
+    branches/dev/frma/VN7.3_rad_dev@12732 \
+    branches/dev/frlk/VN7.3_melt_fix@13822
+
+mirror.target = $HPC:$THIS_RUN_DIR_HPC
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_var.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_var.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_var.cfg	(revision 5095)
@@ -0,0 +1,80 @@
+steps = extract preprocess build
+
+extract.ns = gcom gen ops var var_admin
+extract.location[var] = trunk@14844
+extract.path-root[var] = src
+extract.path-excl[var] = config scripts
+extract.path-incl[var] = scripts/Var_Scripts
+
+extract.location[ops] = trunk@18341
+extract.path-root[ops] = src/code
+extract.path-excl[ops] = /
+extract.path-incl[ops] = OpsMod_Constants \
+                       \ OpsMod_Control OpsMod_GeoIR \
+                       \ OpsMod_ObsInfo \
+                       \ OpsMod_RTTOV \
+                       \ OpsMod_Sort \
+                       \ OpsMod_Utilities \
+                       \ OpsMod_Varobs \
+                       \ OpsMod_VerticalInterp \
+                       \ OpsMod_VisControl \
+                       \ OpsProg_RTTOV9 \
+                       \ Ops_AIRS_1DVar \
+                       \ Ops_AIRS_Utilities \
+                       \ Ops_RTTOV7 \
+                       \ Ops_RTTOV7_RTTOVCLD \
+                       \ Ops_RTTOV9 \
+                       \ Ops_SatRad_Info \
+                       \ Ops_SatRad_Process \
+                       \ Ops_SatRad_SetUp \
+                       \ Ops_SatRad_Utilities
+
+extract.location[gen] = trunk@3073
+extract.path-root[gen] = src/code
+
+extract.location[var_admin] = trunk@14851
+extract.path-root[var_admin] = src/code
+
+extract.location[gcom] = branches/dev/ibmjb/r12957_2194_ralltoalle_out_of_order@15824
+extract.path-root[gcom] = build
+extract.path-excl[gcom] = configs ext_scripts
+
+preprocess.ns-excl = var ops gen var_admin
+preprocess.ns-incl = var/code/PF_MPP \
+                   \ ops/Ops_RTTOV9/rttov9_parallel_ad.F90 \
+                   \ ops/Ops_RTTOV9/rttov9_parallel_direct.F90 \
+                   \ ops/Ops_RTTOV9/rttov9_parallel_k.F90 \
+                   \ ops/Ops_RTTOV9/rttov9_parallel_tl.F90
+preprocess.prop{fpp} = wrap_pp
+preprocess.prop{fpp.defs} = IFORT_CDIRS
+preprocess.prop{fpp.defs}[gcom] = GC_VERSION="'3.4+'" GC_BUILD_DATE="'15824'" PREC_64B GC__FLUSHUNIT6 GC__FORTERRUNIT=0 GC_DESCRIP="'MPP'" MPI_SRC MPILIB_32B
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_ad.F90]     = _RTTOV_PARALLEL_AD
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_direct.F90] = _RTTOV_PARALLEL_DIRECT
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_k.F90]      = _RTTOV_PARALLEL_K
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_tl.F90]     = _RTTOV_PARALLEL_TL
+
+$OPSDIR{?} = /home/h04/opsrc/ops0
+$mpich2 = $OPSDIR/mpi/mpich2-1.4-ukmo-v1/ifort-12
+build.target = VarScr_HelpCompile
+build.prop{cc} = wrap_cc
+build.prop{cc.defs}[gen/GenMod_Platform] = LOWERCASE UNDERSCORE FRL8 C_LONG_LONG_INT
+build.prop{cc.defs}[gen/UM_Platform]     = VAROPSVER C_LOW_U FRL8 C_LONG_LONG_INT LINUX LITTLE_END
+build.prop{fc} = wrap_fc
+build.prop{fc.flags}                   = -implicitnone -integer_size 64 -real_size 64 -ftrapuv
+build.prop{fc.flags}[gcom]             = -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none
+build.prop{fc.flags}[gcom/mpl/mpl.F90] = -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none -I$mpich2/include
+build.prop{fc.flags}[gen]              = -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn noerrors
+build.prop{fc.flags}[ops/Ops_RTTOV9]   = -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none
+build.prop{fc.flags}[var_admin]        = -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn none
+build.prop{fc.flags}[var/code/PF_Interpolation/Cubic_Lagrange_Adj.F90] = -implicitnone -integer_size 64 -real_size 64 -ftrapuv -Wp,-P
+build.prop{fc.flags}[var/code/PF_MPP] = -implicitnone -integer_size 64 -real_size 64 -ftrapuv -warn noerrors
+build.prop{fc.flags}[var/code/VarProg_UMFileUtils] = -implicitnone -integer_size 64 -real_size 64
+build.prop{fc.defs}[var ops gen var_admin] = IFORT_CDIRS
+build.prop{fc.defs}[var/code/PF_MPP] =
+build.prop{fc.defs}[gen/GenMod_Control] = GCOMHEADERS
+build.prop{fc.defs}[gen/GenMod_Utilities/Gen_FlushUnit.F90] = USE_FLUSH
+build.prop{fc.defs}[gen/UM_COEX gen/UM_Platform] = VAROPSVER
+build.prop{fc.flags-ld} = -L$mpich2/lib -lmpich -lmpl -lpthread
+build.prop{ns-dep.o}[var] = gcom var_admin/VarMod_Lapack var_admin/VarMod_Blas
+build.prop{no-dep.f.module} = f90_unix_io xlfutility
+build.prop{no-dep.include}  = mpif.h
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_var_hpc.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_var_hpc.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/cfg/fcm2_var_hpc.cfg	(revision 5095)
@@ -0,0 +1,87 @@
+steps = extract mirror
+
+mirror.target = $HPC:$THIS_RUN_DIR_HPC
+mirror.prop{config-file.steps} =  preprocess build
+
+extract.ns = gcom gen ops var var_admin
+extract.location[var] = trunk@14844
+extract.path-root[var] = src
+extract.path-excl[var] = config scripts
+extract.path-incl[var] = scripts/Var_Scripts
+
+extract.location[ops] = trunk@18341
+extract.path-root[ops] = src/code
+extract.path-excl[ops] = /
+extract.path-incl[ops] = OpsMod_Constants \
+                       \ OpsMod_Control OpsMod_GeoIR \
+                       \ OpsMod_ObsInfo \
+                       \ OpsMod_RTTOV \
+                       \ OpsMod_Sort \
+                       \ OpsMod_Utilities \
+                       \ OpsMod_Varobs \
+                       \ OpsMod_VerticalInterp \
+                       \ OpsMod_VisControl \
+                       \ OpsProg_RTTOV9 \
+                       \ Ops_AIRS_1DVar \
+                       \ Ops_AIRS_Utilities \
+                       \ Ops_RTTOV7 \
+                       \ Ops_RTTOV7_RTTOVCLD \
+                       \ Ops_RTTOV9 \
+                       \ Ops_SatRad_Info \
+                       \ Ops_SatRad_Process \
+                       \ Ops_SatRad_SetUp \
+                       \ Ops_SatRad_Utilities
+
+extract.location[gen] = trunk@3073
+extract.path-root[gen] = src/code
+
+extract.location[var_admin] = trunk@14851
+extract.path-root[var_admin] = src/code
+
+extract.location[gcom] = branches/dev/ibmjb/r12957_2194_ralltoalle_out_of_order@15824
+extract.path-root[gcom] = build
+extract.path-excl[gcom] = configs ext_scripts
+
+preprocess.ns-excl = var ops gen var_admin
+preprocess.ns-incl = ops/Ops_RTTOV9/rttov9_parallel_ad.F90 \
+                   \ ops/Ops_RTTOV9/rttov9_parallel_direct.F90 \
+                   \ ops/Ops_RTTOV9/rttov9_parallel_k.F90 \
+                   \ ops/Ops_RTTOV9/rttov9_parallel_tl.F90
+preprocess.prop{cpp.flags} = -C
+preprocess.prop{cpp.defs} = LOWERCASE
+preprocess.prop{fpp.defs}[gcom] = GC_VERSION="'3.4+'" GC_BUILD_DATE="'15824'" PREC_64B GC__FLUSHUNIT6 GC__FORTERRUNIT=0 MPI_SRC MPILIB_32B GC_DESCRIP="'MPP'" MPI_BSEND_BUFFER_SIZE=10240000 IBM
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_ad.F90]     = _RTTOV_PARALLEL_AD
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_direct.F90] = _RTTOV_PARALLEL_DIRECT
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_k.F90]      = _RTTOV_PARALLEL_K
+preprocess.prop{fpp.defs}[ops/Ops_RTTOV9/rttov9_parallel_tl.F90]     = _RTTOV_PARALLEL_TL
+
+build.target = VarScr_HelpCompile
+build.prop{cc} = xlc
+build.prop{cc.defs}[gen/GenMod_Platform] = LOWERCASE FRL8 C_LONG_INT
+build.prop{cc.defs}[gen/UM_Platform]     = VAROPSVER C_LOW FRL8 C_LONG_INT
+build.prop{fc} = mpxlf95_r
+build.prop{fc.flags}                       = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1
+build.prop{fc.flags}[var/code/PFMod_Model] = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -NS1024
+build.prop{fc.flags}[gcom/gc]              = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed
+build.prop{fc.flags}[gcom/gcg]             = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed
+build.prop{fc.flags}[gen/GenMod_Reporting/GenMod_Reporting.F90] = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -WF,-qfpp
+build.prop{fc.flags}[gen/GenMod_Utilities/Gen_FlushUnit.F90]    = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -WF,-qfpp
+build.prop{fc.flags}[gen/UM_COEX]                     = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -WF,-qfpp
+build.prop{fc.flags}[gen/UM_General]                  = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed
+build.prop{fc.flags}[gen/UM_Platform]                 = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -WF,-qfpp
+build.prop{fc.flags}[gen/UM_Platform/IOERROR.F90]     = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -WF,-qfpp -qfree=f90
+build.prop{fc.flags}[var/code/VarMod_CovVertical]     = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qnoessl
+build.prop{fc.flags}[var/code/VarMod_CovVerticalData] = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qnoessl
+build.prop{fc.flags}[var_admin]                       = -O0 -qrealsize=8 -qintsize=8 -g -qfullpath -qessl -qarch=pwr6 -qtune=pwr6 -qmaxmem=-1 -qfixed -qrealsize=4 -qstrict
+build.prop{fc.flag-define} = -WF,-D%s
+build.prop{fc.defs}[gen/GenMod_Control]                        = GCOMHEADERS
+build.prop{fc.defs}[gen/GenMod_Control/Gen_SetupControl.F90]   = USE_CUSTOM_SIGNAL_HANDLER AIX
+build.prop{fc.defs}[gen/GenMod_Reporting/GenMod_Reporting.F90] = GEN_LEN_ERROR_OUT=134
+build.prop{fc.defs}[gen/GenMod_Utilities/Gen_FlushUnit.F90]    = USE_FLUSH AIX
+build.prop{fc.defs}[gen/UM_COEX gen/UM_Platform]               = VAROPSVER
+build.prop{fc.defs}[ops/Ops_RTTOV9]                            = RTTOV_ARCH_VECTOR
+build.prop{fc.defs}[ops/Ops_RTTOV9/rttov9_parkind1.F90]        = DEFAULT_INTEGER_32BIT
+build.prop{fc.flags-ld} = -L/projects/um1/lib -lsig -lessl -lmassvp6 -lmass
+build.prop{ns-dep.o}[var] = gcom var_admin/VarMod_Lapack var_admin/VarMod_Blas
+build.prop{no-dep.f.module} = f90_unix_io xlfutility
+build.prop{no-dep.include}  = mpif.h
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants.f90	(revision 5095)
@@ -0,0 +1,5 @@
+MODULE Hello_Constants
+
+INCLUDE 'hello_constants_dummy.inc'
+
+END MODULE Hello_Constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants.inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants.inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants.inc	(revision 5095)
@@ -0,0 +1,1 @@
+CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants_dummy.inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants_dummy.inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/module/hello_constants_dummy.inc	(revision 5095)
@@ -0,0 +1,1 @@
+INCLUDE 'hello_constants.inc'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/namelist/namelist.NL
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/namelist/namelist.NL	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/namelist/namelist.NL	(revision 5095)
@@ -0,0 +1,3 @@
+&TestNL
+  test = ,
+/
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/pro/hello.pro
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/pro/hello.pro	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/pro/hello.pro	(revision 5095)
@@ -0,0 +1,2 @@
+PRO HELLO
+END
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/pro/plot.pro
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/pro/plot.pro	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/pro/plot.pro	(revision 5095)
@@ -0,0 +1,3 @@
+PRO PLOT
+;  Calls    : hello.pro
+END
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/program/hello.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/program/hello.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/program/hello.F90	(revision 5095)
@@ -0,0 +1,26 @@
+PROGRAM Hello
+
+#if !defined(LOCAL_STRING)
+USE Hello_Constants, ONLY: hello_string
+#endif
+
+IMPLICIT NONE
+
+#if defined(LOCAL_STRING)
+CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Mother Earth!'
+#endif
+
+INTEGER :: integer_arg = 1234
+
+#if defined(CALL_HELLO_SUB)
+INCLUDE 'hello_sub.interface'
+#endif
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello'
+
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
+#if defined(CALL_HELLO_SUB)
+CALL Hello_Sub (integer_arg)
+#endif
+
+END PROGRAM Hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/script/hello.sh
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/script/hello.sh	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/script/hello.sh	(revision 5095)
@@ -0,0 +1,5 @@
+#!/usr/bin/ksh
+# Calls : hello.exe
+# Calls : plot.pro
+
+hello.exe
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_c.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_c.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_c.c	(revision 5095)
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+void hello_c_ () {
+  printf ("%s\n", "Hello_C: Hello Earth!");
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub.F90	(revision 5095)
@@ -0,0 +1,24 @@
+#if defined(HELLO_SUB)
+SUBROUTINE Hello_Sub (integer_arg)
+
+USE Hello_Constants, ONLY: hello_string
+
+IMPLICIT NONE
+
+CHARACTER (LEN=*), PARAMETER :: this = 'Hello_Sub'
+INTEGER :: integer_arg
+INTEGER :: integer_common
+COMMON /general/integer_common
+
+! DEPENDS ON: hello_c.o
+EXTERNAL Hello_C
+
+#include "hello_sub_dummy.h"
+
+WRITE (*, '(A,I0)') this // ': integer (arg): ', integer_arg
+WRITE (*, '(A,I0)') this // ': integer (common): ', integer_common
+
+CALL Hello_C ()
+
+END SUBROUTINE Hello_Sub
+#endif
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub.h
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub.h	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub.h	(revision 5095)
@@ -0,0 +1,1 @@
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub_dummy.h
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub_dummy.h	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/repos/trunk/subroutine/hello_sub_dummy.h	(revision 5095)
@@ -0,0 +1,1 @@
+#include "hello_sub.h"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/run_tests
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/run_tests	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/run_tests	(revision 5095)
@@ -0,0 +1,259 @@
+#!/bin/ksh
+set -u
+trap "echo Received signal ERR; exit 1"  ERR
+trap "echo Received signal TERM; exit 1" TERM
+
+export MY_BIN=$(cd $(dirname $0) && pwd)
+PATH=$MY_BIN:$PATH
+
+export DEBUG=false
+FCM1=true
+FCM2=true
+while getopts ":c:t:flradgh12" opt
+do
+  case $opt in
+    c ) CONTROL_URL=$OPTARG ;;
+    t ) TEST_URL=$OPTARG ;;
+    f ) FUNC_TESTS=true ;;
+    l ) LOCAL_TESTS=true ;;
+    r ) REMOTE_TESTS=true ;;
+    a ) FUNC_TESTS=true
+        LOCAL_TESTS=true
+        REMOTE_TESTS=true ;;
+    d ) DELETE=true ;;
+    g ) DEBUG=true ;;
+    h ) HELP=true ;;
+    1 ) FCM2=false ;;
+    2 ) FCM1=false ;;
+    \? ) echo "Invalid option"
+        HELP=true
+        break ;;
+  esac
+done
+if [[ $# != $(($OPTIND - 1)) ]]; then
+  echo "Invalid argument"
+  HELP=true
+fi
+
+if [[ ${HELP:-} == true ]]; then
+ echo 'Usage: run_tests [options]'
+ echo 'Valid options:'
+ echo '-c URL'
+ echo '   Generate the control results using the version of FCM at "URL"'
+ echo '-t URL'
+ echo '   Generate the test results using the version of FCM at "URL"'
+ echo '-f'
+ echo '   Perform the functional tests'
+ echo '-l'
+ echo '   Perform the local performance tests'
+ echo '-r'
+ echo '   Perform the remote performance tests'
+ echo '-a'
+ echo '   Perform all the tests (equivalent to -flr)'
+ echo '-d'
+ echo '   Remove any previous test results before starting'
+ echo '-g'
+ echo '   Output additional diagnostics'
+ echo '-h'
+ echo '   Print this help message'
+ echo '-1'
+ echo '   Only run the FCM1 tests'
+ echo '-2'
+ echo '   Only run the FCM2 tests'
+ exit 1
+fi
+
+if [[ -n "${CONTROL_URL:-}" ]]; then
+  TYPES="control"
+fi
+if [[ -n "${TEST_URL:-}" ]]; then
+  TYPES="${TYPES:-} test"
+fi
+if [[ -z "${TYPES:-}" ]]; then
+  echo "Either a control or a test URL must be specified"
+  exit 1
+fi
+
+if [[ ${REMOTE_TESTS:-} == true ]]; then
+  export HPC=$(rose host-select -q hpc)
+  export BASE_DIR_HPC=$(ssh $HPC 'echo $PWD')/working/fcm_test_suite
+fi
+
+export BASE_DIR=$LOCALTEMP/fcm_test_suite
+if [[ ${DELETE:-} == true ]]; then
+  if $DEBUG; then
+    echo "Removing any previous test directory ..."
+  fi
+  rm -rf $BASE_DIR
+  if [[ ${REMOTE_TESTS:-} == true ]]; then
+    ssh $HPC "rm -rf $BASE_DIR_HPC"
+  fi
+fi
+mkdir -p $BASE_DIR
+
+export REPOS_DIR=$BASE_DIR/test_svn
+export REPOS_URL="file://$REPOS_DIR"
+if [[ ! -d $REPOS_DIR ]]; then
+  echo "$(date): Creating test repository ..."
+  $MY_BIN/create_repos > $BASE_DIR/repos.stdout 2> $BASE_DIR/repos.stderr
+fi
+
+cp $MY_BIN/compare_*_fcm* $BASE_DIR
+PATH_BASE=$MY_BIN/wrapper_scripts:$PATH:~opsrc/ops0/mpi/mpich2-1.4-ukmo-v1/ifort-12/bin
+
+trap ""  ERR
+export TEST
+export TYPE
+for TYPE in $TYPES
+do
+  if [[ $TYPE == test ]]; then
+    URL=$TEST_URL
+  else
+    URL=$CONTROL_URL
+  fi
+  REV=$(git describe $URL) || exit $RC
+  echo "FCM version to be used: $REV"
+
+  export RUN_DIR=$BASE_DIR/$TYPE
+  rm -rf $RUN_DIR
+  mkdir $RUN_DIR
+
+  if $DEBUG; then
+    echo "Creating local copy of FCM ..."
+  fi
+  (cd $MY_BIN/.. && git archive --format=tar --prefix=fcm/ $REV) | (cd $RUN_DIR && tar xf -)
+  if [[ -a $RUN_DIR/fcm/etc/fcm.cfg ]]; then
+    echo "set::url::test_suite $REPOS_URL" >>$RUN_DIR/fcm/etc/fcm.cfg
+  else
+    echo "location{primary}[test_suite] = $REPOS_URL" >>$RUN_DIR/fcm/etc/fcm/keyword.cfg
+  fi
+  export PATH=$RUN_DIR/fcm/bin:$PATH_BASE
+
+  if [[ ${FUNC_TESTS:-} == true ]]; then
+    . $MY_BIN/tests_functional.list
+    export COMPARE_TIMES=false
+    let failed=0
+    if [[ $FCM1 == true ]]; then
+      for TEST in $TESTS_FCM1
+      do
+        $MY_BIN/perform_test_fcm1
+        if [[ $? != 0 ]]; then
+          let failed=failed+1
+        fi
+      done
+    fi
+    if [[ $FCM2 == true ]]; then
+      for TEST in $TESTS_FCM2
+      do
+        $MY_BIN/perform_test_fcm2
+        if [[ $? != 0 ]]; then
+          let failed=failed+1
+        fi
+      done
+    fi
+
+    echo "$(date): Functional tests finished"
+    if [[ $failed == 0 ]]; then
+      echo "SUMMARY: All functional tests succeeded"
+    else
+      echo "SUMMARY: $failed functional tests failed"
+    fi
+  fi
+
+  if [[ ${LOCAL_TESTS:-} == true ]]; then
+    . $MY_BIN/tests_perf_local.list
+    export COMPARE_TIMES=true
+    export run_1=no
+    export run_2=no
+    let failed=0
+    if [[ $FCM1 == true ]]; then
+      for TEST in $TESTS_FCM1
+      do
+        $MY_BIN/perform_test_fcm1
+        if [[ $? != 0 ]]; then
+          let failed=failed+1
+        fi
+      done
+    fi
+    if [[ $FCM2 == true ]]; then
+      for TEST in $TESTS_FCM2
+      do
+        $MY_BIN/perform_test_fcm2
+        if [[ $? != 0 ]]; then
+          let failed=failed+1
+        fi
+      done
+    fi
+    unset run_1 run_2
+
+    echo "$(date): Local performance tests finished"
+    if [[ $failed == 0 ]]; then
+      echo "SUMMARY: All local performance tests succeeded"
+    else
+      echo "SUMMARY: $failed local performance tests failed"
+    fi
+  fi
+
+  if [[ ${REMOTE_TESTS:-} == true ]]; then
+    if $DEBUG; then
+      echo "Copying files to HPC platform ..."
+    fi
+    export RUN_DIR_HPC=$BASE_DIR_HPC/$TYPE
+    ssh $HPC "rm -rf $RUN_DIR_HPC"
+    ssh $HPC "mkdir -p $RUN_DIR_HPC"
+    rsync -a --rsh="ssh" $RUN_DIR/fcm $HPC:$RUN_DIR_HPC
+    rsync -a --rsh="ssh" compare_*_fcm* report_hpc_results $HPC:$BASE_DIR_HPC
+
+    BATCH_SCRIPT_NAME=hpc_batch.sh
+    export BATCH_DIRS_NAME=hpc_dirs.sh
+    export BATCH_SCRIPT=$RUN_DIR/$BATCH_SCRIPT_NAME
+    $MY_BIN/create_hpc_batch_script
+    export BATCH_DIRS=$RUN_DIR/$BATCH_DIRS_NAME
+
+    . $MY_BIN/tests_perf_remote.list
+    export COMPARE_TIMES=true
+    export mirror=remote
+    let failed=0
+    echo 'TESTS_FCM1="' >$BATCH_DIRS
+    if [[ $FCM1 == true ]]; then
+      for TEST in $TESTS_FCM1
+      do
+        $MY_BIN/perform_test_fcm1
+        if [[ $? != 0 ]]; then
+          let failed=failed+1
+        else
+          SUBMIT_REMOTE=true
+        fi
+      done
+    fi
+    echo '"' >>$BATCH_DIRS
+    echo 'TESTS_FCM2="' >>$BATCH_DIRS
+    if [[ $FCM2 == true ]]; then
+      export NPROC=6
+      for TEST in $TESTS_FCM2
+      do
+        $MY_BIN/perform_test_fcm2
+        if [[ $? != 0 ]]; then
+          let failed=failed+1
+        else
+          SUBMIT_REMOTE=true
+        fi
+      done
+    fi
+    echo '"' >>$BATCH_DIRS
+    unset mirror NPROC
+
+    if [[ ${SUBMIT_REMOTE:-} == true ]]; then
+      echo "$(date): Submitting HPC build job ..."
+      rsync -a --rsh="ssh" $BATCH_SCRIPT $BATCH_DIRS $HPC:$RUN_DIR_HPC
+      ssh $HPC "llsubmit $RUN_DIR_HPC/$BATCH_SCRIPT_NAME"
+    fi
+
+    echo "$(date): HPC performance tests finished"
+    if [[ $failed == 0 ]]; then
+      echo "SUMMARY: All HPC performance tests succeeded"
+    else
+      echo "SUMMARY: $failed HPC performance tests failed"
+    fi
+  fi
+done
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_add_directory
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_add_directory	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_add_directory	(revision 5095)
@@ -0,0 +1,3 @@
+build_1=fail_known
+
+# This case fails since the extract system does not detect the new directory.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_add_directory_expsrc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_add_directory_expsrc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_add_directory_expsrc	(revision 5095)
@@ -0,0 +1,2 @@
+# Although this case works, the output from the extract system does not correctly
+# identify the file as added from branch1 (unlike in fcm1_add_file).
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_clash
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_clash	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_clash	(revision 5095)
@@ -0,0 +1,1 @@
+extract_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_conflict_fail
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_conflict_fail	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_conflict_fail	(revision 5095)
@@ -0,0 +1,1 @@
+extract_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm1_base fcm1_branches_merge"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_inherit_wrong_include
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_inherit_wrong_include	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_inherit_wrong_include	(revision 5095)
@@ -0,0 +1,2 @@
+# The wrong include file gets used as demonstrated by the output from running the program.
+# See ticket:206
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_wcopies
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_wcopies	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_wcopies	(revision 5095)
@@ -0,0 +1,4 @@
+mkdir -p $BASE_DIR/work
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_base $BASE_DIR/work/b1
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_merge1 $BASE_DIR/work/b2
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_merge2 $BASE_DIR/work/b3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_wcopy
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_wcopy	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_branches_merge_wcopy	(revision 5095)
@@ -0,0 +1,1 @@
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_merge2 $BASE_DIR/work
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_cflags_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_cflags_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_cflags_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm1_base fcm1_cflags fcm1_base"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_change_src_type_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_change_src_type_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_change_src_type_incremental	(revision 5095)
@@ -0,0 +1,5 @@
+cfg_name="fcm1_change_src_type fcm1_change_src_type"
+
+# This test was designed to demonstrate the problem raised in ticket:345.
+# The problem (now fixed) was that some files got rebuilt unnecessarily
+# in the incremental build.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_directory
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_directory	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_directory	(revision 5095)
@@ -0,0 +1,3 @@
+extract_1=fail_known
+
+# This case fails since the extract system does not handle deleted directories.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_directory_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_directory_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_directory_inherit	(revision 5095)
@@ -0,0 +1,3 @@
+extract_1=fail_known
+
+# This case fails since the extract system does not handle deleted directories.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_file	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_file_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file_inherit	(revision 5095)
@@ -0,0 +1,4 @@
+build_1=succeed_known
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file_inherit_force
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file_inherit_force	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_inc_file_inherit_force	(revision 5095)
@@ -0,0 +1,4 @@
+build_1=succeed_known
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_pp_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_pp_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_pp_file	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_pp_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_pp_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_pp_file_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file_inherit	(revision 5095)
@@ -0,0 +1,4 @@
+build_1=succeed_known
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file_inherit_force
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file_inherit_force	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_delete_ppinc_file_inherit_force	(revision 5095)
@@ -0,0 +1,4 @@
+build_1=succeed_known
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_duplicate_target
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_duplicate_target	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_duplicate_target	(revision 5095)
@@ -0,0 +1,4 @@
+build_1=succeed_known
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:205
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exclude_dependency
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exclude_dependency	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exclude_dependency	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exe_permissions
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exe_permissions	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exe_permissions	(revision 5095)
@@ -0,0 +1,3 @@
+svn co -q $REPOS_URL/branches/dev/Share/exe_rename $BASE_DIR/work
+chmod 644 $BASE_DIR/work/script/hello.sh
+run_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exe_rename_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exe_rename_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_exe_rename_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm1_base fcm1_exe_rename"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_fc_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_fc_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_fc_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm1_base fcm1_fc fcm1_base"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_fflags_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_fflags_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_fflags_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm1_base fcm1_fflags1 fcm1_fflags2 fcm1_base"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_inc_devnull
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_inc_devnull	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_inc_devnull	(revision 5095)
@@ -0,0 +1,1 @@
+run_1=no
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_inherit_invalid_path
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_inherit_invalid_path	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_inherit_invalid_path	(revision 5095)
@@ -0,0 +1,3 @@
+extract_1=fail
+
+# Poor error message using FCM 1
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_base_url
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_base_url	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_base_url	(revision 5095)
@@ -0,0 +1,1 @@
+extract_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_branch_url
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_branch_url	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_branch_url	(revision 5095)
@@ -0,0 +1,1 @@
+extract_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_inc	(revision 5095)
@@ -0,0 +1,1 @@
+extract_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_namespace
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_namespace	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_namespace	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_variable
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_variable	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_invalid_variable	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_ld_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_ld_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_ld_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm1_base fcm1_ld fcm1_base"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_library
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_library	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_library	(revision 5095)
@@ -0,0 +1,1 @@
+run_1=no
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_library_rename
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_library_rename	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_library_rename	(revision 5095)
@@ -0,0 +1,1 @@
+run_1=no
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_mirror
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_mirror	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_mirror	(revision 5095)
@@ -0,0 +1,1 @@
+mirror=local
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_mirror_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_mirror_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_mirror_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+mirror=local
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_no_dep
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_no_dep	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_no_dep	(revision 5095)
@@ -0,0 +1,1 @@
+run_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_ops_parallel
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_ops_parallel	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_ops_parallel	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name=fcm1_ops
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_change_include_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_change_include_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_change_include_inherit	(revision 5095)
@@ -0,0 +1,2 @@
+# The wrong include file gets used as demonstrated by the lack of anything being compiled.
+# See ticket:206
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_change_keys_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_change_keys_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_change_keys_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm1_pp_change_dependency fcm1_base fcm1_pp_change_blockdata"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine	(revision 5095)
@@ -0,0 +1,1 @@
+build_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine_inherit	(revision 5095)
@@ -0,0 +1,3 @@
+build_1=succeed_known
+
+# This case does not fail as expected (see #335)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine_inherit_force
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine_inherit_force	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_pp_empty_subroutine_inherit_force	(revision 5095)
@@ -0,0 +1,5 @@
+build_1=fail
+
+# This case does fail as expected.
+# However, the failure is at the link stage whereas it ought to fail
+# due to a missing dependency (see #335)
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_revmatch_true
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_revmatch_true	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_revmatch_true	(revision 5095)
@@ -0,0 +1,1 @@
+extract_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_sps_parallel
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_sps_parallel	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_sps_parallel	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name=fcm1_sps
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_um
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_um	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_um	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm1_um fcm1_um"
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_um_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_um_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_um_inherit	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm1_um_inherit fcm1_um_inherit"
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_var_parallel
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_var_parallel	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm1_var_parallel	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name=fcm1_var
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_clash
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_clash	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_clash	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm2_base fcm2_branches_merge"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_inherit_wrong_include
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_inherit_wrong_include	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_inherit_wrong_include	(revision 5095)
@@ -0,0 +1,2 @@
+# The wrong include file gets used as demonstrated by the output from running the program.
+# See ticket:206
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_wcopies
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_wcopies	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_wcopies	(revision 5095)
@@ -0,0 +1,4 @@
+mkdir -p $BASE_DIR/work
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_base $BASE_DIR/work/b1
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_merge1 $BASE_DIR/work/b2
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_merge2 $BASE_DIR/work/b3
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_wcopy
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_wcopy	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_branches_merge_wcopy	(revision 5095)
@@ -0,0 +1,1 @@
+svn co -q $REPOS_URL/branches/dev/Share/modify_files_merge2 $BASE_DIR/work
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_cflags_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_cflags_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_cflags_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm2_base fcm2_cflags fcm2_base"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_cyclic_dep_fail
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_cyclic_dep_fail	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_cyclic_dep_fail	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_file	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_file_inherit	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file_inherit_force
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file_inherit_force	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_inc_file_inherit_force	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_pp_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_pp_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_pp_file	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_pp_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_pp_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_pp_file_inherit	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file_inherit_force
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file_inherit_force	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_delete_ppinc_file_inherit_force	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_dep_o_invalid
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_dep_o_invalid	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_dep_o_invalid	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_duplicate_target
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_duplicate_target	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_duplicate_target	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exclude_dependency
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exclude_dependency	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exclude_dependency	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exe_permissions
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exe_permissions	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exe_permissions	(revision 5095)
@@ -0,0 +1,3 @@
+svn co -q $REPOS_URL/branches/dev/Share/exe_rename $BASE_DIR/work
+chmod 644 $BASE_DIR/work/script/hello.sh
+run_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exe_rename_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exe_rename_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_exe_rename_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm2_base fcm2_exe_rename"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_fc_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_fc_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_fc_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm2_base fcm2_fc fcm2_base"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_fflags_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_fflags_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_fflags_incremental	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm2_base fcm2_fflags1 fcm2_fflags2 fcm2_base"
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inc_devnull
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inc_devnull	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inc_devnull	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inherit_invalid_path
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inherit_invalid_path	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inherit_invalid_path	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inherit_redefine_fail
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inherit_redefine_fail	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_inherit_redefine_fail	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_base_url
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_base_url	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_base_url	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_branch_url
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_branch_url	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_branch_url	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_branch_url2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_branch_url2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_branch_url2	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_inc	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_label
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_label	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_label	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_modifier
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_modifier	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_modifier	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_modifiers
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_modifiers	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_modifiers	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_namespace
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_namespace	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_namespace	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_namespace2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_namespace2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_namespace2	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_target
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_target	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_target	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_variable
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_variable	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_invalid_variable	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_library
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_library	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_library	(revision 5095)
@@ -0,0 +1,2 @@
+run_1=no
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_library_rename
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_library_rename	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_library_rename	(revision 5095)
@@ -0,0 +1,1 @@
+run_1=no
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror	(revision 5095)
@@ -0,0 +1,1 @@
+mirror=local
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_after_pp
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_after_pp	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_after_pp	(revision 5095)
@@ -0,0 +1,2 @@
+mirror=local
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+mirror=local
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit_fflags
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit_fflags	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit_fflags	(revision 5095)
@@ -0,0 +1,1 @@
+mirror=local
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit_notarget
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit_notarget	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_mirror_inherit_notarget	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_modify_subroutine_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_modify_subroutine_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_modify_subroutine_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_multi_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_multi_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_multi_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_no_dep
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_no_dep	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_no_dep	(revision 5095)
@@ -0,0 +1,1 @@
+run_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_ns-dep_o_invalid
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_ns-dep_o_invalid	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_ns-dep_o_invalid	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_ops_parallel
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_ops_parallel	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_ops_parallel	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name=fcm2_ops
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_override_variable
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_override_variable	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_override_variable	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm2_base"
+export fcflags="-assume nosource_include -g"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_change_include_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_change_include_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_change_include_inherit	(revision 5095)
@@ -0,0 +1,4 @@
+compare_fcm1=false
+
+# The wrong include file gets used as demonstrated by the lack of anything being compiled.
+# See ticket:206
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_change_keys_incremental
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_change_keys_incremental	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_change_keys_incremental	(revision 5095)
@@ -0,0 +1,1 @@
+cfg_name="fcm2_pp_change_dependency fcm2_base fcm2_pp_change_blockdata"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine_inherit	(revision 5095)
@@ -0,0 +1,1 @@
+make_1=fail
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine_inherit_force
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine_inherit_force	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_pp_empty_subroutine_inherit_force	(revision 5095)
@@ -0,0 +1,2 @@
+make_1=fail
+compare_fcm1=false
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_sps_parallel
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_sps_parallel	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_sps_parallel	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name=fcm2_sps
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm2_um fcm2_um"
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um77
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um77	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um77	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm2_um77 fcm2_um77"
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um77_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um77_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um77_inherit	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm2_um77_inherit fcm2_um77_inherit"
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um_inherit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um_inherit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_um_inherit	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name="fcm2_um_inherit fcm2_um_inherit"
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_var_parallel
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_var_parallel	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_config/fcm2_var_parallel	(revision 5095)
@@ -0,0 +1,2 @@
+cfg_name=fcm2_var
+NPROC=6
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/inc/fortran.inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/inc/fortran.inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/inc/fortran.inc	(revision 5095)
@@ -0,0 +1,1 @@
+WRITE (*, '(A)') 'PASSED: correct include file'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/fortran.inc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/fortran.inc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/fortran.inc	(revision 5095)
@@ -0,0 +1,1 @@
+WRITE (*, '(A)') 'FAILED: wrong include file'
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/test_fortran_inc.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/test_fortran_inc.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/test_fortran_inc.f90	(revision 5095)
@@ -0,0 +1,5 @@
+PROGRAM Test
+
+INCLUDE 'fortran.inc'
+
+END PROGRAM Test
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/test_prepro_inc.F90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/test_prepro_inc.F90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/prog/test_prepro_inc.F90	(revision 5095)
@@ -0,0 +1,5 @@
+PROGRAM Test
+
+#include "fortran.inc"
+
+END PROGRAM Test
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/test.sh
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/test.sh	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/test_include/test.sh	(revision 5095)
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+echo "### Fortran compiler tests"
+for fc in \
+  "ifort" \
+  "ifort -assume nosource_include" \
+  "gfortran" \
+  "gfortran -I-"
+do
+  echo
+  echo "Compiler: $fc"
+  echo "Fortran include test:"
+  $fc -o test.o -I$PWD/inc -c prog/test_fortran_inc.f90
+  $fc -o test.exe test.o
+  test.exe
+  rm test.exe test.o
+  echo "CPP include test:"
+  $fc -o test.o -I$PWD/inc -c prog/test_prepro_inc.F90
+  $fc -o test.exe test.o
+  test.exe
+  rm test.exe test.o
+done
+
+echo
+echo "### Preprocessor tests"
+fc=gfortran
+for cpp in \
+  "cpp -P -traditional" \
+  "cpp -P -traditional -I-"
+do
+  echo
+  echo "Pre-processor: $cpp"
+  $cpp -I$PWD/inc prog/test_prepro_inc.F90 >tmp.f90
+  $fc -o test.o -I$PWD/inc -c tmp.f90
+  $fc -o test.exe test.o
+  test.exe
+  rm test.exe test.o tmp.f90
+done
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_functional.list
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_functional.list	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_functional.list	(revision 5095)
@@ -0,0 +1,158 @@
+TESTS_FCM1="
+fcm1_base
+fcm1_add_directory
+fcm1_add_directory_expsrc
+fcm1_add_file
+fcm1_add_file_inherit
+fcm1_branches_clash
+fcm1_branches_merge
+fcm1_branches_merge_conflict_fail
+fcm1_branches_merge_conflict_override
+fcm1_branches_merge_incremental
+fcm1_branches_merge_inherit
+fcm1_branches_merge_inherit_wrong_include
+fcm1_branches_merge_wcopy
+fcm1_branches_merge_wcopies
+fcm1_cflags_incremental
+fcm1_change_src_type_incremental
+fcm1_delete_directory
+fcm1_delete_directory_inherit
+fcm1_delete_file
+fcm1_delete_file_inherit
+fcm1_delete_inc_file
+fcm1_delete_inc_file_inherit
+fcm1_delete_inc_file_inherit_force
+fcm1_delete_pp_file
+fcm1_delete_pp_file_inherit
+fcm1_delete_ppinc_file
+fcm1_delete_ppinc_file_inherit
+fcm1_delete_ppinc_file_inherit_force
+fcm1_duplicate_target
+fcm1_exclude_dependency
+fcm1_exe_permissions
+fcm1_exe_rename
+fcm1_exe_rename_incremental
+fcm1_fc_incremental
+fcm1_fflags_incremental
+fcm1_fflags_inherit
+fcm1_inc_devnull
+fcm1_inherit_invalid_path
+fcm1_inherit_target
+fcm1_invalid_base_url
+fcm1_invalid_branch_url
+fcm1_invalid_inc
+fcm1_invalid_namespace
+fcm1_invalid_variable
+fcm1_ld_incremental
+fcm1_library
+fcm1_library_rename
+fcm1_mirror
+fcm1_mirror_inherit
+fcm1_modify_subroutine_inherit
+fcm1_modify_subroutine_interface_inherit
+fcm1_multi_inherit
+fcm1_no_dep
+fcm1_pp_change_include
+fcm1_pp_change_include_inherit
+fcm1_pp_change_keys_incremental
+fcm1_pp_empty_subroutine
+fcm1_pp_empty_subroutine_inherit
+fcm1_pp_empty_subroutine_inherit_force
+fcm1_revmatch_false
+fcm1_revmatch_true
+fcm1_suite
+fcm1_symbolic_link
+"
+
+TESTS_FCM2="
+fcm2_base
+fcm2_add_directory_expsrc
+fcm2_add_file
+fcm2_add_file_inherit
+fcm2_branches_clash
+fcm2_branches_merge
+fcm2_branches_merge_duplicate
+fcm2_branches_merge_incremental
+fcm2_branches_merge_inherit
+fcm2_branches_merge_inherit_wrong_include
+fcm2_branches_merge_wcopy
+fcm2_branches_merge_wcopies
+fcm2_cflags_incremental
+fcm2_change_variable
+fcm2_cyclic_dep_fail
+fcm2_cyclic_dep_ok
+fcm2_delete_directory
+fcm2_delete_directory_inherit
+fcm2_delete_file
+fcm2_delete_file_inherit
+fcm2_delete_inc_file
+fcm2_delete_inc_file_inherit
+fcm2_delete_inc_file_inherit_force
+fcm2_delete_pp_file
+fcm2_delete_pp_file_inherit
+fcm2_delete_ppinc_file
+fcm2_delete_ppinc_file_inherit
+fcm2_delete_ppinc_file_inherit_force
+fcm2_dep_o
+fcm2_dep_o_all
+fcm2_dep_o_invalid
+fcm2_duplicate_target
+fcm2_exclude_dependency
+fcm2_extract_path_excl_no_ns
+fcm2_exe_permissions
+fcm2_exe_rename
+fcm2_exe_rename_incremental
+fcm2_fc_incremental
+fcm2_fflags_incremental
+fcm2_fflags_inherit
+fcm2_flag-output
+fcm2_inc_devnull
+fcm2_inherit_invalid_path
+fcm2_inherit_redefine_fail
+fcm2_inherit_redefine_ok
+fcm2_invalid_base_url
+fcm2_invalid_branch_url
+fcm2_invalid_branch_url2
+fcm2_invalid_inc
+fcm2_invalid_label
+fcm2_invalid_modifier
+fcm2_invalid_modifiers
+fcm2_invalid_namespace
+fcm2_invalid_namespace2
+fcm2_invalid_target
+fcm2_invalid_variable
+fcm2_library
+fcm2_library_rename
+fcm2_mirror
+fcm2_mirror_after_pp
+fcm2_mirror_inherit
+fcm2_mirror_inherit_notarget
+fcm2_mirror_inherit_fflags
+fcm2_modify_subroutine_inherit
+fcm2_modify_subroutine_interface_inherit
+fcm2_multi_inherit
+fcm2_multiple_build
+fcm2_multiple_build_inherit
+fcm2_multiple_pp-build
+fcm2_multiple_pp-build_inherit
+fcm2_no_dep
+fcm2_ns-dep_o
+fcm2_ns-dep_o_all
+fcm2_ns-dep_o_file
+fcm2_ns-dep_o_invalid
+fcm2_override_variable
+fcm2_pp_change_include
+fcm2_pp_change_include_inherit
+fcm2_pp_change_keys_incremental
+fcm2_pp_empty_subroutine
+fcm2_pp_empty_subroutine_inherit
+fcm2_pp_empty_subroutine_inherit_force
+fcm2_revmatch_false
+fcm2_single_file
+fcm2_space_in_name
+fcm2_symbolic_link
+"
+
+# Remove any commented out tests
+TESTS_FCM1=$(echo $TESTS_FCM1 | sed 's/#[^ ]*//g')
+TESTS_FCM2=$(echo $TESTS_FCM2 | sed 's/#[^ ]*//g')
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_perf_local.list
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_perf_local.list	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_perf_local.list	(revision 5095)
@@ -0,0 +1,23 @@
+TESTS_FCM1="
+fcm1_var
+fcm1_var_parallel
+fcm1_ops_parallel
+fcm1_sps_parallel
+fcm1_um
+fcm1_um_inherit
+"
+
+TESTS_FCM2="
+fcm2_var
+fcm2_var_parallel
+fcm2_ops_parallel
+fcm2_sps_parallel
+fcm2_um
+fcm2_um_inherit
+fcm2_um77
+fcm2_um77_inherit
+"
+
+# Remove any commented out tests
+TESTS_FCM1=$(echo $TESTS_FCM1 | sed 's/#[^ ]*//g')
+TESTS_FCM2=$(echo $TESTS_FCM2 | sed 's/#[^ ]*//g')
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_perf_remote.list
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_perf_remote.list	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/tests_perf_remote.list	(revision 5095)
@@ -0,0 +1,19 @@
+TESTS_FCM1="
+fcm1_var_hpc
+fcm1_postproc_hpc
+fcm1_um_hpc
+fcm1_um_inherit_hpc
+"
+
+TESTS_FCM2="
+fcm2_var_hpc
+fcm2_postproc_hpc
+fcm2_um_hpc
+fcm2_um_inherit_hpc
+fcm2_um77_hpc
+fcm2_um77_inherit_hpc
+"
+
+# Remove any commented out tests
+TESTS_FCM1=$(echo $TESTS_FCM1 | sed 's/#[^ ]*//g')
+TESTS_FCM2=$(echo $TESTS_FCM2 | sed 's/#[^ ]*//g')
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ar
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ar	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ar	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_ar $@" | sed "s#$RUN_DIR##g" | sed "s#/var/tmp/[^/]*/lib#/lib#" >>$command_file
+ar  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_cc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_cc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_cc	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_cc $@" | sed "s#$RUN_DIR##g" | sed "s#-I\./#-I#g" >>$command_file
+gcc  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_fc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_fc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_fc	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_fc $@" | sed "s#$RUN_DIR##g" | sed "s#-I\./#-I#g" | sed "s#-L/var/tmp/[^ ]* #-Llib #" >>$command_file
+ifort  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_fc2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_fc2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_fc2	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_fc2 $@" | sed "s#$RUN_DIR##g" | sed "s#-I\./#-I#g" | sed "s#-L/var/tmp/[^ ]* #-Llib #" >>$command_file
+ifort  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ld
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ld	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ld	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_ld $@" | sed "s#$RUN_DIR##g" >>$command_file
+ifort  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ld2
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ld2	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_ld2	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_ld2 $@" | sed "s#$RUN_DIR##g" >>$command_file
+ifort  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_mpicc
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_mpicc	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_mpicc	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_mpicc $@" | sed "s#$RUN_DIR##g" | sed "s#-I\./#-I#g" >>$command_file
+mpicc  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_mpif90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_mpif90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_mpif90	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_mpif90 $@" | sed "s#$RUN_DIR##g" | sed "s#-I\./#-I#g" | sed "s#-L/var/tmp/[^ ]* #-Llib #" >>$command_file
+mpif90 "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_pp
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_pp	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/test/wrapper_scripts/wrap_pp	(revision 5095)
@@ -0,0 +1,2 @@
+echo "wrap_pp $@" | sed "s#$RUN_DIR##g" | sed "s#-I\./#-I#g" >>$command_file
+cpp  "$@"
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/README
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/README	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/README	(revision 5095)
@@ -0,0 +1,14 @@
+This directory contains the files necessary to set up a Subversion repository
+for the FCM tutorial.
+
+For example (in bash/ksh):
+
+(shell)$ cd fcm-release/tutorial/ # to this directory
+(shell)$ ./fcm-tutorial-repos-create /path/to/tutorial/repos
+
+The repository should be configured to allow users write access. You may find
+it easiest to simply allow anonymous access.
+
+A Trac environment should be configured to be associated with the tutorial
+repository. You then need to allow users write access. You may find it easiest
+to set up a number of guest accounts for this purpose.
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/fcm-tutorial-repos-create
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/fcm-tutorial-repos-create	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/fcm-tutorial-repos-create	(revision 5095)
@@ -0,0 +1,80 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+set -eu
+if (($# < 1)); then
+    echo "Usage: $(basename $0) DEST" >&2
+    exit 1
+fi
+DEST=$1
+if [[ -e $DEST ]]; then
+    echo "$DEST: destination already exists." >&2
+    exit 1
+fi
+THIS_HOME=$(cd $(dirname $0) && pwd)
+WORK_DIR=
+function FINALLY() {
+    trap '' ERR
+    trap '' EXIT
+    cd ~
+    if [[ -n $WORK_DIR ]]; then
+        rm -rf $WORK_DIR
+    fi
+}
+#-------------------------------------------------------------------------------
+function rsyncs() {
+    rsync -a --exclude=".svn" --checksum "$@"
+}
+#-------------------------------------------------------------------------------
+
+WORK_DIR=$(mktemp -d)
+trap FINALLY ERR
+trap FINALLY EXIT
+cd $WORK_DIR
+
+svnadmin create repos
+REPOS_URL=file://$PWD/repos
+svn checkout -q $REPOS_URL working-copy
+mkdir -p working-copy/tutorial/{trunk,branches,tags}
+
+# r1
+rsyncs $THIS_HOME/trunk-r1/* working-copy/tutorial/trunk/
+svn add -q working-copy/tutorial
+svn commit -q -m'tutorial: initial import.' working-copy
+svn update -q working-copy
+
+# r2
+TRUNK_SRC=working-copy/tutorial/trunk/src
+svn move -q $TRUNK_SRC/module/hello_num.f90 $TRUNK_SRC/module/hello_number.f90
+sed -i 's/Hello World/Hello Earth/' $TRUNK_SRC/module/hello_constants.f90
+sed -i 's/Hello World/Hello Earth/' $TRUNK_SRC/subroutine/hello_c.c
+svn commit -q -m'tutorial: World=Earth, and correct module name.' working-copy
+svn update -q working-copy
+
+rsyncs $THIS_HOME/hooks/* repos/hooks/
+curl -o repos/hooks/svnperms.py \
+    http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/svnperms.py
+chmod +x repos/hooks/svnperms.py
+cat >repos/hooks/svnperms.conf <<__SVNPERMS_CONF__
+[$(basename $DEST)]
+tutorial/branches/[^/]+/.* = *(add,remove,update)
+__SVNPERMS_CONF__
+mkdir -p $(dirname $DEST)
+svnadmin hotcopy repos $DEST
+echo "$DEST: tutorial repository created."
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/hooks/pre-commit
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/hooks/pre-commit	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/hooks/pre-commit	(revision 5095)
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -eu
+REPOS=$1
+TXN=$2
+SCRIPT=$REPOS/hooks/svnperms.py
+CONFIG=$REPOS/hooks/svnperms.conf
+$SCRIPT -r $REPOS -t $TXN -f $CONFIG
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/doc/hello.html
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/doc/hello.html	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/doc/hello.html	(revision 5095)
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+
+<html>
+<head>
+  <title>Hello</title>
+  <meta name="author" content="FCM Team">
+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body>
+  <h1>Hello</h1>
+  <p>Hello from the FCM team!</p>
+</body>
+</html>
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/fcm-make.cfg
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/fcm-make.cfg	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/fcm-make.cfg	(revision 5095)
@@ -0,0 +1,5 @@
+steps = extract build
+extract.ns = tutorial
+extract.location[tutorial] = $HERE
+extract.path-root[tutorial] = src
+build.target{task} = link
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/module/hello_constants.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/module/hello_constants.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/module/hello_constants.f90	(revision 5095)
@@ -0,0 +1,3 @@
+MODULE hello_constants
+CHARACTER(*), PARAMETER :: hello_string='Hello World!'
+END MODULE hello_constants
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/module/hello_num.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/module/hello_num.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/module/hello_num.f90	(revision 5095)
@@ -0,0 +1,17 @@
+MODULE hello_number
+
+IMPLICIT NONE
+
+PRIVATE
+INTEGER, PARAMETER :: i=0
+INTEGER, PARAMETER :: huge_number=HUGE(i)
+
+PUBLIC hello_huge_number
+
+CONTAINS
+SUBROUTINE hello_huge_number()
+CHARACTER(LEN=*), PARAMETER :: this='hello_huge_number'
+WRITE(*, '(A,I0)') this // ': maximum integer: ', huge_number
+END SUBROUTINE hello_huge_number
+
+END MODULE hello_number
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/program/hello.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/program/hello.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/program/hello.f90	(revision 5095)
@@ -0,0 +1,12 @@
+PROGRAM hello
+
+USE Hello_Constants, ONLY: hello_string
+
+IMPLICIT NONE
+INCLUDE 'hello_sub.interface'
+CHARACTER(*), PARAMETER :: this='hello'
+
+WRITE(*, '(A)') this // ': ' // TRIM(hello_string)
+CALL Hello_Sub()
+
+END PROGRAM hello
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/subroutine/hello_c.c
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/subroutine/hello_c.c	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/subroutine/hello_c.c	(revision 5095)
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+void hello_c_() {
+    printf ("%s\n", "hello_c: Hello World!");
+}
Index: LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/subroutine/hello_sub.f90
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/subroutine/hello_sub.f90	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/tutorial/trunk-r1/src/subroutine/hello_sub.f90	(revision 5095)
@@ -0,0 +1,15 @@
+SUBROUTINE hello_sub
+
+USE hello_constants, ONLY: hello_string
+USE hello_number, ONLY: hello_huge_number
+
+IMPLICIT NONE
+CHARACTER(*), PARAMETER :: this = 'hello_sub'
+! DEPENDS ON: hello_c.o
+EXTERNAL hello_c
+
+WRITE(*, '(A)') this // ': ' // TRIM(hello_string)
+CALL hello_huge_number()
+CALL hello_c()
+
+END SUBROUTINE hello_sub
Index: LMDZ6/branches/Amaury_dev/tools/fcm/usr/bin/fcm
===================================================================
--- LMDZ6/branches/Amaury_dev/tools/fcm/usr/bin/fcm	(revision 5095)
+++ LMDZ6/branches/Amaury_dev/tools/fcm/usr/bin/fcm	(revision 5095)
@@ -0,0 +1,31 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
+#
+# This file is part of FCM, tools for managing and building source code.
+#
+# FCM is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# FCM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with FCM. If not, see <http://www.gnu.org/licenses/>.
+#-------------------------------------------------------------------------------
+# Wrapper for "fcm":
+# * Should be installed at a location like "/usr/bin".
+# * Modify the default value for FCM_HOME_ROOT to suit your site.
+#-------------------------------------------------------------------------------
+if [[ -z ${FCM_HOME:-} ]]; then
+    FCM_HOME_ROOT=${FCM_HOME_ROOT:-/opt}
+    FCM_HOME=$FCM_HOME_ROOT/fcm
+    if [[ -n ${FCM_VERSION:-} && -d $FCM_HOME_ROOT/fcm-$FCM_VERSION ]]; then
+        FCM_HOME=$FCM_HOME_ROOT/fcm-$FCM_VERSION
+    fi
+fi
+exec $FCM_HOME/bin/$(basename $0) "$@"
