Index: vendors/fcm/1-5/COPYRIGHT.txt
===================================================================
--- vendors/fcm/1-5/COPYRIGHT.txt	(revision 1981)
+++ vendors/fcm/1-5/COPYRIGHT.txt	(revision 1981)
@@ -0,0 +1,109 @@
+!------------------------------------------------------------------------------!
+!              Flexible Configuration Management Software License              !
+!                                                                              !
+!     Please read this Software Licence as you will be bound by its terms      !
+!                           if you use the Software                            !
+!------------------------------------------------------------------------------!
+
+The Licensor:
+-------------
+
+The Met Office of FitzRoy Road, Exeter EX1 3PB, United Kingdom
+--------------------------------------------------------------------------------
+
+1. Licence.
+-----------
+
+The Met Office grants you a non-exclusive, royalty free; world-wide,
+transferable Licence to use, modify, copy and distribute the Flexible
+Configuration Management software ("the software") accompanying this License
+providing:
+
+a. you undertake to provide to the Met Office a copy of any modifications made
+   by you on the same terms contained within this licence agreement;
+
+b. modified files carry prominent notices stating that you changed the files
+   and the date of change;
+
+c. distribution of original or modified files is made free of charge under the
+   terms of this Licence;
+
+d. the appropriate copyright notices, the above copyright notice and a
+   disclaimer of warranty is included with the distribution.
+
+2. Ownership.
+-------------
+
+The Flexible Configuration Management software is Crown copyright and is
+reproduced with the permission of Met Office under delegated authority from
+the Controller of HMSO. The software and documentation are provided to you to
+allow you to exercise your rights under this License, which is granted to you.
+
+3. Duration.
+------------
+
+This license will remain in effect until terminated.
+ 
+4. Termination.
+---------------
+
+You may terminate this license at any time by removing all copies of the
+software from your system.  This License will terminate immediately without
+notice from us if you fail to comply with any of the provisions of this
+License or in the event of your breaching the terms of this licence you are
+given notice that the license has been terminated.  Upon termination you will
+delete all copies of the software and any related documentation.
+
+5. Disclaimer of Warranty.
+--------------------------
+
+a. THE MET OFFICE DISCLAIMS ALL WARRANTIES, REPRESENTATIONS AND PROMISES,
+   INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF SATISFACTORY QUALITY
+   AND FIT FOR THE PURPOSE.  NEITHER DOES THE MET OFFICE MAKE ANY
+   REPRESENTATIONS AS TO COMPATABILITY WITH YOUR OPERATING SYSTEMS AND
+   PLATFORMS.
+
+b. In no event does the Met Office warrant that the software or related
+   documentation will satisfy your requirements, that the software and
+   documentation will be without errors or defects or that the operation of
+   the software will be uninterrupted.
+
+c. IN NO EVENT WILL THE MET OFFICE BE LIABLE FOR ANY OTHER DAMAGES, INCLUDING
+   BUT NOT LIMITED TO DAMAGES FOR LOSS OF PROFITS DATA OR USE OF THE SOFTWARE
+   OR FOR ANY INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF THE MET
+   OFFICE HAS BEEN SPECIFICALLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+6. General Provisions.
+----------------------
+
+a. You will not do anything, relating to this software that will bring the Met
+   Office into disrepute.
+
+b. You will not use the name of the Met Office or any other contributor to
+   endorse or promote any products derived from the software without the
+   written permission of the Met Office.
+
+7. Acknowledgements.
+--------------------
+
+The logic to extract the calling interfaces of top level subroutines and
+functions from a Fortran source file is adapted from a script developed at
+ECMWF and is provided by kind permission of ECMWF under the same terms of this
+Licence.
+
+8. Entire Agreement.
+--------------------
+
+This License constitutes the entire agreement between us with respect to your
+rights or warranties for using the software and related documentation.  If any
+provision of this agreement is determined to be invalid or unenforceable the
+remaining provisions shall continue in full force.
+
+9. Governing Law.
+-----------------
+
+This Agreement is governed by and construed in accordance with the Laws of
+England.
+
+--------------------------------------------------------------------------------
+                                              © British Crown copyright 2006-10.
Index: vendors/fcm/1-5/LICENSE.html
===================================================================
--- vendors/fcm/1-5/LICENSE.html	(revision 1981)
+++ vendors/fcm/1-5/LICENSE.html	(revision 1981)
@@ -0,0 +1,154 @@
+<!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>Flexible Configuration Management Software License</title>
+  <meta name="copyright" content="&copy; British Crown Copyright" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<style type="text/css">
+/*<![CDATA[*/
+  <!--
+  body {
+    font: normal normal 10pt Arial, sans-serif;
+  }
+
+  h1 {
+    text-align: center;
+    text-decoration: underline;
+    font: normal bold 14pt Arial, sans-serif;
+  }
+
+  h2 {
+    text-align: left;
+    text-decoration: none;
+    font: normal normal 14pt Arial, sans-serif;
+  }
+
+  h3 {
+    text-align: left;
+    text-decoration: none;
+    font: normal bold 10pt Arial, sans-serif;
+  }
+
+  address {
+    text-align: right;
+    font: normal bold 11pt Arial, sans-serif;
+  }
+
+  ol li {
+    list-style: lower-alpha;
+  }
+  //-->
+/*]]>*/
+</style>
+</head>
+
+<body>
+  <h1>Flexible Configuration Management Software License</h1>
+
+  <h1>Please read this Software Licence as you will be bound by its terms if
+  you use the Software</h1>
+
+  <h2>The Licensor:</h2>
+
+  <h2>The Met Office of FitzRoy Road, Exeter EX1 3PB, United Kingdom</h2>
+
+  <h3>1. Licence.</h3>
+
+  <p>The Met Office grants you a non-exclusive, royalty free; world-wide,
+  transferable Licence to use, modify, copy and distribute the Flexible
+  Configuration Management software ("the software") accompanying this License
+  providing:</p>
+
+  <ol>
+    <li>you undertake to provide to the Met Office a copy of any modifications
+    made by you on the same terms contained within this licence agreement;</li>
+
+    <li>modified files carry prominent notices stating that you changed the
+    files and the date of change;</li>
+
+    <li>distribution of original or modified files is made free of charge under
+    the terms of this Licence;</li>
+
+    <li>the appropriate copyright notices, the above copyright notice and a
+    disclaimer of warranty is included with the distribution.</li>
+  </ol>
+
+  <h3>2. Ownership.</h3>
+
+  <p>The Flexible Configuration Management software is Crown copyright and is
+  reproduced with the permission of Met Office under delegated authority from
+  the Controller of HMSO. The software and documentation are provided to you to
+  allow you to exercise your rights under this License, which is granted to
+  you.</p>
+
+  <h3>3. Duration.</h3>
+
+  <p>This license will remain in effect until terminated.</p>
+
+  <h3>4. Termination.</h3>
+
+  <p>You may terminate this license at any time by removing all copies of the
+  software from your system. This License will terminate immediately without
+  notice from us if you fail to comply with any of the provisions of this
+  License or in the event of your breaching the terms of this licence you are
+  given notice that the license has been terminated. Upon termination you will
+  delete all copies of the software and any related documentation.</p>
+
+  <h3>5. Disclaimer of Warranty.</h3>
+
+  <ol>
+    <li>THE MET OFFICE DISCLAIMS ALL WARRANTIES, REPRESENTATIONS AND PROMISES,
+    INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF SATISFACTORY QUALITY
+    AND FIT FOR THE PURPOSE. NEITHER DOES THE MET OFFICE MAKE ANY
+    REPRESENTATIONS AS TO COMPATABILITY WITH YOUR OPERATING SYSTEMS AND
+    PLATFORMS.</li>
+
+    <li>In no event does the Met Office warrant that the software or related
+    documentation will satisfy your requirements, that the software and
+    documentation will be without errors or defects or that the operation of
+    the software will be uninterrupted.</li>
+
+    <li>IN NO EVENT WILL THE MET OFFICE BE LIABLE FOR ANY OTHER DAMAGES,
+    INCLUDING BUT NOT LIMITED TO DAMAGES FOR LOSS OF PROFITS DATA OR USE OF THE
+    SOFTWARE OR FOR ANY INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF
+    THE MET OFFICE HAS BEEN SPECIFICALLY ADVISED OF THE POSSIBILITY OF SUCH
+    DAMAGES.</li>
+  </ol>
+
+  <h3>6. General Provisions.</h3>
+
+  <ol>
+    <li>You will not do anything, relating to this software that will bring the
+    Met Office into disrepute.</li>
+
+    <li>You will not use the name of the Met Office or any other contributor to
+    endorse or promote any products derived from the software without the
+    written permission of the Met Office.</li>
+  </ol>
+
+  <h3>7. Acknowledgements.</h3>
+
+  <p>The logic to extract the calling interfaces of top level subroutines and
+  functions from a Fortran source file is adapted from a script developed at
+  ECMWF and is provided by kind permission of ECMWF under the same terms of this
+  Licence.</p>
+
+  <h3>8. Entire Agreement.</h3>
+
+  <p>This License constitutes the entire agreement between us with respect to
+  your rights or warranties for using the software and related documentation.
+  If any provision of this agreement is determined to be invalid or
+  unenforceable the remaining provisions shall continue in full force.</p>
+
+  <h3>9. Governing Law.</h3>
+
+  <p>This Agreement is governed by and construed in accordance with the Laws of
+  England.</p>
+
+  <address>
+    &copy; British Crown copyright 2006-10.
+  </address>
+</body>
+</html>
Index: vendors/fcm/1-5/README
===================================================================
--- vendors/fcm/1-5/README	(revision 1981)
+++ vendors/fcm/1-5/README	(revision 1981)
@@ -0,0 +1,4 @@
+FCM release 1-5 created from revision 3579.
+
+For further details please refer to the release notes
+which can be found in the directory doc/release_notes.
Index: vendors/fcm/1-5/bin/fcm
===================================================================
--- vendors/fcm/1-5/bin/fcm	(revision 1981)
+++ vendors/fcm/1-5/bin/fcm	(revision 1981)
@@ -0,0 +1,66 @@
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Fcm::CLI;
+use Fcm::Interactive;
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+    if (@ARGV && $ARGV[0] eq 'gui-internal') {
+        shift(@ARGV);
+        Fcm::Interactive::set_impl(
+            'Fcm::Interactive::InputGetter::GUI',
+            {geometry => shift(@ARGV)},
+        );
+    }
+    Fcm::CLI::invoke();
+}
+
+__END__
+
+=head1 NAME
+
+fcm
+
+=head1 SYNOPSIS
+
+fcm SUBCOMMAND [OPTIONS] [ARGUMENTS]
+
+=head1 OVERVIEW
+
+B<fcm> is the command line client for code management commands, the extract
+system and the build system of the Flexible Configuration Management (FCM)
+system.  For full detail of the system, please refer to the FCM user guide,
+which you should receive with this distribution in both HTML and PDF formats.
+
+Run "fcm help" to access the built-in tool documentation.
+
+=head1 AUTHOR
+
+FCM Team L<fcm-team@metoffice.gov.uk>.
+Please feedback any bug reports or feature requests to us by e-mail.
+
+=head1 SEE ALSO
+
+L<svn (1)|svn>,
+L<perl (1)| perl>,
+L<Fcm::CLI|Fcm::CLI>
+
+=head1 COPYRIGHT
+
+You can use this release of B<FCM> freely under the terms of the FCM LICENSE,
+which you should receive with this distribution.
+
+=cut
Index: vendors/fcm/1-5/bin/fcm_graphic_diff
===================================================================
--- vendors/fcm/1-5/bin/fcm_graphic_diff	(revision 1981)
+++ vendors/fcm/1-5/bin/fcm_graphic_diff	(revision 1981)
@@ -0,0 +1,96 @@
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use Getopt::Long qw{GetOptions};
+
+# ------------------------------------------------------------------------------
+
+my ($u, @label);
+GetOptions ('u' => \$u, 'L=s' => \@label);
+
+# Check existence of files
+for my $i (0 .. 1) {
+  die $ARGV[$i], ': not found, abort' unless $ARGV[$i] and -f $ARGV[$i];
+}
+
+my ($old, $new) = @ARGV;
+
+if ($old =~ m#.svn/empty-file$#) {
+  print 'Skipping new file', "\n\n";
+
+} elsif ($new =~ m#.svn/empty-file$#) {
+  print 'Skipping deleted file', "\n\n";
+
+} elsif (-z $old) {
+  print 'Skipping as old file is empty (or does not exist)', "\n\n";
+
+} elsif (-z $new) {
+  print 'Skipping as new file is empty (or deleted)', "\n\n";
+
+} elsif (-B $new) {
+  print 'Skipping binary file', "\n\n";
+
+} else {
+  # Print descriptions of files
+  if (@label >= 2) {
+    print '--- ', $label[0], "\n", '+++ ', $label[1], "\n\n";
+  }
+
+  # FCM_GRAPHIC_DIFF is the graphical diff tool command
+  my $cmd = (exists $ENV{FCM_GRAPHIC_DIFF} ? $ENV{FCM_GRAPHIC_DIFF} : 'xxdiff');
+
+  if ($cmd) {
+    my @options = ();
+
+    # Set options for labels if appropriate
+    if (@label >= 2) {
+      if ($cmd eq 'tkdiff') {
+        # Use tkdiff
+        @options = ('-L', $label[0], '-L', $label[1]);
+
+      } elsif ($cmd eq 'xxdiff') {
+        # Use xxdiff
+        @options = ('--title1', $label[0], '--title2', $label[1]);
+      }
+    }
+
+    # Execute the command
+    my @command = ($cmd, @options, $old, $new);
+    exec (@command) or die 'Cannot execute: ', join (' ', @command);
+  }
+
+  exit;
+}
+
+__END__
+
+=head1 NAME
+
+fcm_graphic_diff
+
+=head1 SYNOPSIS
+
+    fcm_graphic_diff [-u] [-L OLD_DESC] [-L NEW_DESC] OLD NEW
+
+=head1 DESCRIPTION
+
+Wrapper script which invokes a graphical diff tool. Its interface is
+compatible with the "svn diff" command and can be used in combination with
+its "--diff-cmd" option. The command prints the OLD_DESC and NEW_DESC if
+they are both set. The two arguments OLD and NEW must be set and are the
+files to compare. The graphical diff tool invoked depends on the value of
+the FCM_GRAPHIC_DIFF environment variable. The command exits if the
+environment variable is not set.
+
+=head1 COPYRIGHT
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/bin/fcm_graphic_merge
===================================================================
--- vendors/fcm/1-5/bin/fcm_graphic_merge	(revision 1981)
+++ vendors/fcm/1-5/bin/fcm_graphic_merge	(revision 1981)
@@ -0,0 +1,83 @@
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+my ($base, $mine, $older, $yours) = @ARGV;
+
+# FCM_GRAPHIC_MERGE is the graphical merge tool command
+my $cmd = (exists $ENV{FCM_GRAPHIC_MERGE} ? $ENV{FCM_GRAPHIC_MERGE} : 'xxdiff');
+
+my $rc  = 2;
+my $out = '';
+if ($cmd eq 'xxdiff') {
+  # Launch xxdiff
+  my @command   = ($cmd, qw/-m -M/, $base, qw/-O -X/, $mine, $older, $yours);
+  my ($cmd_out) = qx(@command);
+  my $cmd_rc    = $?;
+
+  # Parse output from xxdiff
+  if ($cmd_out) {
+    chomp $cmd_out;
+    if ($cmd_out eq 'NODECISION') {
+      $out = 'made no decision';
+      $rc  = 1;
+
+    } elsif ($cmd_out eq 'MERGED' and $cmd_rc) {
+      $out = 'not resolved all the conflicts';
+      $rc  = 1;
+
+    } else {
+      $out = lc ($cmd_out);
+      $rc  = 0;
+    }
+
+  } else {
+    print STDERR $cmd, ': failed, abort.', "\n";
+  }
+
+} else {
+  # Throw error for unknown/undefined graphic merge tool
+  print STDERR ($cmd ? $cmd . ': ' : ''),
+               'unknown/undefined graphic merge tool, abort.', "\n";
+}
+
+if ($rc == 1) {
+  # Merge unresolved
+  print 'You have ', $out, '.', "\n";
+
+} elsif ($rc == 0) {
+  # Merge resolved
+  print 'You ', $out, ' all the changes.', "\n";
+}
+
+exit $rc;
+
+__END__
+
+=head1 NAME
+
+fcm_graphic_merge
+
+=head1 SYNOPSIS
+
+    fcm_graphic_merge BASE MINE OLDER YOURS
+
+=head1 DESCRIPTION
+
+Wrapper script which invokes a graphical merge tool. It returns 0 on
+success, 1 if conflicts not resolved or 2 on failure. (This is similar to
+GNU diff3.) 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
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/bin/fcm_gui
===================================================================
--- vendors/fcm/1-5/bin/fcm_gui	(revision 1981)
+++ vendors/fcm/1-5/bin/fcm_gui	(revision 1981)
@@ -0,0 +1,1346 @@
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Cwd;
+use Fcm::Config;
+use Fcm::Keyword;
+use Fcm::Timer;
+use Fcm::Util;
+use File::Basename;
+use File::Spec;
+use Tk;
+use Tk::ROText;
+
+# ------------------------------------------------------------------------------
+
+# Argument
+if (@ARGV) {
+  my $dir = shift @ARGV;
+  chdir $dir if -d $dir;
+}
+
+# Get configuration settings
+my $config = Fcm::Config->new ();
+$config->get_config ();
+
+# ------------------------------------------------------------------------------
+
+# FCM subcommands
+my @subcmds = qw/CHECKOUT BRANCH STATUS DIFF ADD DELETE MERGE CONFLICTS COMMIT
+                 UPDATE SWITCH/;
+
+# Subcommands allowed when CWD is not a WC
+my @nwc_subcmds = qw/CHECKOUT BRANCH/;
+
+# Subcommands allowed, when CWD is a WC
+my @wc_subcmds = qw/STATUS BRANCH DIFF ADD DELETE MERGE CONFLICTS COMMIT UPDATE
+                    SWITCH/;
+
+# Subcommands that apply to WC only
+my @wco_subcmds = qw/BRANCH STATUS DIFF ADD DELETE MERGE CONFLICTS COMMIT UPDATE
+                     SWITCH/;
+
+# Subcommands that apply to top level WC only
+my @wcto_subcmds = qw/BRANCH MERGE COMMIT UPDATE SWITCH/;
+
+# Selected subcommand
+my $selsubcmd = '';
+
+# Selected subcommand is running?
+my $cmdrunning = 0;
+
+# PID of running subcommand
+my $cmdpid = undef;
+
+# List of subcommand frames
+my %subcmd_f;
+
+# List of subcommand buttons
+my %subcmd_b;
+
+# List of subcommand button help strings
+my %subcmd_help = (
+  BRANCH    => 'list information about, create or delete a branch.',
+  CHECKOUT  => 'check out a working copy from a repository.',
+  STATUS    => 'print the status of working copy files and directories.',
+  DIFF      => 'display the differences in modified files.',
+  ADD       => 'put files and directories under version control.',
+  DELETE    => 'remove files and directories from version control.',
+  MERGE     => 'merge changes into your working copy.',
+  CONFLICTS => 'use a graphical tool to resolve conflicts in your working copy.',
+  COMMIT    => 'send changes from your working copy to the repository.',
+  UPDATE    => 'bring changes from the repository into your working copy.',
+  SWITCH    => 'update your working copy to a different URL.',
+);
+
+for (keys %subcmd_help) {
+  $subcmd_help{$_} = 'Select the "' . lc ($_) . '" sub-command - ' .
+                     $subcmd_help{$_};
+}
+
+# List of subcommand button bindings (key name and underline position)
+my %subcmd_bind = (
+  BRANCH    => {KEY => '<Alt-Key-b>', U => 0},
+  CHECKOUT  => {KEY => '<Alt-Key-o>', U => 5},
+  STATUS    => {KEY => '<Alt-Key-s>', U => 0},
+  DIFF      => {KEY => '<Alt-Key-d>', U => 0},
+  ADD       => {KEY => '<Alt-Key-a>', U => 0},
+  DELETE    => {KEY => '<Alt-Key-t>', U => 4},
+  MERGE     => {KEY => '<Alt-Key-m>', U => 0},
+  CONFLICTS => {KEY => '<Alt-Key-f>', U => 3},
+  COMMIT    => {KEY => '<Alt-Key-c>', U => 0},
+  UPDATE    => {KEY => '<Alt-Key-u>', U => 0},
+  SWITCH    => {KEY => '<Alt-Key-w>', U => 1},
+);
+
+# List of subcommand variables
+my %subcmdvar = (
+  CWD       => cwd (),
+  WCT       => '',
+  CWD_URL   => '',
+  WCT_URL   => '',
+
+  BRANCH    => {
+    OPT     => 'info',
+    URL     => '',
+    NAME    => '',
+    TYPE    => 'DEV',
+    REVFLAG => 'NORMAL',
+    REV     => '',
+    TICKET  => '',
+    SRCTYPE => 'trunk',
+    S_CHD   => 0,
+    S_SIB   => 0,
+    S_OTH   => 0,
+    VERBOSE => 0,
+    OTHER   => '',
+  },
+
+  CHECKOUT  => {
+    URL     => '',
+    REV     => 'HEAD',
+    PATH    => '',
+    OTHER   => '',
+  },
+
+  STATUS    => {
+    USEWCT  => 0,
+    UPDATE  => 0,
+    VERBOSE => 0,
+    OTHER   => '',
+  },
+
+  DIFF      => {
+    USEWCT  => 0,
+    TOOL    => 'graphical',
+    BRANCH  => 0,
+    URL     => '',
+    OTHER   => '',
+  },
+
+  ADD       => {
+    USEWCT  => 0,
+    CHECK   => 1,
+    OTHER   => '',
+  },
+
+  DELETE    => {
+    USEWCT  => 0,
+    CHECK   => 1,
+    OTHER   => '',
+  },
+
+  MERGE     => {
+    USEWCT  => 1,
+    SRC     => '',
+    MODE    => 'automatic',
+    DRYRUN  => 0,
+    VERBOSE => 0,
+    REV     => '',
+    OTHER   => '',
+  },
+
+  CONFLICTS => {
+    USEWCT  => 0,
+    OTHER   => '',
+  },
+
+  COMMIT    => {
+    USEWCT  => 1,
+    DRYRUN  => 0,
+    OTHER   => '',
+  },
+
+  UPDATE    => {
+    USEWCT  => 1,
+    OTHER   => '',
+  },
+
+  SWITCH    => {
+    USEWCT  => 1,
+    URL     => '',
+    OTHER   => '',
+  },
+);
+
+# List of action buttons
+my %action_b;
+
+# List of action button help strings
+my %action_help = (
+  QUIT  => 'Quit fcm gui',
+  HELP  => 'Print help to the output text box for the selected sub-command',
+  CLEAR => 'Clear the output text box',
+  RUN   => 'Run the selected sub-command',
+);
+
+# List of action button bindings
+my %action_bind = (
+  QUIT  => {KEY => '<Control-Key-q>', U => undef},
+  HELP  => {KEY => '<F1>'           , U => undef},
+  CLEAR => {KEY => '<Alt-Key-l>'    , U => 1},
+  RUN   => {KEY => '<Alt-Key-r>'    , U => 0},
+);
+
+# List of branch subcommand options
+my %branch_opt = (
+  INFO   => undef,
+  CREATE => undef,
+  DELETE => undef,
+  LIST   => undef,
+);
+
+# List of branch create types
+my %branch_type = (
+  'DEV'         => undef,
+  'DEV::SHARE'  => undef,
+  'TEST'        => undef,
+  'TEST::SHARE' => undef,
+  'PKG'         => undef,
+  'PKG::SHARE'  => undef,
+  'PKG::CONFIG' => undef,
+  'PKG::REL'    => undef,
+);
+
+# List of branch create source type
+my %branch_srctype = (
+  TRUNK  => undef,
+  BRANCH => undef,
+);
+
+# List of branch create revision prefix option
+my %branch_revflag = (
+  NORMAL => undef,
+  NUMBER => undef,
+  NONE   => undef,
+);
+
+# List of branch info/delete options
+my %branch_info_opt = (
+  S_CHD   => 'Show children',
+  S_SIB   => 'Show siblings',
+  S_OTH   => 'Show other',
+  VERBOSE => 'Print extra information',
+);
+
+# List of diff display options
+my %diff_display_opt = (
+  default   => 'Default mode',
+  graphical => 'Graphical tool',
+  trac      => 'Trac (only for diff relative to the base of the branch)',
+);
+
+# Text in the status bar
+my $statustext = '';
+
+# ------------------------------------------------------------------------------
+
+my $mw = MainWindow->new ();
+
+my $mw_title = 'FCM GUI';
+$mw->title ($mw_title);
+
+# Frame containing subcommand selection buttons
+my $top_f = $mw->Frame ()->grid (
+  '-row'    => 0,
+  '-column' => 0,
+  '-sticky' => 'w',
+);
+
+# Frame containing subcommand options
+my $mid_f = $mw->Frame ()->grid (
+  '-row'    => 1,
+  '-column' => 0,
+  '-sticky' => 'ew',
+);
+
+# Frame containing action buttons
+my $bot_f = $mw->Frame ()->grid (
+  '-row'    => 2,
+  '-column' => 0,
+  '-sticky' => 'ew',
+);
+
+# Text box to display output
+my $out_t  = $mw->Scrolled ('ROText', '-scrollbars' => 'osow')->grid (
+  '-row'    => 3,
+  '-column' => 0,
+  '-sticky' => 'news',
+);
+
+# Text box - allow scroll with mouse wheel
+$out_t->bind (
+  '<4>' => sub {
+    $_[0]->yview ('scroll', -1, 'units') unless $Tk::strictMotif;
+  },
+);
+
+$out_t->bind (
+  '<5>' => sub {
+    $_[0]->yview ('scroll', +1, 'units') unless $Tk::strictMotif;
+  },
+);
+
+# Status bar
+$mw->Label (
+  '-textvariable' => \$statustext,
+  '-relief'       => 'groove',
+)->grid (
+  '-row'    => 4,
+  '-column' => 0,
+  '-sticky' => 'ews',
+);
+
+# Main window grid configure
+{
+  my ($cols, $rows) = $mw->gridSize ();
+  $mw->gridColumnconfigure ($_, '-weight' => 1) for (0 .. $cols - 1);
+  $mw->gridRowconfigure    ( 3, '-weight' => 1);
+}
+
+# Frame grid configure
+{
+  my ($cols, $rows) = $mid_f->gridSize ();
+  $bot_f->gridColumnconfigure (3, '-weight' => 1);
+}
+
+$mid_f->gridRowconfigure    (0, '-weight' => 1);
+$mid_f->gridColumnconfigure (0, '-weight' => 1);
+
+# ------------------------------------------------------------------------------
+
+# Buttons to select subcommands
+{
+  my $col = 0;
+  for my $name (@subcmds) {
+    $subcmd_b{$name} = $top_f->Button (
+      '-text'    => uc (substr ($name, 0, 1)) . lc (substr ($name, 1)),
+      '-command' => [\&button_clicked, $name],
+      '-width'   => 8,
+    )->grid (
+      '-row'    => 0,
+      '-column' => $col++,
+      '-sticky' => 'w',
+    );
+
+    $subcmd_b{$name}->bind ('<Enter>', sub {$statustext = $subcmd_help{$name}});
+    $subcmd_b{$name}->bind ('<Leave>', sub {$statustext = ''});
+
+    $subcmd_b{$name}->configure ('-underline' => $subcmd_bind{$name}{U})
+      if defined $subcmd_bind{$name}{U};
+
+    $mw->bind ($subcmd_bind{$name}{KEY}, sub {$subcmd_b{$name}->invoke});
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+# Frames to contain subcommands options
+{
+  my %row = ();
+
+  for my $name (@subcmds) {
+    $subcmd_f{$name} = $mid_f->Frame ();
+    $subcmd_f{$name}->gridColumnconfigure (1, '-weight' => 1);
+
+    $row{$name} = 0;
+
+    # Widgets common to all sub-commands
+    $subcmd_f{$name}->Label ('-text' => 'Current working directory: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Label ('-textvariable' => \($subcmdvar{CWD}))->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'w',
+    );
+  }
+
+  # Widgets common to all sub-commands that apply to working copies
+  for my $name (@wco_subcmds) {
+    my @labtxts = (
+      'Corresponding URL: ',
+      'Working copy top: ',
+      'Corresponding URL: ',
+    );
+    my @varrefs = \(
+      $subcmdvar{URL_CWD},
+      $subcmdvar{WCT},
+      $subcmdvar{URL_WCT},
+    );
+
+    for my $i (0 .. $#varrefs) {
+      $subcmd_f{$name}->Label ('-text' => $labtxts[$i])->grid (
+        '-row'    => $row{$name},
+        '-column' => 0,
+        '-sticky' => 'w',
+      );
+      $subcmd_f{$name}->Label ('-textvariable' => $varrefs[$i])->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+    }
+
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Apply sub-command to working copy top',
+      '-variable' => \($subcmdvar{$name}{USEWCT}),
+      '-state'    => (grep ({$_ eq $name} @wcto_subcmds) ? 'disabled' : 'normal'),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+  }
+
+  # Widget for the Branch sub-command
+  {
+    my $name = 'BRANCH';
+
+    # Radio buttons to select the sub-option of the branch sub-command
+    my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    my $col = 0;
+    for my $key (sort keys %branch_opt) {
+      my $opt = lc $key;
+
+      $branch_opt{$key} = $opt_f->Radiobutton (
+        '-text'     => $opt,
+        '-value'    => $opt,
+        '-variable' => \($subcmdvar{$name}{OPT}),
+        '-state'    => 'normal',
+      )->grid (
+        '-row'      => 0,
+        '-column'   => $col++,
+        '-sticky'   => 'w',
+      );
+    }
+
+    # Label and entry box for specifying URL
+    $subcmd_f{$name}->Label ('-text' => 'URL: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{URL}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and entry box for specifying create branch name
+    $subcmd_f{$name}->Label (
+      '-text' => 'Branch name (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{NAME}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and entry box for specifying create branch source revision
+    $subcmd_f{$name}->Label (
+      '-text' => 'Source revision (create/list only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{REV}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and radio buttons box for specifying create branch type
+    $subcmd_f{$name}->Label (
+      '-text' => 'Branch type (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (sort keys %branch_type) {
+        my $txt = lc $key;
+        my $opt = $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{TYPE}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Label and radio buttons box for specifying create source type
+    $subcmd_f{$name}->Label (
+      '-text' => 'Source type (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (sort keys %branch_srctype) {
+        my $txt = lc $key;
+        my $opt = lc $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{SRCTYPE}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Label and radio buttons box for specifying create prefix option
+    $subcmd_f{$name}->Label (
+      '-text' => 'Prefix option (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (sort keys %branch_revflag) {
+        my $txt = lc $key;
+        my $opt = $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{REVFLAG}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Label and entry box for specifying ticket number
+    $subcmd_f{$name}->Label (
+      '-text' => 'Related Trac ticket(s) (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{TICKET}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Check button for info/delete
+    # --show-children, --show-siblings, --show-other, --verbose
+    $subcmd_f{$name}->Label (
+      '-text' => 'Options for info/delete only: ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+
+      for my $key (sort keys %branch_info_opt) {
+        $opt_f->Checkbutton (
+          '-text'     => $branch_info_opt{$key},
+          '-variable' => \($subcmdvar{$name}{$key}),
+        )->grid (
+          '-row'    => 0,
+          '-column' => $col++,
+          '-sticky' => 'w',
+        );
+      }
+    }
+  }
+
+  # Widget for the Checkout sub-command
+  {
+    my $name = 'CHECKOUT';
+
+    # Label and entry boxes for specifying URL and revision
+    my @labtxts = (
+      'URL: ',
+      'Revision: ',
+      'Path: ',
+    );
+    my @varrefs = \(
+      $subcmdvar{$name}{URL},
+      $subcmdvar{$name}{REV},
+      $subcmdvar{$name}{PATH},
+    );
+
+    for my $i (0 .. $#varrefs) {
+      $subcmd_f{$name}->Label ('-text' => $labtxts[$i])->grid (
+        '-row'    => $row{$name},
+        '-column' => 0,
+        '-sticky' => 'w',
+      );
+      $subcmd_f{$name}->Entry (
+        '-textvariable' => $varrefs[$i],
+      )->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'ew',
+      );
+    }
+  }
+
+  # Widget for the Status sub-command
+  {
+    my $name = 'STATUS';
+
+    # Checkbuttons for various options
+    my @labtxts = (
+      'Display update information',
+      'Print extra information',
+    );
+    my @varrefs = \(
+      $subcmdvar{$name}{UPDATE},
+      $subcmdvar{$name}{VERBOSE},
+    );
+
+    for my $i (0 .. $#varrefs) {
+      $subcmd_f{$name}->Checkbutton (
+        '-text'     => $labtxts[$i],
+        '-variable' => $varrefs[$i],
+      )->grid (
+        '-row'        => $row{$name}++,
+        '-column'     => 0,
+        '-columnspan' => 2,
+        '-sticky'     => 'w',
+      );
+    }
+  }
+
+  # Widget for the Diff sub-command
+  {
+    my $name = 'DIFF';
+
+    my $entry;
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Show differences relative to the base of the branch',
+      '-variable' => \($subcmdvar{$name}{BRANCH}),
+      '-command'  => sub {
+        $entry->configure (
+          '-state' => ($subcmdvar{$name}{BRANCH} ? 'normal' : 'disabled'),
+        );
+      },
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    # Label and radio buttons box for specifying tool
+    $subcmd_f{$name}->Label (
+      '-text' => 'Display diff in: ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (qw/default graphical trac/) {
+        my $txt = $diff_display_opt{$key};
+        my $opt = $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{TOOL}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    $subcmd_f{$name}->Label ('-text' => 'Branch URL')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    $entry = $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{URL}),
+      '-state'        => ($subcmdvar{$name}{BRANCH} ? 'normal' : 'disabled'),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+
+  # Widget for the Add/Delete sub-command
+  for my $name (qw/ADD DELETE/) {
+
+    # Checkbuttons for various options
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Check for files or directories not under version control',
+      '-variable' => \($subcmdvar{$name}{CHECK}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+  }
+
+  # Widget for the Merge sub-command
+  {
+    my $name = 'MERGE';
+
+    # Label and radio buttons box for specifying merge mode
+    $subcmd_f{$name}->Label (
+      '-text' => 'Mode: ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (qw/automatic custom reverse/) {
+        my $txt = lc $key;
+        my $opt = $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{MODE}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Check buttons for dry-run
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Dry run',
+      '-variable' => \($subcmdvar{$name}{DRYRUN}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    # Check buttons for verbose mode
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Print extra information',
+      '-variable' => \($subcmdvar{$name}{VERBOSE}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    # Label and entry boxes for specifying merge source
+    $subcmd_f{$name}->Label (
+      '-text' => 'Source (automatic/custom only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{SRC}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and entry boxes for specifying merge revision (range)
+    $subcmd_f{$name}->Label (
+      '-text' => 'Revision (custom/reverse only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{REV}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+
+  # Widget for the Commit sub-command
+  {
+    my $name = 'COMMIT';
+
+    # Checkbuttons for various options
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Dry run',
+      '-variable' => \($subcmdvar{$name}{DRYRUN}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+  }
+
+  # Widget for the Switch sub-command
+  {
+    my $name = 'SWITCH';
+
+    # Label and entry boxes for specifying switch URL
+    $subcmd_f{$name}->Label ('-text' => 'URL: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{URL}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+
+  # Widgets common to all sub-commands
+  for my $name (@subcmds) {
+    $subcmd_f{$name}->Label ('-text' => 'Other options: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{OTHER}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+# Buttons to perform main actions
+{
+  my $col = 0;
+  for my $name (qw/QUIT HELP CLEAR RUN/) {
+    $action_b{$name} = $bot_f->Button (
+      '-text'    => uc (substr ($name, 0, 1)) . lc (substr ($name, 1)),
+      '-command' => [\&button_clicked, $name],
+      '-width'   => 8,
+    )->grid (
+      '-row'    => 0,
+      '-column' => $col++,
+      '-sticky' => ($name eq 'RUN' ? 'ew' : 'w'),
+    );
+
+    $action_b{$name}->bind ('<Enter>', sub {$statustext = $action_help{$name}});
+    $action_b{$name}->bind ('<Leave>', sub {$statustext = ''});
+
+    $action_b{$name}->configure ('-underline' => $action_bind{$name}{U})
+      if defined $action_bind{$name}{U};
+
+    $mw->bind ($action_bind{$name}{KEY}, sub {$action_b{$name}->invoke});
+  }
+}
+
+&change_cwd ($subcmdvar{CWD});
+
+# ------------------------------------------------------------------------------
+
+# Handle the situation when the user attempts to quit the window while a
+# sub-command is running
+
+$mw->protocol ('WM_DELETE_WINDOW', sub {
+  if (defined $cmdpid) {
+    my $ans = $mw->messageBox (
+      '-title'   => $mw_title,
+      '-message' => $selsubcmd . ' is still running. Really quit?',
+      '-type'    => 'YesNo',
+      '-default' => 'No',
+    );
+
+    if ($ans eq 'Yes') {
+      kill 9, $cmdpid; # Need to kill the sub-process before quitting
+
+    } else {
+      return; # Do not quit
+    }
+  }
+
+  exit;
+});
+
+MainLoop;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &change_cwd ($dir);
+#
+# DESCRIPTION
+#   Change current working directory to $dir
+# ------------------------------------------------------------------------------
+
+sub change_cwd {
+  my $dir = $_[0];
+  my @allowed_subcmds = (&is_wc ($dir) ? @wc_subcmds : @nwc_subcmds);
+
+  for my $subcmd (@subcmds) {
+    if (grep {$_ eq $subcmd} @allowed_subcmds) {
+      $subcmd_b{$subcmd}->configure ('-state' => 'normal');
+
+    } else {
+      $subcmd_b{$subcmd}->configure ('-state' => 'disabled');
+    }
+  }
+
+  &display_subcmd_frame ($allowed_subcmds[0])
+    if not grep {$_ eq $selsubcmd} @allowed_subcmds;
+
+  chdir $dir;
+  $subcmdvar{CWD} = $dir;
+
+  if (&is_wc ($dir)) {
+    $subcmdvar{WCT}     = &get_wct ($dir);
+    $subcmdvar{URL_CWD} = &get_url_of_wc ($dir);
+    $subcmdvar{URL_WCT} = &get_url_of_wc ($subcmdvar{WCT});
+
+    $branch_opt{INFO}  ->configure ('-state' => 'normal');
+    $branch_opt{DELETE}->configure ('-state' => 'normal');
+    $subcmdvar{BRANCH}{OPT} = 'info';
+
+  } else {
+    $branch_opt{INFO}  ->configure ('-state' => 'disabled');
+    $branch_opt{DELETE}->configure ('-state' => 'disabled');
+    $subcmdvar{BRANCH}{OPT} = 'create';
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &button_clicked ($name);
+#
+# DESCRIPTION
+#   Call back function to handle a click on a command button named $name.
+# ------------------------------------------------------------------------------
+
+sub button_clicked {
+  my $name = $_[0];
+
+  if (grep {$_ eq $name} keys %subcmd_b) {
+    &display_subcmd_frame ($name);
+
+  } elsif ($name eq 'CLEAR') {
+    $out_t->delete ('1.0', 'end');
+
+  } elsif ($name eq 'QUIT') {
+    exit;
+
+  } elsif ($name eq 'HELP') {
+    &invoke_cmd ('help ' . lc ($selsubcmd));
+
+  } elsif ($name eq 'RUN') {
+    &invoke_cmd (&setup_cmd ($selsubcmd));
+
+  } else {
+    $out_t->insert ('end', $name . ': function to be implemented' . "\n");
+    $out_t->yviewMoveto (1);
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &display_subcmd_frame ($name);
+#
+# DESCRIPTION
+#   Change selected subcommand to $name, and display the frame containing the
+#   widgets for configuring the options and arguments of that subcommand.
+# ------------------------------------------------------------------------------
+
+sub display_subcmd_frame {
+  my $name = $_[0];
+
+  if ($selsubcmd ne $name and not $cmdrunning) {
+    $subcmd_b{$name     }->configure ('-relief' => 'sunken');
+    $subcmd_b{$selsubcmd}->configure ('-relief' => 'raised') if $selsubcmd;
+
+    $subcmd_f{$name     }->grid ('-sticky' => 'new');
+    $subcmd_f{$selsubcmd}->gridForget if $selsubcmd;
+
+    $selsubcmd = $name;
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $pos = &get_wm_pos ();
+#
+# DESCRIPTION
+#   Returns the position part of the geometry string of the main window.
+# ------------------------------------------------------------------------------
+
+sub get_wm_pos {
+  my $geometry = $mw->geometry ();
+  $geometry =~ /^=?(?:\d+x\d+)?([+-]\d+[+-]\d+)$/;
+  return $1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $command = &setup_cmd ($name);
+#
+# DESCRIPTION
+#   Setup the the system command for the sub-command $name.
+# ------------------------------------------------------------------------------
+
+sub setup_cmd {
+  my $name = $_[0];
+  my $cmd  = '';
+
+  if ($name eq 'BRANCH') {
+    $cmd .= lc ($name);
+    if ($subcmdvar{$name}{OPT} eq 'create') {
+      $cmd .= ' -c --svn-non-interactive';
+      $cmd .= ' -n '     . $subcmdvar{$name}{NAME} if $subcmdvar{$name}{NAME};
+      $cmd .= ' -t '     . $subcmdvar{$name}{TYPE};
+      $cmd .= ' --rev-flag ' . $subcmdvar{$name}{REVFLAG};
+      $cmd .= ' -r ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+      $cmd .= ' -k ' . $subcmdvar{$name}{TICKET} if $subcmdvar{$name}{TICKET};
+      $cmd .= ' --branch-of-branch ' if $subcmdvar{$name}{SRCTYPE} eq 'branch';
+
+    } elsif ($subcmdvar{$name}{OPT} eq 'delete') {
+      $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
+      $cmd .= ' -d --svn-non-interactive';
+
+    } elsif ($subcmdvar{$name}{OPT} eq 'list') {
+      $cmd .= ' -l';
+      $cmd .= ' -r ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+
+    } else {
+      $cmd .= ' -i';
+      $cmd .= ' --show-children' if $subcmdvar{$name}{S_CHD};
+      $cmd .= ' --show-siblings' if $subcmdvar{$name}{S_SIB};
+      $cmd .= ' --show-other'    if $subcmdvar{$name}{S_OTH};
+      $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
+    }
+    $cmd .= ' ' . $subcmdvar{$name}{URL}   if $subcmdvar{$name}{URL};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'CHECKOUT') {
+    $cmd .= lc ($name);
+    $cmd .= ' -r' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+    $cmd .= ' ' . $subcmdvar{$name}{URL};
+    $cmd .= ' ' . $subcmdvar{$name}{PATH} if $subcmdvar{$name}{PATH};
+
+  } elsif ($name eq 'STATUS') {
+    $cmd .= lc ($name);
+    $cmd .= ' -u' if $subcmdvar{$name}{UPDATE};
+    $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'DIFF') {
+    $cmd .= lc ($name);
+    $cmd .= ' -g' if $subcmdvar{$name}{TOOL} eq 'graphical';
+
+    if ($subcmdvar{$name}{BRANCH}) {
+      $cmd .= ' -b';
+      $cmd .= ' -t' if $subcmdvar{$name}{TOOL} eq 'trac';
+      $cmd .= ' ' . $subcmdvar{$name}{URL} if $subcmdvar{$name}{URL};
+    }
+
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'ADD' or $name eq 'DELETE') {
+    $cmd .= lc ($name);
+    $cmd .= ' -c' if $subcmdvar{$name}{CHECK};
+    $cmd .= ' --non-interactive'
+      if $name eq 'DELETE' and not $subcmdvar{$name}{CHECK};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'MERGE') {
+    $cmd .= lc ($name);
+
+    if ($subcmdvar{$name}{MODE} ne 'automatic') {
+      $cmd .= ' --' . $subcmdvar{$name}{MODE};
+      $cmd .= ' --revision ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+    }
+
+    $cmd .= ' --dry-run' if $subcmdvar{$name}{DRYRUN};
+    $cmd .= ' -v'        if $subcmdvar{$name}{VERBOSE};
+    $cmd .= ' ' . $subcmdvar{$name}{SRC}   if $subcmdvar{$name}{SRC};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'CONFLICTS') {
+    $cmd .= lc ($name);
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'COMMIT') {
+    $cmd .= lc ($name);
+    $cmd .= ' --dry-run' if $subcmdvar{$name}{DRYRUN};
+    $cmd .= ' --svn-non-interactive';
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'SWITCH') {
+    $cmd .= lc ($name);
+    $cmd .= ' ' . $subcmdvar{$name}{URL}   if $subcmdvar{$name}{URL};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'UPDATE') {
+    $cmd .= lc ($name);
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  }
+
+  return $cmd;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_cmd ($cmd);
+#
+# DESCRIPTION
+#   Invoke the command $cmd.
+# ------------------------------------------------------------------------------
+
+sub invoke_cmd {
+  my $cmd      = $_[0];
+  return unless $cmd;
+
+  my $disp_cmd = 'fcm ' . $cmd;
+  $cmd         = (index ($cmd, 'help ') == 0)
+                 ? $disp_cmd
+                 : ('fcm gui-internal ' . &get_wm_pos () . ' ' . $cmd);
+
+  # Change directory to working copy top if necessary
+  if ($subcmdvar{$selsubcmd}{USEWCT} and $subcmdvar{WCT} ne $subcmdvar{CWD}) {
+    chdir $subcmdvar{WCT};
+    $out_t->insert ('end', 'cd ' . $subcmdvar{WCT} . "\n");
+    $out_t->yviewMoveto (1);
+  }
+
+  # Report start of command
+  $out_t->insert ('end', timestamp_command ($disp_cmd, 'Start'));
+  $out_t->yviewMoveto (1);
+
+  # Open the command as a pipe
+  if ($cmdpid = open CMD, '-|', $cmd . ' 2>&1') {
+    # Disable all action buttons
+    $action_b{$_}->configure ('-state' => 'disabled') for (keys %action_b);
+    $cmdrunning = 1;
+
+    # Set up a file event to read output from the command
+    $mw->fileevent (\*CMD, readable => sub {
+      if (sysread CMD, my ($buf), 1024) {
+        # Insert text into the output text box as it becomes available
+        $out_t->insert ('end', $buf);
+        $out_t->yviewMoveto (1);
+
+      } else {
+        # Delete the file event and close the file when the command finishes
+        $mw->fileevent(\*CMD, readable => '');
+        close CMD;
+        $cmdpid = undef;
+
+        # Check return status
+        if ($?) {
+          $out_t->insert (
+            'end', '"' . $disp_cmd . '" failed (' . $? . ')' . "\n",
+          );
+          $out_t->yviewMoveto (1);
+        }
+
+        # Report end of command
+        $out_t->insert ('end', timestamp_command ($disp_cmd, 'End'));
+        $out_t->yviewMoveto (1);
+
+        # Change back to CWD if necessary
+        if ($subcmdvar{$selsubcmd}{USEWCT} and
+            $subcmdvar{WCT} ne $subcmdvar{CWD}) {
+          chdir $subcmdvar{CWD};
+          $out_t->insert ('end', 'cd ' . $subcmdvar{CWD} . "\n");
+          $out_t->yviewMoveto (1);
+        }
+
+        # Enable all action buttons again
+        $action_b{$_}->configure ('-state' => 'normal') for (keys %action_b);
+        $cmdrunning = 0;
+
+        # If the command is "checkout", change directory to working copy
+        if (lc ($selsubcmd) eq 'checkout' && $subcmdvar{CHECKOUT}{URL}) {
+          my $url = Fcm::Keyword::expand($subcmdvar{CHECKOUT}{URL});
+          my $dir = $subcmdvar{CHECKOUT}{PATH}
+                  ? $subcmdvar{CHECKOUT}{PATH}
+                  : basename $url;
+          $dir    = File::Spec->rel2abs ($dir);
+          &change_cwd ($dir);
+
+        # If the command is "switch", change URL
+        } elsif (lc ($selsubcmd) eq 'switch') {
+          $subcmdvar{URL_CWD} = &get_url_of_wc ($subcmdvar{CWD}, 1);
+          $subcmdvar{URL_WCT} = &get_url_of_wc ($subcmdvar{WCT}, 1);
+        }
+      }
+      1;
+    });
+
+  } else {
+    $mw->messageBox (
+      '-title'   => 'Error',
+      '-message' => 'Error running "' . $cmd . '"',
+      '-icon'    => 'error',
+    );
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+
+__END__
+
+=head1 NAME
+
+fcm_gui
+
+=head1 SYNOPSIS
+
+fcm_gui [DIR]
+
+=head1 DESCRIPTION
+
+The fcm_gui command is a simple graphical user interface for some of the
+commands of the FCM system. The optional argument DIR modifies the initial
+working directory.
+
+=head1 COPYRIGHT
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/bin/fcm_internal
===================================================================
--- vendors/fcm/1-5/bin/fcm_internal	(revision 1981)
+++ vendors/fcm/1-5/bin/fcm_internal	(revision 1981)
@@ -0,0 +1,615 @@
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use Fcm::Timer qw{timestamp_command};
+
+# Function declarations
+sub catfile;
+sub basename;
+sub dirname;
+
+# ------------------------------------------------------------------------------
+
+# Module level variables
+my %unusual_tool_name = ();
+
+# ------------------------------------------------------------------------------
+
+MAIN: {
+  # Name of program
+  my $this = basename $0;
+
+  # Arguments
+  my $subcommand = shift @ARGV;
+  my ($function, $type) = split /:/, $subcommand; 
+
+  my ($srcpackage, $src, $target, $requirepp, @objects, @blockdata);
+  
+  if ($function eq 'archive') {
+    ($target, @objects) = @ARGV;
+
+  } elsif ($function eq 'load') {
+    ($srcpackage, $src, $target, @blockdata) = @ARGV;
+
+  } else {
+    ($srcpackage, $src, $target, $requirepp) = @ARGV;
+  }
+
+  # Set up hash reference for all the required information
+  my %info = (
+    SRCPACKAGE => $srcpackage,
+    SRC        => $src,
+    TYPE       => $type,
+    TARGET     => $target,
+    REQUIREPP  => $requirepp,
+    OBJECTS    => \@objects,
+    BLOCKDATA  => \@blockdata,
+  );
+
+  # Get list of unusual tools
+  my $i = 0;
+  while (my $label = &get_env ('FCM_UNUSUAL_TOOL_LABEL' . $i)) {
+    my $value = &get_env ('FCM_UNUSUAL_TOOL_VALUE' . $i);
+    $unusual_tool_name{$label} = $value;
+    $i++;
+  }
+
+  # Invoke the action
+  my $rc = 0;
+  if ($function eq 'compile') {
+    $rc = &compile (\%info);
+
+  } elsif ($function eq 'load') {
+    $rc = &load (\%info);
+
+  } elsif ($function eq 'archive') {
+    $rc = &archive (\%info);
+
+  } else {
+    print STDERR $this, ': incorrect usage, abort';
+    $rc = 1;
+  }
+
+  # Throw error if action failed
+  if ($rc) {
+    print STDERR $this, ' ', $function, ' failed (', $rc, ')', "\n";
+    exit 1;
+
+  } else {
+    exit;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &compile (\%info);
+#
+# DESCRIPTION
+#   This method invokes the correct compiler with the correct options to
+#   compile the source file into the required target. The argument $info is a
+#   hash reference set up in MAIN. The following environment variables are
+#   used, where * is the source file type (F for Fortran, and C for C/C++):
+# 
+#   *C          - compiler command
+#   *C_OUTPUT   - *C option to specify the name of the output file
+#   *C_DEFINE   - *C option to declare a pre-processor def
+#   *C_INCLUDE  - *C option to declare an include directory
+#   *C_MODSEARCH- *C option to declare a module search directory
+#   *C_COMPILE  - *C option to ask the compiler to perform compile only
+#   *CFLAGS     - *C user options
+#   *PPKEYS     - list of pre-processor defs (may have sub-package suffix)
+#   FCM_VERBOSE - verbose level
+#   FCM_OBJDIR  - destination directory of object file
+#   FCM_TMPDIR  - temporary destination directory of object file
+# ------------------------------------------------------------------------------
+
+sub compile {
+  my $info = shift;
+
+  # Verbose mode
+  my $verbose = &get_env ('FCM_VERBOSE');
+  $verbose    = 1 unless defined ($verbose);
+
+  my @command = ();
+
+  # Guess file type for backward compatibility
+  my $type = $info->{TYPE} ? $info->{TYPE} : &guess_file_type ($info->{SRC});
+
+  # Compiler
+  push @command, &get_env ($type . 'C', 1);
+
+  # Compile output target (typical -o option)
+  push @command, &get_env ($type . 'C_OUTPUT', 1), $info->{TARGET};
+
+  # Pre-processor definition macros
+  if ($info->{REQUIREPP}) {
+    my @ppkeys = split /\s+/, &select_flags ($info, $type . 'PPKEYS');
+    my $defopt = &get_env ($type . 'C_DEFINE', 1);
+
+    push @command, (map {$defopt . $_} @ppkeys);
+  }
+
+  # Include search path
+  my $incopt  = &get_env ($type . 'C_INCLUDE', 1);
+  my @incpath = split /:/, &get_env ('FCM_INCPATH');
+  push @command, (map {$incopt . $_} @incpath);
+
+  # Compiled module search path
+  my $modopt  = &get_env ($type . 'C_MODSEARCH');
+  if ($modopt) {
+    push @command, (map {$modopt . $_} @incpath);
+  }
+
+  # Other compiler flags
+  my $flags = &select_flags ($info, $type . 'FLAGS');
+  push @command, $flags if $flags;
+
+  my $compile_only = &get_env ($type . 'C_COMPILE');
+  if ($flags !~ /(?:^|\s)$compile_only\b/) {
+    push @command, &get_env ($type . 'C_COMPILE');
+  }
+
+  # Name of source file
+  push @command, $info->{SRC};
+
+  # Execute command
+  my $objdir = &get_env ('FCM_OBJDIR', 1);
+  my $tmpdir = &get_env ('FCM_TMPDIR', 1);
+  chdir $tmpdir;
+
+  my $command = join ' ', @command;
+  if ($verbose > 1) {
+    print 'cd ', $tmpdir, "\n";
+    print &timestamp_command ($command, 'Start');
+
+  } elsif ($verbose) {
+    print $command, "\n";
+  }
+
+  my $rc = system $command;
+
+  print &timestamp_command ($command, 'End  ') if $verbose > 1;
+
+  # Move temporary output to correct location on success
+  # Otherwise, remove temporary output
+  if ($rc) { # error
+    unlink $info->{TARGET};
+
+  } else {   # success
+    print 'mv ', $info->{TARGET}, ' ', $objdir, "\n" if $verbose > 1;
+    rename $info->{TARGET}, &catfile ($objdir, $info->{TARGET});
+  }
+
+  # Move any Fortran module definition files to the INC directory
+  my @modfiles = <*.mod *.MOD>;
+  for my $file (@modfiles) {
+    rename $file, &catfile ($incpath[0], $file);
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &load (\%info);
+#
+# DESCRIPTION
+#   This method invokes the correct loader with the correct options to link
+#   the main program object into an executable. The argument $info is a hash
+#   reference set up in MAIN. The following environment variables are used:
+# 
+#   LD           - * linker command
+#   LD_OUTPUT    - LD option to specify the name of the output file
+#   LD_LIBSEARCH - LD option to declare a directory in the library search path
+#   LD_LIBLINK   - LD option to declare an object library
+#   LDFLAGS      - LD user options
+#   FCM_VERBOSE  - verbose level
+#   FCM_LIBDIR   - destination directory of object libraries
+#   FCM_OBJDIR   - destination directory of object files
+#   FCM_BINDIR   - destination directory of executable file
+#   FCM_TMPDIR   - temporary destination directory of executable file
+#
+#   * If LD is not set, it will attempt to guess the file type and use the
+#     compiler as the linker.
+# ------------------------------------------------------------------------------
+
+sub load {
+  my $info = shift;
+
+  my $rc = 0;
+
+  # Verbose mode
+  my $verbose = &get_env ('FCM_VERBOSE');
+  $verbose    = 1 unless defined ($verbose);
+
+  # Create temporary object library
+  (my $name   = $info->{TARGET}) =~ s/\.\S+$//;
+  my $libname = '__fcm__' . $name;
+  my $lib     = 'lib' . $libname . '.a';
+  my $libfile = catfile (&get_env ('FCM_LIBDIR', 1), $lib);
+  $rc = &archive ({TARGET => $lib});
+
+  unless ($rc) {
+    my @command = ();
+
+    # Linker
+    my $ld = &select_flags ($info, 'LD');
+    if (not $ld) {
+      # Guess file type for backward compatibility
+      my $type = $info->{TYPE} ? $info->{TYPE} : &guess_file_type ($info->{SRC});
+      $ld = &get_env ($type . 'C', 1);
+    }
+    push @command, $ld;
+
+    # Linker output target (typical -o option)
+    push @command, &get_env ('LD_OUTPUT', 1), $info->{TARGET};
+
+    # Name of main object file
+    my $mainobj = (basename ($info->{SRC}) eq $info->{SRC})
+                  ? catfile (&get_env ('FCM_OBJDIR'), $info->{SRC})
+                  : $info->{SRC};
+    push @command, $mainobj;
+
+    # Link with Fortran BLOCKDATA objects if necessary
+    if (@{ $info->{BLOCKDATA} }) {
+      my @blockdata = @{ $info->{BLOCKDATA} };
+      my @objpath   = split /:/, &get_env ('FCM_OBJPATH');
+
+      # Search each BLOCKDATA object file from the object search path
+      for my $file (@blockdata) {
+        for my $dir (@objpath) {
+          my $full = catfile ($dir, $file);
+
+          if (-r $full) {
+            $file = $full;
+            last;
+          }
+        }
+
+        push @command, $file;
+      }
+    }
+
+    # Library search path
+    my $libopt  = &get_env ('LD_LIBSEARCH', 1);
+    my @libpath = split /:/, &get_env ('FCM_LIBPATH');
+    push @command, (map {$libopt . $_} @libpath);
+
+    # Link with temporary object library if it exists
+    push @command, &get_env ('LD_LIBLINK', 1) . $libname if -f $libfile;
+
+    # Other linker flags
+    my $flags = &select_flags ($info, 'LDFLAGS');
+    push @command, $flags;
+
+    # Execute command
+    my $tmpdir = &get_env ('FCM_TMPDIR', 1);
+    my $bindir = &get_env ('FCM_BINDIR', 1);
+    chdir $tmpdir;
+
+    my $command = join ' ', @command;
+    if ($verbose > 1) {
+      print 'cd ', $tmpdir, "\n";
+      print &timestamp_command ($command, 'Start');
+
+    } elsif ($verbose) {
+      print $command, "\n";
+    }
+
+    $rc = system $command;
+
+    print &timestamp_command ($command, 'End  ') if $verbose > 1;
+
+    # Move temporary output to correct location on success
+    # Otherwise, remove temporary output
+    if ($rc) { # error
+      unlink $info->{TARGET};
+
+    } else {   # success
+      print 'mv ', $info->{TARGET}, ' ', $bindir, "\n" if $verbose > 1;
+      rename $info->{TARGET}, &catfile ($bindir, $info->{TARGET});
+    }
+  }
+
+  # Remove the temporary object library
+  unlink $libfile if -f $libfile;
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &archive (\%info);
+#
+# DESCRIPTION
+#   This method invokes the library archiver to create an object library. The
+#   argument $info is a hash reference set up in MAIN. The following
+#   environment variables are used:
+# 
+#   AR           - archiver command
+#   ARFLAGS      - AR options to update/create an object library
+#   FCM_VERBOSE  - verbose level
+#   FCM_LIBDIR   - destination directory of object libraries
+#   FCM_OBJPATH  - search path of object files
+#   FCM_OBJDIR   - destination directory of object files
+#   FCM_TMPDIR   - temporary destination directory of executable file
+# ------------------------------------------------------------------------------
+
+sub archive {
+  my $info = shift;
+
+  my $rc = 0;
+
+  # Verbose mode
+  my $verbose = &get_env ('FCM_VERBOSE');
+  $verbose    = 1 unless defined ($verbose);
+
+  # Set up the archive command
+  my $lib     = &basename ($info->{TARGET});
+  my $tmplib  = &catfile (&get_env ('FCM_TMPDIR', 1), $lib);
+  my @ar_cmd  = ();
+  push @ar_cmd, (&get_env ('AR', 1), &get_env ('ARFLAGS', 1));
+  push @ar_cmd, $tmplib;
+
+  # Get object directories and their files
+  my %objdir;
+  if (exists $info->{OBJECTS}) {
+    # List of objects set in the argument, sort into directory/file list
+    for my $name (@{ $info->{OBJECTS} }) {
+      my $dir = (&dirname ($name) eq '.')
+                ? &get_env ('FCM_OBJDIR', 1) : &dirname ($name);
+      $objdir{$dir}{&basename ($name)} = 1;
+    }
+
+  } else {
+    # Objects not listed in argument, search object path for all files
+    my @objpath  = split /:/, &get_env ('FCM_OBJPATH', 1);
+    my %objbase  = ();
+
+    # Get registered objects into a hash (keys = objects, values = 1)
+    my %objects = map {($_, 1)} split (/\s+/, &get_env ('OBJECTS'));
+
+    # Seach object path for all files
+    for my $dir (@objpath) {
+      next unless -d $dir;
+
+      chdir $dir;
+
+      # Use all files from each directory in the object search path
+      for ((glob ('*'))) {
+        next unless exists $objects{$_}; # consider registered objects only
+        $objdir{$dir}{$_} = 1 unless exists $objbase{$_};
+        $objbase{$_} = 1;
+      }
+    }
+  }
+
+  for my $dir (sort keys %objdir) {
+    next unless -d $dir;
+
+    # Go to each object directory and executes the library archive command 
+    chdir $dir;
+    my $command = join ' ', (@ar_cmd, sort keys %{ $objdir{$dir} });
+
+    if ($verbose > 1) {
+      print 'cd ', $dir, "\n";
+      print &timestamp_command ($command, 'Start');
+
+    } else {
+      print $command, "\n" if exists $info->{OBJECTS};
+    }
+
+    $rc = system $command;
+
+    print &timestamp_command ($command, 'End  ')
+      if $verbose > 1;
+    last if $rc;
+  }
+
+  # Move temporary output to correct location on success
+  # Otherwise, remove temporary output
+  if ($rc) { # error
+    unlink $tmplib;
+
+  } else {   # success
+    my $libdir = &get_env ('FCM_LIBDIR', 1);
+
+    print 'mv ', $tmplib, ' ', $libdir, "\n" if $verbose > 1;
+    rename $tmplib, &catfile ($libdir, $lib);
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $type = &guess_file_type ($filename);
+#
+# DESCRIPTION
+#   This function attempts to guess the file type by looking at the extension
+#   of the $filename. Only C and Fortran at the moment.
+# ------------------------------------------------------------------------------
+
+sub guess_file_type {
+  return (($_[0] =~ /\.c(\w+)?$/i) ? 'C' : 'F');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flags = &select_flags (\%info, $set);
+#
+# DESCRIPTION
+#   This function selects the correct compiler/linker flags for the current
+#   sub-package from the environment variable prefix $set. The argument $info
+#   is a hash reference set up in MAIN.
+# ------------------------------------------------------------------------------
+
+sub select_flags {
+  my ($info, $set) = @_;
+
+  my $srcbase = &basename ($info->{SRC});
+  my @names    = ($set);
+  push @names, split (/__/, $info->{SRCPACKAGE} . '__' . $srcbase);
+
+  my $string = '';
+  for my $i (reverse (0 .. $#names)) {
+    my $var  = &get_env (join ('__', (@names[0 .. $i])));
+
+    $var = &get_env (join ('__', (@names[0 .. $i])))
+      if (not defined ($var)) and $i and $names[-1] =~ s/\.[^\.]+$//;
+
+    next unless defined $var;
+    $string = $var;
+    last;
+  }
+
+  return $string;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $variable = &get_env ($name);
+#   $variable = &get_env ($name, $compulsory);
+#
+# DESCRIPTION
+#   This internal method gets a variable from $ENV{$name}. If $compulsory is
+#   set to true, it throws an error if the variable is a not set or is an empty
+#   string. Otherwise, it returns C<undef> if the variable is not set.
+# ------------------------------------------------------------------------------
+
+sub get_env {
+  (my $name, my $compulsory) = @_;
+  my $string;
+
+  if ($name =~ /^\w+$/) {
+    # $name contains only word characters, variable is exported normally
+    die 'The environment variable "', $name, '" must be set, abort'
+      if $compulsory and not exists $ENV{$name};
+
+    $string = exists $ENV{$name} ? $ENV{$name} : undef;
+
+  } else {
+    # $name contains unusual characters
+    die 'The environment variable "', $name, '" must be set, abort'
+      if $compulsory and not exists $unusual_tool_name{$name};
+
+    $string = exists $unusual_tool_name{$name}
+              ? $unusual_tool_name{$name} : undef;
+  }
+
+  return $string;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = &catfile (@paths);
+#
+# DESCRIPTION
+#   This is a local implementation of what is in the File::Spec module.
+# ------------------------------------------------------------------------------
+
+sub catfile {
+  my @names = split (m!/!, join ('/', @_));
+  my $path  = shift @names;
+
+  for my $name (@names) {
+    $path .= '/' . $name if $name;
+  }
+
+  return $path;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $basename = &basename ($path);
+#
+# DESCRIPTION
+#   This is a local implementation of what is in the File::Basename module.
+# ------------------------------------------------------------------------------
+
+sub basename {
+  my $name = $_[0];
+
+  $name =~ s{/*$}{}; # remove trailing slashes
+
+  if ($name =~ m#.*/([^/]+)$#) {
+    return $1;
+
+  } else {
+    return $name;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dirname = &dirname ($path);
+#
+# DESCRIPTION
+#   This is a local implementation of what is in the File::Basename module.
+# ------------------------------------------------------------------------------
+
+sub dirname {
+  my $name = $_[0];
+
+  if ($name =~ m#^/+$#) {
+    return '/'; # dirname of root is root
+
+  } else {
+    $name =~ s{/*$}{}; # remove trailing slashes
+
+    if ($name =~ m#^(.*)/[^/]+$#) {
+      my $dir = $1;
+      $dir =~ s{/*$}{}; # remove trailing slashes
+      return $dir;
+
+    } else {
+      return '.';
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+__END__
+
+=head1 NAME
+
+fcm_internal
+
+=head1 SYNOPSIS
+
+    fcm_internal SUBCOMMAND ARGS
+
+=head1 DESCRIPTION
+
+The fcm_internal command is a frontend for some of the internal commands of
+the FCM build system. The subcommand can be "compile", "load" or "archive"
+for invoking the compiler, loader and library archiver respectively. If
+"compile" or "load" is specified, it can be suffixed with ":TYPE" to
+specify the nature of the source file. If TYPE is not specified, it is set
+to C if the file extension begins with ".c". For all other file types, it
+is set to F (for Fortran source). For compile and load, the other arguments
+are 1) the name of the container package of the source file, 2) the path to
+the source file and 3) the target name after compiling or loading the
+source file. For compile, the 4th argument is a flag to indicate whether
+pre-processing is required for compiling the source file.  For load, the
+4th and the rest of the arguments is a list of object files that cannot be
+archived into the temporary load library and must be linked into the target
+through the linker command. (E.g. Fortran BLOCKDATA program units must be
+linked this way.) If archive is specified, the first argument should be the
+name of the library archive target and the rest should be the object files
+to be included in the archive. This command is invoked via the build system
+and should never be called directly by the user.
+
+=head1 COPYRIGHT
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/bin/fcm_setup_konqueror
===================================================================
--- vendors/fcm/1-5/bin/fcm_setup_konqueror	(revision 1981)
+++ vendors/fcm/1-5/bin/fcm_setup_konqueror	(revision 1981)
@@ -0,0 +1,47 @@
+#!/bin/sh
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm_setup_konqueror
+#
+# SYNOPSIS
+#   fcm_setup_konqueror
+#
+# DESCRIPTION
+#   Set up Konqueror to use "fcm gui".
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Check number of arguments
+script=`basename $0`
+usage="$script: no argument required"
+if (( $# != 0 )); then
+  echo "$usage, abort..." >&2
+  exit 1
+fi
+
+filename=fcm_gui.desktop
+
+file=`dirname $0`
+if [[ `basename $file` = bin ]]; then
+  file=`dirname $file`
+fi
+file=$file/etc/$filename
+
+if [[ ! -f $file ]]; then
+  echo "$script: $file not found, abort..." >&2
+  exit 1
+fi
+
+dir=$HOME/.kde/share/applnk/.hidden
+mkdir -p $dir
+cd $dir
+rm -f $filename             # Always remove.
+ln -s $file .
+
+echo "$script: finished"
+
+#EOF
Index: vendors/fcm/1-5/bin/fcm_update_version_dir.pl
===================================================================
--- vendors/fcm/1-5/bin/fcm_update_version_dir.pl	(revision 1981)
+++ vendors/fcm/1-5/bin/fcm_update_version_dir.pl	(revision 1981)
@@ -0,0 +1,289 @@
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Cwd qw{cwd};
+use Getopt::Long qw{GetOptions};
+use Fcm::Config;
+use Fcm::Keyword;
+use Fcm::Util qw{get_url_of_wc get_wct is_wc run_command tidy_url};
+use File::Basename qw{basename dirname};
+use File::Path qw{mkpath};
+use File::Spec;
+use Pod::Usage qw{pod2usage};
+
+# Usage
+# ------------------------------------------------------------------------------
+my $this  = basename($0);
+
+# Options
+# ------------------------------------------------------------------------------
+my ($dest, $full, $help, $url);
+my $rc = GetOptions(
+  'dest|d=s' => \$dest,
+  'full|f'   => \$full,
+  'help'     => \$help,
+  'url|u=s'  => \$url,
+);
+if (!$rc) {
+  pod2usage({'-verbose' => 1});
+}
+if ($help) {
+  pod2usage({'-exitval' => 0, '-verbose' => 1});
+}
+if (!$url) {
+  pod2usage(
+    {'-message' => 'The --url=URL option is compulsory', '-verbose' => 1},
+  );
+}
+$dest ||= cwd();
+
+# Arguments
+# ------------------------------------------------------------------------------
+if (@ARGV) {
+  die 'Cannot read: ', $ARGV[0], ', abort' unless -f $ARGV[0] and -r $ARGV[0];
+}
+
+# Get configuration settings
+# ------------------------------------------------------------------------------
+my $config = Fcm::Config->new ();
+$config->get_config ();
+
+# Expand URL keyword
+$url = Fcm::Util::tidy_url(Fcm::Keyword::expand($url));
+
+# ------------------------------------------------------------------------------
+
+MAIN: {
+  my $date = localtime;
+  print $this, ': started on ', $date, "\n";
+
+  my %dirs;
+
+  # Read input (file) for a list directories and update conditions
+  while (<>) {
+    chomp;
+
+    # Ignore empty and comment lines
+    next if /^\s*(?:#|$)/;
+
+    # Each line must contain a relative path, and optionally a list of
+    # space delimited conditions
+    my @words = split /\s+/;
+    my $dir   = shift @words;
+
+    # Check that the conditions are valid
+    my @conditions;
+    for my $word (@words) {
+      if ($word =~ /^([<>]=?|[!=]=)(.+)$/i) {
+        # Condition must be a conditional operator followed by a revision
+        my ($operator, $rev) = ($1, $2);
+        $rev = (Fcm::Keyword::expand($url, $rev))[1];
+        push @conditions, $operator . $rev;
+
+      } else {
+        print STDERR 'Warning: ignore unknown syntax for update condition: ',
+                     $word, "\n";
+      }
+    }
+
+    # Add directory and its conditions to a hash
+    if ($dir =~ s#/\*$##) { # Directory finishes with wildcard
+
+      # Run "svn ls" in recursive mode
+      my $dirurl  = join ('/', ($url, $dir));
+      my @files   = &run_command ([qw/svn ls -R/, $dirurl], METHOD => 'qx');
+
+      # Find directories containing regular files
+      while (my $file = shift @files) {
+	# Skip directories
+	next if $file =~ m#/$#;
+
+	# Get "dirname" of regular file and add to hash
+	my $subdir = join ('/', ($dir, dirname ($file)));
+	$dirs{$subdir} = \@conditions;
+      }
+
+    } else {
+      $dirs{$dir} = \@conditions;
+    }
+
+  }
+
+  # Update each directory, if required
+  for my $dir (sort keys %dirs) {
+    # Use "svn log" to determine the revisions that need to be updated
+    my %allversions;
+    {
+      my $command = 'svn log -q ' . join ('/', ($url, $dir));
+      my @log     = &run_command (
+        [qw/svn log -q/, join ('/', ($url, $dir))], METHOD => 'qx',
+      );
+      @log        = grep /^r\d+/, @log;
+
+      # Assign a sequential "version" number to each sub-directory
+      my $version = scalar @log;
+      for (@log) {
+        m/^r(\d+)/;
+        $allversions{$1} = 'v' . $version--;
+      }
+    }
+    my %versions = %allversions;
+
+    # Extract only revisions matching the conditions
+    if (@{ $dirs{$dir} }) {
+      my @conditions = @{ $dirs{$dir} };
+
+      for my $condition (@conditions) {
+        for my $rev (keys %versions) {
+          delete $versions{$rev} unless eval ($rev . $condition);
+        }
+      }
+    }
+
+    # Destination directory
+    my $dirpath = File::Spec->catfile ($dest, $dir);
+
+    if (-d $dirpath) {
+      if ($full or not keys %versions) {
+        # Remove destination directory top, in full mode
+        # or if there are no matching revisions
+        &run_command ([qw/rm -rf/, $dirpath], PRINT => 1);
+
+      } else {
+        # Delete excluded revisions if they exist, in incremental mode
+        if (opendir DIR, $dirpath) {
+          while (my $rev = readdir 'DIR') {
+            next unless $rev =~ /^\d+$/;
+
+            if (not grep {$_ eq $rev} keys %versions) {
+              my @command = (qw/rm -rf/, File::Spec->catfile ($dirpath, $rev));
+              &run_command (\@command, PRINT => 1);
+
+              # Remove "version" symlink
+              my $verlink = File::Spec->catfile ($dirpath, $allversions{$rev});
+              unlink $verlink if -l $verlink;
+            }
+          }
+          closedir DIR;
+        }
+      }
+    }
+
+    # Create container directory of destination if it does not already exist
+    if (keys %versions and not -d $dirpath) {
+      print '-> mkdir -p ', $dirpath, "\n";
+      my $rc = mkpath $dirpath;
+      die 'mkdir -p ', $dirpath, ' failed' unless $rc;
+    }
+
+    # Update each version directory that needs updating
+    for my $rev (keys %versions) {
+      my $revpath = File::Spec->catfile ($dest, $dir, $rev);
+
+      # Create version directory if it does not exist
+      if (not -e $revpath) {
+        # Use "svn export" to create the version directory
+        my @command = (
+          qw/svn export -q -r/,
+          $rev,
+          join ('/', ($url, $dir)),
+          $revpath,
+        );
+
+        &run_command (\@command, PRINT => 1);
+      }
+
+      # Create "version" symlink if necessary
+      my $verlink = File::Spec->catfile ($dest, $dir, $versions{$rev});
+      symlink $rev, $verlink unless -l $verlink;
+    }
+
+    # Symbolic link to the "latest" version directory
+    my $headlink = File::Spec->catfile ($dest, $dir, 'latest');
+    my $headrev  = 0;
+    for my $rev (keys %versions) {
+      $headrev = $rev if $rev > $headrev;
+    }
+
+    if (-l $headlink) {
+      # Remove old symbolic link if there is no revision to update or if it
+      # does not point to the correct version directory
+      my $org = readlink $headlink;
+      unlink $headlink if (! $headrev or $org ne $headrev);
+    }
+
+    # (Re-)create the "latest" symbolic link, if necessary
+    symlink $headrev, $headlink if ($headrev and not -l $headlink);
+  }
+
+  $date = localtime;
+  print $this, ': finished normally on ', $date, "\n";
+}
+
+__END__
+
+=head1 NAME
+
+fcm_update_version_dir.pl
+
+=head1 SYNOPSIS
+
+    fcm_update_version_dir.pl [OPTIONS] [CFGFILE]
+
+=head1 DESCRIPTION
+
+Update the version directories for a list of relative paths in the source
+repository URL.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --dest=DEST, -d DEST
+
+Specify a destination for the extraction. If not specified, the command extracts
+to the current working directory.
+
+=item --help, -h
+
+Print help and exit.
+
+=item --full, -f
+
+Specify the full mode. If not specified, the command runs in incremental mode.
+
+=item --url=URL, -u URL
+
+Specify the source repository URL. No default.
+
+=back
+
+=head1 ARGUMENTS
+
+A configuration file may be given to this command, or it will attempt to read
+from the standard input. Each line in the configuration must contain a relative
+path that resides under the given source repository URL. (Empty lines and lines
+beginning with a "#" are ignored.) Optionally, each relative path may be
+followed by a list of space separated "conditions".  Each condition is a
+conditional operator (>, >=, <, <=, == or !=) followed by a revision number or
+the keyword HEAD. The command uses the revision log to determine the revisions
+at which the relative path has been updated in the source repository URL. If
+these revisions also satisfy the "conditions" set by the user, they will be
+considered in the extraction.  In full mode, everything is re-extracted. In
+incremental mode, the version directories are only updated if they do not
+already exist.
+
+=head1 COPYRIGHT
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/doc/collaboration/fcm.js
===================================================================
--- vendors/fcm/1-5/doc/collaboration/fcm.js	(revision 1981)
+++ vendors/fcm/1-5/doc/collaboration/fcm.js	(revision 1981)
@@ -0,0 +1,212 @@
+//------------------------------------------------------------------------------
+// JS for displaying information of FCM documents.
+//------------------------------------------------------------------------------
+FCM = {
+    // List of heading tags for the contents
+    CONTENT_INDEX_OF: {'H2': 0, 'H3': 1, 'H4': 2, 'H5': 3, 'H6': 4},
+    // ID in a document, for the content
+    ID_OF_CONTENT: 'fcm-content',
+    // ID in a document, for the maintenance information
+    ID_OF_MAINTENANCE: 'fcm-js-maintenance',
+    // ID in a document, for the link to its PDF version
+    ID_OF_PDF: 'fcm-js-pdf',
+    // ID in a document, for the breadcrumb trail
+    ID_OF_TRAIL: 'fcm-js-trail',
+    // URL to the log browser of the FCM trunk
+    URL_OF_LOG_BROWSER: 'http://fcm1/projects/FCM/intertrac/log:/FCM/trunk/',
+    // URL to the FCM team page
+    URL_OF_TEAM: null,
+
+    // Making DOM manipulation less painful
+    DOM: {
+        // The current document
+        DOC: document,
+
+        // Adds an array of "nodes" (Node or String) to "node"
+        append: function(node, items) {
+            if (node == null || items == null) {
+                return;
+            }
+            for (var i = 0; i < items.length; i++) {
+                if (items[i] == null) {
+                    continue;
+                }
+                if (items[i].nodeType == null) {
+                    node.appendChild(this.DOC.createTextNode(items[i]));
+                }
+                else {
+                    node.appendChild(items[i]);
+                }
+            }
+        },
+
+        // Returns the base name of a URL, relative to current document
+        basename: function(url) {
+            if (url == null) {
+                url = this.DOC.URL;
+            }
+            return url.substr(this.DOC.URL.lastIndexOf('/') + 1);
+        },
+
+        // Creates and returns an element
+        create: function(tag, items, attribs) {
+            var element = this.DOC.createElement(tag);
+            this.append(element, items);
+            if (attribs == null) {
+                return element;
+            }
+            for (var i = 0; i < attribs.length; i++) {
+                element[attribs[i][0]] = attribs[i][1];
+            }
+            return element;
+        },
+
+        // Shorthand for root[attrib]
+        get_attrib: function(root, attrib) {
+            return root[attrib];
+        },
+
+        // Shorthand for this.DOC.getElementById(id)
+        get_by_id: function(id) {
+            return this.DOC.getElementById(id);
+        },
+
+        // Shorthand for root.getElementsByTagName(name)[i]
+        get_by_name: function(root, name, i) {
+            var elements = root.getElementsByTagName(name);
+            if (i == null) {
+                return elements;
+            }
+            if (elements == null || i >= elements.length) {
+                return null;
+            }
+            return elements[i];
+        },
+
+        map: function(action_on, items) {
+            var ret = [];
+            for (var i = 0; i < items.length; i++) {
+                ret = ret.concat(action_on(items[i], i));
+            }
+            return ret;
+        }
+    },
+
+    // Creates and returns the content
+    create_content: function() {
+        var root = $D.get_attrib($D.DOC, 'body');
+        var stack = [{content: null, item: null}];
+        for (var node = root.firstChild; node != null; node = node.nextSibling) {
+            if (
+                node.nodeType != 1
+                || node.id == null
+                || node.id == ''
+                || this.CONTENT_INDEX_OF[node.tagName] == null
+                || this.CONTENT_INDEX_OF[node.tagName] + 1 > stack.length
+            ) {
+                continue;
+            }
+            while (this.CONTENT_INDEX_OF[node.tagName] + 1 < stack.length) {
+                stack.shift();
+            }
+            if (stack[0].content == null) {
+                stack[0].content = $D.create('ul');
+                if (stack[0].item != null) {
+                    $D.append(stack[0].item, [stack[0].content]);
+                }
+            }
+            var item = $D.create(
+                'li',
+                [$D.create('a', [node.innerHTML], [['href', '#' + node.id]])]
+            );
+            $D.append(stack[0].content, [item]);
+            stack.unshift({content: null, item: item});
+        }
+        return [stack[stack.length - 1].content];
+    },
+
+    // Creates and returns the maintenance information for "path"
+    create_info_for_maintenance: function(path) {
+        if (this.URL_OF_TEAM == null) {
+            return [];
+        }
+        return [
+            'Maintained by: ',
+            $D.create('a', ['FCM team'], [['href', FCM.URL_OF_TEAM]]),
+            '. History: ',
+            $D.create(
+                'a',
+                ['log'],
+                [['href', this.URL_OF_LOG_BROWSER + path + $D.basename()]]
+            ),
+            '.'
+        ];
+    },
+
+    // Creates and returns the PDF link for "name"
+    create_info_for_pdf: function(name) {
+        return [
+            'For printing, please use the ',
+            $D.create('a', ['PDF'], [['href', 'fcm-' + name + '.pdf']]),
+            ' version of the document.'
+        ];
+    },
+
+    // Creates a breadcrumb trail from a list of [[href, text], ...]
+    create_trail: function(items) {
+        if (items == null) {
+            return [];
+        }
+        return [].concat(
+            $D.map(
+                function(item, i) {
+                    return [].concat(
+                        (i != 0 ? ' > ' : []),
+                        $D.create('a', [item[1]], [['href', item[0]]])
+                    );
+                },
+                items
+            ),
+            ' > ',
+            $D.get_by_name($D.DOC, 'title', 0).innerHTML
+        );
+    },
+
+    // A simple facade for doing the onload tasks
+    load: function(maintenance_path, pdf_name, trail_list) {
+        var tasks = [
+            {
+                id: this.ID_OF_CONTENT,
+                task: function() {
+                    return FCM.create_content();
+                }
+            },
+            {
+                id: this.ID_OF_MAINTENANCE,
+                task: function() {
+                    return FCM.create_info_for_maintenance(maintenance_path);
+                }
+            },
+            {
+                id: this.ID_OF_PDF,
+                task: function() {
+                    return FCM.create_info_for_pdf(pdf_name);
+                }
+            },
+            {
+                id: this.ID_OF_TRAIL,
+                task: function() {
+                    return FCM.create_trail(trail_list);
+                }
+            }
+        ];
+        for (var i = 0; i < tasks.length; i++) {
+            var node = $D.get_by_id(tasks[i].id);
+            if (node == null) {
+                continue;
+            }
+            $D.append(node, tasks[i].task());
+        }
+    }
+};
+$D = FCM.DOM;
Index: vendors/fcm/1-5/doc/collaboration/index.html
===================================================================
--- vendors/fcm/1-5/doc/collaboration/index.html	(revision 1981)
+++ vendors/fcm/1-5/doc/collaboration/index.html	(revision 1981)
@@ -0,0 +1,460 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>External Distribution &amp; Collaboration for FCM Projects</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content=
+  "External Distribution &amp; Collaboration" />
+  <meta name="keywords" content="FCM, distribution, collaboration" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload="javascript: FCM.load('doc/collaboration/', 'collaboration');">
+  <div id="ukmo-logo">
+    <img src="logo.png" alt="UK Met Office" />
+  </div>
+
+  <h1>External Distribution &amp; Collaboration for FCM Projects</h1>
+
+  <address>
+    Latest content update: 02 Nov 2007. <span id="fcm-js-maintenance"></span>
+  </address>
+
+  <address>
+    Questions regarding this document or permissions to quote from it should be
+    directed to:
+  </address>
+
+  <address>
+    <a href="mailto:iprmanager@metoffice.gov.uk">IPR Manager</a><br />
+    Met Office<br />
+    FitzRoy Road<br />
+    Exeter, Devon<br />
+    EX1 3PB<br />
+    United Kingdom
+  </address>
+
+  <address>
+    &copy; Crown Copyright 2006-7
+  </address>
+
+  <address id="fcm-js-pdf"></address>
+
+  <hr />
+
+  <div id="fcm-content"></div>
+
+  <h2 id="introduction">Introduction</h2>
+
+  <p>This document describes how projects configured under FCM can be
+  distributed externally. Particular attention is given to collaborative
+  distributions - where the external user regularly returns code for
+  consolidation into the central repositories which hold the master copies of
+  the code.</p>
+
+  <p><dfn>Note:</dfn> This document assumes that the repositories are
+  inaccessible to the external user, due to issues of security and
+  practicality.</p>
+
+  <h2 id="distribution">Creating a Distribution</h2>
+
+  <p>A system configured under FCM can be distributed by packaging a known
+  revision (usually corresponding to a stable release) into an archive (e.g. a
+  tarball) of directories and files. Various issues need to be considered:</p>
+
+  <ul>
+    <li>A distribution may contain a variety of different files including
+    source code, scripts, benchmark and validation tests, documentation,
+    etc.</li>
+
+    <li>A system may consist of several different <em>projects</em> which
+    should be put into separate directories in the distribution. Please refer
+    to the section <a href=
+    "../user_guide/system_admin.html#svn_design">Repository design</a> in the
+    FCM user guide for an explanation of what is meant by a project in this
+    context.</li>
+
+    <li>Some files in a project may not be included in the distribution. This
+    may be because they are of no interest to external users or because of
+    license restrictions. Such files will need to be filtered out when creating
+    the distribution.</li>
+
+    <li>The distribution may also contain some files which are not maintained
+    under FCM version control (test results for instance).</li>
+
+    <li>Some systems share code with other systems.
+
+      <ul>
+        <li>If a distribution is intended to be used standalone then the
+        necessary files from these other systems will need to be included. e.g.
+        The VAR system requires code from the OPS and GEN systems.</li>
+
+        <li>If the distribution is part of a wider collaboration then it is
+        likely that the files from the other systems will be distributed
+        separately. It is best if stable releases of the various systems can be
+        synchronised so that, for example, a VAR stable release uses code from
+        an OPS stable release which both use code from the same GEN
+        release.</li>
+      </ul>
+    </li>
+
+    <li>Release notes should be prepared to accompany a distribution which
+    explain, among other things, how the distribution is structured.</li>
+
+    <li>The distribution should contain a file which identifies the repository
+    revision(s) contained in the distribution.</li>
+
+    <li>System managers will probably wish to maintain a script which automates
+    the generation of these distributions.</li>
+  </ul>
+
+  <h2 id="feedback">Feeding Back Changes</h2>
+
+  <p>Although we would encourage all collaborators to make use of the FCM
+  system for version control, we recognise that they may already have their own
+  preferred systems in place. There is no particular problem with this. The
+  main requirement is that any proposed changes are provided as a modification
+  relative to the provided distribution. The changeset could be provided in the
+  form of a modified project tree or as a patchfile (refer to the later section
+  <a href="#patchfiles">Exchanging Changesets using Patchfiles</a> for further
+  discussion). If the change involves any renaming or removal of files or
+  directories then special instructions should be provided plus a script to
+  perform the changes.</p>
+
+  <p>At the central repository, the changeset should be applied to a branch
+  created from the repository revision which formed the basis of the changeset
+  (possibly making use of the Subversion utility <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.advanced.vendorbr.html#svn.advanced.vendorbr.svn_load_dirs">
+  svn_load_dirs.pl</a>). Note that extra care is needed with changesets
+  provided as modified project trees if there are any files in the project
+  which are excluded from the distribution. Once imported, the changeset should
+  then undergo any necessary testing or review before being merged into the
+  trunk.</p>
+
+  <h2 id="usingfcm">Collaborating Using FCM for Version Control</h2>
+
+  <p>There are a number of advantages if the FCM system is used for version
+  control by the collaborator. In particular it means that:</p>
+
+  <ul>
+    <li>Collaborators will be able to see all of the individual changesets
+    which went in to a new release rather than only being able to view each new
+    release as one big change.</li>
+
+    <li>The process of sending a proposed change to the central repository can
+    be standardised through the use of an <em>FCM patch</em> (explained
+    later).</li>
+
+    <li>The FCM Extract system can be fully utilised.</li>
+
+    <li>Common tools will help to ease communication. We will all use technical
+    terms to mean the same thing.</li>
+  </ul>
+
+  <p>This section explains the recommended way of using FCM in a
+  collaboration.</p>
+
+  <h3 id="initsvn">Initialising the Subversion Repositories</h3>
+
+  <p>The collaborator needs to set up a repository and import each of the
+  projects. Please see the section <a href=
+  "../user_guide/system_admin.html#svn_create">Creating a repository</a> in the
+  FCM user guide for advice. Collaborators may wish to use separate
+  repositories and Trac systems for each project or they may prefer to use a
+  single repository for all projects and use a single Trac system. Either
+  option should be fine so long as the same set of projects is retained.</p>
+
+  <p>After completing the initial import, the collaborator should have the
+  required set of projects available in Subversion where the initial version of
+  the trunk of each project corresponds with the initial stable release
+  provided in the distribution.</p>
+
+  <h3 id="prepchanges">Preparing Changes at the Collaborator's Site</h3>
+
+  <p>The recommended way of preparing changes is illustrated in Figure 1a:</p>
+
+  <p class="image"><img src="working-as-collaborator.png" alt=
+  "Figure 1a: working at the collaborator's site" /><br />
+  Figure 1a: working at the collaborator's site</p>
+
+  <p>The collaborator will create a shared package branch from the latest
+  stable release on the trunk. This branch will contain all the changes that
+  will eventually be fed back to the central repository. Developers will also
+  create their own development branches. These may be branched from the latest
+  stable release on the trunk. Alternatively, if the change needs to build on
+  other changes then a branch can be created from the shared package branch.
+  When the changes are ready (i.e. tested, documented, reviewed, etc) then they
+  are merged into the shared package branch. The trunk is not used for the
+  shared changes as it is reserved for changes received from the central
+  repository.</p>
+
+  <p>Should it be required, a second shared package branch can be created from
+  the same point to contain any local modifications that will not be fed back
+  to the central repository. A configuration branch can then be used to combine
+  the local changes with those destined to be fed back. This is illustrated in
+  Figure 1b:</p>
+
+  <p class="image"><img src="managing-local-changes.png" alt=
+  "Figure 1b: managing local changes" /><br />
+  Figure 1b: managing local changes</p>
+
+  <h3 id="feedbackfcm">Feeding Back Changes Using FCM</h3>
+
+  <p>Eventually, a series of changesets will exist on the first package branch.
+  These changes will be fed back to the central repository via an <em>FCM
+  patch</em>. This contains a series of differences associated with changesets
+  in a given branch of development, created by the <code>fcm mkpatch</code>
+  command. For further information about the command, please refer to its
+  <a href="../user_guide/command_ref.html#fcm_svn_mkpatch">command
+  reference</a> in the FCM user guide.</p>
+
+  <p>At the central repository, the changeset will be applied to a branch
+  created from the repository revision which formed the basis of the changeset.
+  This is illustrated in Figure 2:</p>
+
+  <p class="image"><img src="feeding-back-patch.png" alt=
+  "Figure 2: feeding back changes" /><br />
+  Figure 2: feeding back changes</p>
+
+  <p>Patches will usually be exchanged in the form of a tarball. To apply the
+  patch it must first be extracted to a directory. In this directory there
+  should be a shell script called <code>fcm-import-patch</code>. A TARGET needs
+  to be specified when invoking the script. The TARGET must either be a URL or
+  a working copy of a valid project tree that can accept the import of the
+  patches. It is essential that this target matches the version of the project
+  from which the patch was created (usually this means a particular stable
+  release). The script contains a series of <code>cp</code> and
+  <code>svn</code> commands to import the changesets one by one. Note that the
+  changesets are committed automatically with no user interaction. It is worth
+  ensuring that an up to date backup of the repository is available in case of
+  problems.</p>
+
+  <h3 id="changescentral">Incorporating Changes into the Trunk of the Central
+  Repository</h3>
+
+  <p>Once the changes have undergone any necessary testing or review they can
+  be merged into the trunk. There are three ways of approaching this:</p>
+
+  <ol>
+    <li>As one changeset: all changes in the branch will be merged into the
+    trunk as a single changeset. This approach is the easiest and has the
+    advantage that any conflicts only need to be resolved once. However, the
+    drawback of this approach is that the logical changesets as fed back by the
+    collaborator will be combined into a large single changeset on the trunk,
+    which may not be the most desirable (although the logical changesets will
+    still be available to examine on the import branch). This is illustrated in
+    Figure 3a:
+
+      <p class="image"><img src="merging-patch-one.png" alt=
+      "Figure 3a: merging a patch in a single changeset" /><br />
+      Figure 3a: merging a patch in a single changeset</p>
+    </li>
+
+    <li>As multiple changesets: each changeset in the branch will be merged
+    into the trunk in order. This can be quite complicated and time consuming,
+    especially if you have a large number of changesets and there are a lot of
+    clashes. The advantage is that each logical changeset will retain its
+    logical identity, which may be more desirable in the long run, when you
+    come to inspect the history. This is illustrated in Figure 3b:
+
+      <p class="image"><img src="merging-patch-multi.png" alt=
+      "Figure 3b: merging a patch in multiple changesets" /><br />
+      Figure 3b: merging a patch in multiple changesets</p>
+    </li>
+
+    <li>As a mixture of the above: you may want to combine the above two
+    approaches when it makes sense to do so. For example, there may be a series
+    of small changesets that can be combined logically, or there may be a
+    changeset that fixes a bug introduced in the previous one. The bottom line
+    is that the project/system manager should use his/her own judgement in the
+    matter for what is best for the future of the project.</li>
+  </ol>
+
+  <h3 id="changescollab">Incorporating Updates at the Collaborator's Site</h3>
+
+  <p>Once a new stable release is available it will be supplied in the form of
+  a distribution tarball as described earlier. However, collaborators will also
+  be supplied with an <em>FCM patch</em> (as described earlier) for each
+  project containing all the changes made since the previous stable release.
+  Note that this assumes that stable releases are prepared on the trunk and not
+  in branches.</p>
+
+  <p>Each patch should be applied to the trunk of the collaborator's
+  repository. This means that the collaborator's trunk will always be mirroring
+  that of the central repository. This is illustrated in Figure 4:</p>
+
+  <p class="image"><img src="mirroring-trunk.png" alt=
+  "Figure 4: mirroring the trunk at the collaborator's site" /><br />
+  Figure 4: mirroring the trunk at the collaborator's site</p>
+
+  <p>In order to be certain that the patch has worked correctly, we recommend
+  that a check is performed to ensure that the new stable release on the trunk
+  matches the files provided in the distribution (preferably using a copy of
+  the repository for testing purposes before applying the patch to the live
+  repository).</p>
+
+  <h3 id="updatebranches">Updating Existing Branches</h3>
+
+  <p>Old branches that are still active at the collaborators site should be
+  updated to the latest stable release when it becomes available. Developers
+  should create a new branch from the latest stable release and then merge the
+  changes from the old branch to the new branch. The old branch should be
+  deleted once it is no longer required. This is illustrated in Figure 5a:</p>
+
+  <p class="image"><img src="updating-branch.png" alt=
+  "Figure 5a: updating a branch to the latest stable release" /><br />
+  Figure 5a: updating a branch to the latest stable release</p>
+
+  <p>Note that the merge will be easiest if the old branch was created from the
+  previous stable release. If it was created from the shared package branch
+  then a custom merge will be required to achieve the desired result (a normal
+  FCM merge command would choose the wrong base for comparison). This is
+  illustrated in Figure 5b:</p>
+
+  <p class="image"><img src="updating-shared-branch.png" alt=
+  "Figure 5b: updating a branch of the shared package branch" /><br />
+  Figure 5b: updating a branch of the shared package branch</p>
+
+  <h3 id="other">Other Scenarios</h3>
+
+  <p>The previous sections have only considered how developments on the trunk
+  of a central repository can be shared with a single collaborator. However,
+  the same techniques can be applied to more complex situations.</p>
+
+  <ul>
+    <li>If there are multiple external collaborators each working with their
+    own repository then hopefully it is clear that this does not alter things
+    in any way. Inevitably there will be an increased workload on the
+    maintainers of the central repository. There will also be an increased need
+    for coordination of planned code changes. However, the method of code
+    exchange is unaltered.</li>
+
+    <li>Sometimes there may be the need to collaborate on development of a
+    branch (i.e. to exchange code which is not yet ready to be incorporated
+    onto the trunk). The collaborator would maintain the trunk of their
+    repository as before, importing patches to keep their trunk alligned with
+    the stable releases from the central repository. In addition, they would
+    receive an <em>FCM patch</em> from the central repository representing the
+    changes on the shared branch relative to the stable release. The
+    collaborator should create a branch from the stable release and the patch
+    should then be imported onto this branch. They should then create a branch
+    from this branch on which to prepare their changes. When ready the changes
+    would be returned in the form of an <em>FCM patch</em>, and so on.
+    Hopefully it can be seen that the same process can be applied to this
+    shared branch as we have previously described for trunk developments.</li>
+  </ul>
+
+  <h3 id="alternative">An Alternative Branching Strategy</h3>
+
+  <p>We have described the branching strategy we believe will work best for
+  collaborators. However, this is by no means the only branching strategy that
+  can be used. In particular, some collaborators may prefer to keep the latest
+  copies of the code they are using on the trunk. This effectively means
+  getting rid of the shared package branches for shared and local changes and
+  merging all changes on to the trunk. A separate branch would be used for
+  keeping a pristine copy of the main site and merging changes from new stable
+  builds on to the trunk.</p>
+
+  <p>This approach is certainly possible and has the advantage that developers
+  at the collaborator's site may find it easier to work with. However there are
+  two disadvantages that need to be considered:</p>
+
+  <ol>
+    <li>Merging in changes from a new stable release may be more difficult. If
+    the new stable release includes changes which were fed back by the
+    collaborator then these will already be present on the collaborators trunk.
+    If these changes were modified in any way or if they overlap with other
+    changes then this will result in a conflict which could be tricky to
+    resolve.</li>
+
+    <li>Any changes which need to be fed back by the collaborator need to be
+    made relative to a stable release. However, changes will have been prepared
+    relative to some version of the trunk. This means that a separate branch
+    will need to be taken (from the branch containing the pristine copy of the
+    main site) and a custom merge will be required in order to achieve the
+    desired result.</li>
+  </ol>
+
+  <h3 id="patchfiles">Exchanging Changesets using Patchfiles</h3>
+
+  <p>In some cases, an <em>FCM patch</em> may not be the best way of exchanging
+  changesets. For instance, when distributing code changes which have not yet
+  been finalised, you probably wouldn't want to send a patch containing all the
+  individual commits to the branch on which the change is being developed. What
+  you want is a summary of the changes in a single changeset. In this case you
+  will often be better to use a patchfile (which can be applied using the Unix
+  command <code>patch</code>). A patchfile is simply the output from an
+  <code>fcm diff</code> command. For example:</p>
+  <pre>
+fcm diff --branch fcm:myproj-br/dev/frdm/r2134_my_branch &gt; my_patchfile
+</pre>
+
+  <p>The patchfile must be applied to a working copy of the project which
+  corresponds to the same revision from which the patchfile was generated. The
+  option <code>-p0</code> must be used with the <code>patch</code> command. For
+  example:</p>
+  <pre>
+patch -p0 &lt; my_patchfile
+</pre>
+
+  <p>Patchfiles have the advantage that they are simple to generate and
+  exchange and that they can combine the changes from a number of changsets
+  into one. However, they have a number of limitations such as:</p>
+
+  <ul>
+    <li>Binary files are ignored.</li>
+
+    <li>Deleted directories are ignored.</li>
+
+    <li>Deleted files are left as empty files.</li>
+
+    <li>Copied files appear as new files.</li>
+
+    <li>A moved file is treated as a deleted file and a new file.</li>
+  </ul>
+
+  <p>Fortunately these limitations will not be an issue for the majority of
+  changes and, where they are a problem, there are various options such as
+  providing additional instructions with the patchfile, using an <em>FCM
+  patch</em>, or exchanging a modified project tree.</p>
+
+  <h2 id="further">Further Considerations</h2>
+
+  <p>The previous sections have only considered the version control aspects of
+  a collaboration. This section lists some other aspects of the collaboration
+  which will need to be considered.</p>
+
+  <ul>
+    <li>The FCM build system can be used regardless of what version control
+    system is used. This avoids effort being wasted trying to maintain
+    compatibility with an alternate build system. It also ensures that any code
+    changes prepared by the collaborator are compatible with the coding
+    standards which the FCM build system requires. Even if there are good
+    reasons for the collaborator not to use FCM for version control, it is
+    highly recommended that the FCM build system is used (assuming that is what
+    is used at the central repository).</li>
+
+    <li>Coding standards should be agreed by all collaborators.</li>
+
+    <li>Working practises should be agreed which should define, amongst other
+    things, what level of testing, review and documentation is expected to
+    accompany any proposed change.</li>
+
+    <li>All parties in the collaboration should note the advice given in the
+    <a href="../user_guide/code_management.html#svn_problems">FCM user
+    guide</a> to avoid renaming files or directories unless you can ensure that
+    no-one is working in parallel on the affected areas of the project.</li>
+
+    <li><acronym title="intellectual property rights">IPR</acronym>, copyright
+    and license issues should be agreed by all collaborators.</li>
+  </ul>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/collaboration/style.css
===================================================================
--- vendors/fcm/1-5/doc/collaboration/style.css	(revision 1981)
+++ vendors/fcm/1-5/doc/collaboration/style.css	(revision 1981)
@@ -0,0 +1,260 @@
+/**********************************************************************
+ * CSS for FCM documents.
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+a:visited
+{
+    color: purple;
+}
+
+a:active,
+a:hover
+{
+    color: blue;
+}
+
+code,
+kbd,
+var
+{
+    color: navy;
+}
+
+
+/**********************************************************************
+ * BACKGROUND COLOUR
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+code,
+kbd,
+pre,
+samp,
+.image
+{
+    background: #eee;
+}
+
+
+/**********************************************************************
+ * BORDER
+ *
+ **********************************************************************/
+dl,
+kbd,
+pre,
+samp,
+.image
+{
+    border: thin solid #ddd;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: sans-serif;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1
+{
+    font-size: 200%;
+}
+
+h2
+{
+    font-size: 175%;
+}
+
+h3
+{
+    font-size: 150%;
+}
+
+h4,
+h5
+{
+    font-size: medium;
+}
+
+address
+{
+    font-size: small;
+}
+
+address,
+var
+{
+    font-style: normal;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+#fcm-content ul li
+{
+    list-style: none;
+}
+
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT DECORATION
+ *
+ **********************************************************************/
+a:link
+{
+    text-decoration: underline;
+}
+
+a:visited
+{
+    text-decoration: none;
+}
+
+a:active,
+a:hover
+{
+    text-decoration: underline;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+#ukmo-logo,
+#document-info address
+{
+    text-align: right;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+dl,
+pre,
+.image
+{
+    border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+}
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/collaboration/style.html2ps.css
===================================================================
--- vendors/fcm/1-5/doc/collaboration/style.html2ps.css	(revision 1981)
+++ vendors/fcm/1-5/doc/collaboration/style.html2ps.css	(revision 1981)
@@ -0,0 +1,137 @@
+/**********************************************************************
+ * CSS for html2ps for FCM documents.
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: Helvetica;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+@page
+{
+    margin: 2cm;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/etc/fcm.js
===================================================================
--- vendors/fcm/1-5/doc/etc/fcm.js	(revision 1981)
+++ vendors/fcm/1-5/doc/etc/fcm.js	(revision 1981)
@@ -0,0 +1,212 @@
+//------------------------------------------------------------------------------
+// JS for displaying information of FCM documents.
+//------------------------------------------------------------------------------
+FCM = {
+    // List of heading tags for the contents
+    CONTENT_INDEX_OF: {'H2': 0, 'H3': 1, 'H4': 2, 'H5': 3, 'H6': 4},
+    // ID in a document, for the content
+    ID_OF_CONTENT: 'fcm-content',
+    // ID in a document, for the maintenance information
+    ID_OF_MAINTENANCE: 'fcm-js-maintenance',
+    // ID in a document, for the link to its PDF version
+    ID_OF_PDF: 'fcm-js-pdf',
+    // ID in a document, for the breadcrumb trail
+    ID_OF_TRAIL: 'fcm-js-trail',
+    // URL to the log browser of the FCM trunk
+    URL_OF_LOG_BROWSER: 'http://fcm1/projects/FCM/intertrac/log:/FCM/trunk/',
+    // URL to the FCM team page
+    URL_OF_TEAM: null,
+
+    // Making DOM manipulation less painful
+    DOM: {
+        // The current document
+        DOC: document,
+
+        // Adds an array of "nodes" (Node or String) to "node"
+        append: function(node, items) {
+            if (node == null || items == null) {
+                return;
+            }
+            for (var i = 0; i < items.length; i++) {
+                if (items[i] == null) {
+                    continue;
+                }
+                if (items[i].nodeType == null) {
+                    node.appendChild(this.DOC.createTextNode(items[i]));
+                }
+                else {
+                    node.appendChild(items[i]);
+                }
+            }
+        },
+
+        // Returns the base name of a URL, relative to current document
+        basename: function(url) {
+            if (url == null) {
+                url = this.DOC.URL;
+            }
+            return url.substr(this.DOC.URL.lastIndexOf('/') + 1);
+        },
+
+        // Creates and returns an element
+        create: function(tag, items, attribs) {
+            var element = this.DOC.createElement(tag);
+            this.append(element, items);
+            if (attribs == null) {
+                return element;
+            }
+            for (var i = 0; i < attribs.length; i++) {
+                element[attribs[i][0]] = attribs[i][1];
+            }
+            return element;
+        },
+
+        // Shorthand for root[attrib]
+        get_attrib: function(root, attrib) {
+            return root[attrib];
+        },
+
+        // Shorthand for this.DOC.getElementById(id)
+        get_by_id: function(id) {
+            return this.DOC.getElementById(id);
+        },
+
+        // Shorthand for root.getElementsByTagName(name)[i]
+        get_by_name: function(root, name, i) {
+            var elements = root.getElementsByTagName(name);
+            if (i == null) {
+                return elements;
+            }
+            if (elements == null || i >= elements.length) {
+                return null;
+            }
+            return elements[i];
+        },
+
+        map: function(action_on, items) {
+            var ret = [];
+            for (var i = 0; i < items.length; i++) {
+                ret = ret.concat(action_on(items[i], i));
+            }
+            return ret;
+        }
+    },
+
+    // Creates and returns the content
+    create_content: function() {
+        var root = $D.get_attrib($D.DOC, 'body');
+        var stack = [{content: null, item: null}];
+        for (var node = root.firstChild; node != null; node = node.nextSibling) {
+            if (
+                node.nodeType != 1
+                || node.id == null
+                || node.id == ''
+                || this.CONTENT_INDEX_OF[node.tagName] == null
+                || this.CONTENT_INDEX_OF[node.tagName] + 1 > stack.length
+            ) {
+                continue;
+            }
+            while (this.CONTENT_INDEX_OF[node.tagName] + 1 < stack.length) {
+                stack.shift();
+            }
+            if (stack[0].content == null) {
+                stack[0].content = $D.create('ul');
+                if (stack[0].item != null) {
+                    $D.append(stack[0].item, [stack[0].content]);
+                }
+            }
+            var item = $D.create(
+                'li',
+                [$D.create('a', [node.innerHTML], [['href', '#' + node.id]])]
+            );
+            $D.append(stack[0].content, [item]);
+            stack.unshift({content: null, item: item});
+        }
+        return [stack[stack.length - 1].content];
+    },
+
+    // Creates and returns the maintenance information for "path"
+    create_info_for_maintenance: function(path) {
+        if (this.URL_OF_TEAM == null) {
+            return [];
+        }
+        return [
+            'Maintained by: ',
+            $D.create('a', ['FCM team'], [['href', FCM.URL_OF_TEAM]]),
+            '. History: ',
+            $D.create(
+                'a',
+                ['log'],
+                [['href', this.URL_OF_LOG_BROWSER + path + $D.basename()]]
+            ),
+            '.'
+        ];
+    },
+
+    // Creates and returns the PDF link for "name"
+    create_info_for_pdf: function(name) {
+        return [
+            'For printing, please use the ',
+            $D.create('a', ['PDF'], [['href', 'fcm-' + name + '.pdf']]),
+            ' version of the document.'
+        ];
+    },
+
+    // Creates a breadcrumb trail from a list of [[href, text], ...]
+    create_trail: function(items) {
+        if (items == null) {
+            return [];
+        }
+        return [].concat(
+            $D.map(
+                function(item, i) {
+                    return [].concat(
+                        (i != 0 ? ' > ' : []),
+                        $D.create('a', [item[1]], [['href', item[0]]])
+                    );
+                },
+                items
+            ),
+            ' > ',
+            $D.get_by_name($D.DOC, 'title', 0).innerHTML
+        );
+    },
+
+    // A simple facade for doing the onload tasks
+    load: function(maintenance_path, pdf_name, trail_list) {
+        var tasks = [
+            {
+                id: this.ID_OF_CONTENT,
+                task: function() {
+                    return FCM.create_content();
+                }
+            },
+            {
+                id: this.ID_OF_MAINTENANCE,
+                task: function() {
+                    return FCM.create_info_for_maintenance(maintenance_path);
+                }
+            },
+            {
+                id: this.ID_OF_PDF,
+                task: function() {
+                    return FCM.create_info_for_pdf(pdf_name);
+                }
+            },
+            {
+                id: this.ID_OF_TRAIL,
+                task: function() {
+                    return FCM.create_trail(trail_list);
+                }
+            }
+        ];
+        for (var i = 0; i < tasks.length; i++) {
+            var node = $D.get_by_id(tasks[i].id);
+            if (node == null) {
+                continue;
+            }
+            $D.append(node, tasks[i].task());
+        }
+    }
+};
+$D = FCM.DOM;
Index: vendors/fcm/1-5/doc/etc/style.css
===================================================================
--- vendors/fcm/1-5/doc/etc/style.css	(revision 1981)
+++ vendors/fcm/1-5/doc/etc/style.css	(revision 1981)
@@ -0,0 +1,260 @@
+/**********************************************************************
+ * CSS for FCM documents.
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+a:visited
+{
+    color: purple;
+}
+
+a:active,
+a:hover
+{
+    color: blue;
+}
+
+code,
+kbd,
+var
+{
+    color: navy;
+}
+
+
+/**********************************************************************
+ * BACKGROUND COLOUR
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+code,
+kbd,
+pre,
+samp,
+.image
+{
+    background: #eee;
+}
+
+
+/**********************************************************************
+ * BORDER
+ *
+ **********************************************************************/
+dl,
+kbd,
+pre,
+samp,
+.image
+{
+    border: thin solid #ddd;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: sans-serif;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1
+{
+    font-size: 200%;
+}
+
+h2
+{
+    font-size: 175%;
+}
+
+h3
+{
+    font-size: 150%;
+}
+
+h4,
+h5
+{
+    font-size: medium;
+}
+
+address
+{
+    font-size: small;
+}
+
+address,
+var
+{
+    font-style: normal;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+#fcm-content ul li
+{
+    list-style: none;
+}
+
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT DECORATION
+ *
+ **********************************************************************/
+a:link
+{
+    text-decoration: underline;
+}
+
+a:visited
+{
+    text-decoration: none;
+}
+
+a:active,
+a:hover
+{
+    text-decoration: underline;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+#ukmo-logo,
+#document-info address
+{
+    text-align: right;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+dl,
+pre,
+.image
+{
+    border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+}
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/etc/style.html2ps.css
===================================================================
--- vendors/fcm/1-5/doc/etc/style.html2ps.css	(revision 1981)
+++ vendors/fcm/1-5/doc/etc/style.html2ps.css	(revision 1981)
@@ -0,0 +1,137 @@
+/**********************************************************************
+ * CSS for html2ps for FCM documents.
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: Helvetica;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+@page
+{
+    margin: 2cm;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/release_notes/1-1.html
===================================================================
--- vendors/fcm/1-5/doc/release_notes/1-1.html	(revision 1981)
+++ vendors/fcm/1-5/doc/release_notes/1-1.html	(revision 1981)
@@ -0,0 +1,453 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM 1.1 Release Notes</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="FCM Release Notes" />
+  <meta name="keywords" content="FCM, release" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+
+<body>
+  <h1>FCM 1.1 Release Notes<br />
+  06 November 2006</h1>
+
+  <p>These are the release notes for FCM release 1.1. You can use this release
+  of FCM freely under the terms of the <a href="../../LICENSE.html">FCM
+  LICENSE</a>, which you should receive with this distribution. Release 1.1 is
+  the first external release of FCM. (Release 1.0, 30 November 2005, was an
+  internal Met Office release which marked the start of the main migration of
+  systems into FCM.)</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>
+
+  <ul>
+    <li><a href="#new">What's New?</a></li>
+
+    <li><a href="#fix">Minor Enhancements &amp; Bug Fixes</a></li>
+
+    <li><a href="#req">System Requirements</a></li>
+
+    <li><a href="#ins">Installation</a></li>
+  </ul>
+
+  <h2 id="new">What's New?</h2>
+
+  <p>Build system:</p>
+
+  <ul>
+    <li>Support building of Fortran <code>BLOCKDATA</code> program units.</li>
+
+    <li>Option to generate Fortran interface files in lower case names after
+    their program units.</li>
+
+    <li>Allow renaming of main program targets using the build configuration
+    file.</li>
+
+    <li>The <samp>fcm_env.ksh</samp> file now provides an environment variable
+    for the <samp>etc/</samp> sub-directory of the build which can be used if
+    you are <em>building</em> data files.</li>
+
+    <li>The build root directory is now locked while a build is running. This
+    prevents multiple instances of build running in the same directory.
+    However, you can bypass the lock if you specify the new
+    <code>--ignore-lock</code> option with <code>fcm build</code>.</li>
+  </ul>
+
+  <p>Extract system:</p>
+
+  <ul>
+    <li>The destination root directory is now locked while an extract is
+    running. This prevents multiple instances of extract running in the same
+    directory. However, you can bypass the lock if you specify the new
+    <code>--ignore-lock</code> option with <code>fcm extract</code>.</li>
+  </ul>
+
+  <p>Code management commands:</p>
+
+  <ul>
+    <li><code>fcm merge</code> now supports custom and reverse modes.</li>
+
+    <li><code>fcm merge</code> now allows automatic merges in sub-trees if it
+    is safe.</li>
+
+    <li><code>fcm merge</code> now handles automatic merges from sibling
+    branches that are created at different revisions of the parent.</li>
+
+    <li><code>fcm branch, fcm diff --branch</code> and <code>fcm merge</code>
+    can now handle creation, diff and automatic merge of a branch of a
+    branch.</li>
+
+    <li><code>fcm switch</code> is improved to allow safer switches of your
+    working copy to point to different branches in your project.</li>
+
+    <li><code>fcm commit</code> now displays your location in the branch, and
+    extra warning when you are committing to the trunk of a project.</li>
+
+    <li>New <code>fcm mkpatch</code> command.</li>
+  </ul>
+
+  <p>General:</p>
+
+  <ul>
+    <li>Error handling is improved.</li>
+
+    <li>The User Guide is now complete, with a much improved tutorial.</li>
+
+    <li>New document: Fortran coding standard for FCM.</li>
+  </ul>
+
+  <h2 id="fix">Minor Enhancements &amp; Bug Fixes</h2>
+
+  <p>Build system:</p>
+
+  <ul>
+    <li>Ignore empty Fortran source files, so that alternate sections can be
+    handled correctly.</li>
+
+    <li>Handle recursive header file dependency correctly in pre-processing
+    stage.</li>
+
+    <li>Identify changes in pre-processed source file, the pre-processor
+    options and keys correctly in incremental builds.</li>
+
+    <li>Improve speed of pre-processing in incremental builds by not performing
+    any unnecessary null-action pre-processing.</li>
+
+    <li>Load archiver no longer includes main program objects.</li>
+
+    <li>It is now possible for a sub-package to exclude particular types of
+    dependencies.</li>
+  </ul>
+
+  <p>Extract system:</p>
+
+  <ul>
+    <li>Sub-directories are now extracted with the non-recursive mode of
+    <code>svn export</code>.</li>
+
+    <li>Peg revisions should now be handled correctly in <var>INC</var> extract
+    declarations.</li>
+
+    <li>The command should now fail if a declared source directory does not
+    exist or if the update of an extract destination fails.</li>
+  </ul>
+
+  <p>Code management commands:</p>
+
+  <ul>
+    <li><code>fcm branch --info</code> and <code>fcm diff</code> can now take a
+    <code>PATH</code> as an argument.</li>
+
+    <li>The <code>--ticket</code> option of <code>fcm branch --create</code>
+    can now accept multiple tickets.</li>
+
+    <li><code>fcm branch --info</code> and <code>fcm diff --branch</code>
+    should now work correctly in a sub-tree of a branch.</li>
+
+    <li><code>fcm branch --create</code> no longer offers to checkout the
+    branch.</li>
+
+    <li><code>fcm commit</code> no longer fails when adding a new symbolic
+    link.</li>
+
+    <li>The <code>--password</code> option is now supported by the <code>fcm
+    branch --create</code>, <code>fcm branch --delete</code>, <code>fcm
+    commit</code> and <code>fcm delete</code> commands.</li>
+
+    <li>Empty arguments to code management commands are now parsed
+    correctly.</li>
+
+    <li>The <code>fcm diff --graphical</code> option no longer fails with
+    binary files.</li>
+
+    <li>FCM will always set the environment variable <var>LANG=en_GB</var>
+    before running Subversion commands. This prevents failure of FCM when it
+    attempts to parse output from Subversion commands when a different
+    <var>LANG</var> setting is used.</li>
+  </ul>
+
+  <p>General:</p>
+
+  <ul>
+    <li>Various other very minor enhancements and bug fixes.</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.2.0.</li>
+
+    <li>At the Met Office we are currently using Subversion 1.3.2 (although
+    1.2.3 was used until very recently).</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). At the
+  Met Office we are currently using version 0.9.6.</p>
+
+  <h3 id="req_other">Other Requirements</h3>
+
+  <p>The <code>fcm diff --graphical</code> and <code>fcm conflicts</code>
+  commands require <a href="http://furius.ca/xxdiff/">xxdiff</a>. At the Met
+  Office we are currently using version 3.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 9 and Red Hat Enterprise 2.1 and 4.4) 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 FCM User Guide in both
+    <a href="../user_guide/">HTML</a> and <a href=
+    "../user_guide/fcm-user-guide.pdf">PDF</a> form.</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>
+  </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.</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
+        exists. 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 <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/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.ksh</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>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/release_notes/1-2.html
===================================================================
--- vendors/fcm/1-5/doc/release_notes/1-2.html	(revision 1981)
+++ vendors/fcm/1-5/doc/release_notes/1-2.html	(revision 1981)
@@ -0,0 +1,377 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM 1.2 Release Notes</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="FCM Release Notes" />
+  <meta name="keywords" content="FCM, release" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+
+<body>
+  <h1>FCM 1.2 Release Notes<br />
+  22 March 2007</h1>
+
+  <p>These are the release notes for FCM release 1.2. You can use this release
+  of FCM freely under the terms of the <a href="../../LICENSE.html">FCM
+  LICENSE</a>, which you should receive with this distribution.</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>
+
+  <ul>
+    <li><a href="#new">What's New?</a></li>
+
+    <li><a href="#fix">Minor enhancements &amp; Bug Fixes</a></li>
+
+    <li><a href="#req">System Requirements</a></li>
+
+    <li><a href="#ins">Installation</a></li>
+  </ul>
+
+  <h2 id="new">What's New?</h2>
+
+  <p>Code management commands:</p>
+
+  <ul>
+    <li>New options <code>--trac</code> and <code>--wiki</code> for <code>fcm
+    diff --branch</code>.</li>
+
+    <li>Allow other graphical diff tools to be used in place of
+    <code>xxdiff</code>.</li>
+  </ul>
+
+  <p>General:</p>
+
+  <ul>
+    <li>New document <cite>External Distribution &amp; Collaboration for FCM
+    Projects</cite>.</li>
+  </ul>
+
+  <h2 id="fix">Minor enhancements &amp; Bug Fixes</h2>
+
+  <p>Build system:</p>
+
+  <ul>
+    <li>Extra warnings when multiple targets are detected in the source
+    tree.</li>
+
+    <li>Improved the patterns for detecting <code>recursive</code>,
+    <code>pure</code> and <code>elemental</code> Fortran subroutines and
+    functions.</li>
+  </ul>
+
+  <p>Code management commands:</p>
+
+  <ul>
+    <li><code>fcm branch --list</code> now prints the branches using FCM URL
+    keywords by default. Use the <code>--verbose</code> option to print
+    branches in full Subversion URLs.</li>
+  </ul>
+
+  <p>General:</p>
+
+  <ul>
+    <li>Enhanced <code>fcm cmp-ext-cfg</code> to link to tickets.</li>
+
+    <li>Improved handling of FCM URL keywords. The <code>SET::REPOS</code>
+    declaration in the central/user configuration file is deprecated in favour
+    of <code>SET::URL</code>. The keyword of the project with the standard
+    suffices (<code>_tr</code> or <code>-tr</code> for <em>trunk</em>,
+    <code>_br</code> or <code>-br</code> for <em>branches</em>, and
+    <code>_tg</code> or <code>-tg</code> for <em>tags</em>) are recognised
+    automatically.</li>
+
+    <li>Fix: full extract/build should no longer delete one another's cache if
+    they are run in the same directory.</li>
+
+    <li>Various other very minor enhancements and bug fixes.</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.2.0.</li>
+
+    <li>At the Met Office we are currently using Subversion 1.3.2.</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 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.4) 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.</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
+        exists. 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 <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/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.ksh</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>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/release_notes/1-3.html
===================================================================
--- vendors/fcm/1-5/doc/release_notes/1-3.html	(revision 1981)
+++ vendors/fcm/1-5/doc/release_notes/1-3.html	(revision 1981)
@@ -0,0 +1,562 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM 1.3 Release Notes</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="FCM Release Notes" />
+  <meta name="keywords" content="FCM, release" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+
+<body>
+  <h1>FCM 1.3 Release Notes<br />
+  30 January 2008</h1>
+
+  <p>These are the release notes for FCM release 1.3. You can use this release
+  of FCM freely under the terms of the <a href="../../LICENSE.html">FCM
+  LICENSE</a>, which you should receive with this distribution.</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>Contents</h2>
+
+  <ul>
+    <li><a href="#new">What's New?</a></li>
+
+    <li><a href="#fix">Minor enhancements &amp; Bug Fixes</a></li>
+
+    <li><a href="#req">System Requirements</a></li>
+
+    <li><a href="#ins">Installation</a></li>
+  </ul>
+
+  <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>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/release_notes/1-4.html
===================================================================
--- vendors/fcm/1-5/doc/release_notes/1-4.html	(revision 1981)
+++ vendors/fcm/1-5/doc/release_notes/1-4.html	(revision 1981)
@@ -0,0 +1,370 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM 1.4 Release Notes</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="FCM Release Notes" />
+  <meta name="keywords" content="FCM, release" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+
+<body>
+  <h1>FCM 1.4 Release Notes<br />
+  12 February 2009</h1>
+
+  <p>These are the release notes for FCM release 1.4. You can use this release
+  of FCM freely under the terms of the <a href="../../LICENSE.html">FCM
+  LICENSE</a>, which you should receive with this distribution.</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>Contents</h2>
+
+  <ul>
+    <li><a href="#fix">Minor enhancements &amp; Bug Fixes</a></li>
+
+    <li><a href="#req">System Requirements</a></li>
+
+    <li><a href="#ins">Installation</a></li>
+  </ul>
+
+  <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>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/release_notes/1-5.html
===================================================================
--- vendors/fcm/1-5/doc/release_notes/1-5.html	(revision 1981)
+++ vendors/fcm/1-5/doc/release_notes/1-5.html	(revision 1981)
@@ -0,0 +1,460 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM 1.5 Release Notes</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="FCM Release Notes" />
+  <meta name="keywords" content="FCM, release" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+
+<body>
+  <h1>FCM 1.5 Release Notes<br />
+  22 January 2010</h1>
+
+  <p>These are the release notes for FCM release 1.5. You can use this release
+  of FCM freely under the terms of the <a href="../../LICENSE.html">FCM
+  LICENSE</a>, which you should receive with this distribution.</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>
+
+  <ul>
+    <li><a href="#new">What's New?</a></li>
+
+    <li><a href="#fix">Minor Enhancements &amp; Bug Fixes</a></li>
+
+    <li><a href="#req">System Requirements</a></li>
+
+    <li><a href="#ins">Installation</a></li>
+  </ul>
+
+  <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>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/release_notes/index.html
===================================================================
--- vendors/fcm/1-5/doc/release_notes/index.html	(revision 1981)
+++ vendors/fcm/1-5/doc/release_notes/index.html	(revision 1981)
@@ -0,0 +1,64 @@
+<!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" lang="en" xml:lang="en">
+<head>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM Release Notes</title>
+  <meta name="author" content="FCM development team" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+
+<body>
+  <h1>FCM Release Notes</h1>
+
+  <p>The Flexible Configuration Management (FCM) system is a software system
+  for managing and building source code. It is developed at the <a href=
+  "http://www.metoffice.gov.uk/">Met Office</a> using popular open source
+  tools, (e.g. <a href="http://subversion.tigris.org/">Subversion</a>, <a href=
+  "http://trac.edgewall.org/">Trac</a> and <a href=
+  "http://www.gnu.org/software/make/">GNU make</a>).</p>
+
+  <p>FCM is distributed under the terms of the <a href="../../LICENSE.html">FCM
+  LICENSE</a>, which you should receive with this distribution.</p>
+
+  <p>FCM is maintained by the FCM team at the <a href=
+  "http://www.metoffice.gov.uk/">Met Office</a>. Please feedback any bug
+  reports or feature requests to us by <a href=
+  "mailto:fcm-team@metoffice.gov.uk">e-mail</a>.</p>
+
+  <p>Current and past release notes:</p>
+
+  <dl>
+    <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>
+  <hr />
+
+  <address>
+    &copy; Crown copyright 2006-10 <a href="http://www.metoffice.gov.uk/">Met
+    Office</a>. All rights reserved.
+  </address>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/release_notes/style.css
===================================================================
--- vendors/fcm/1-5/doc/release_notes/style.css	(revision 1981)
+++ vendors/fcm/1-5/doc/release_notes/style.css	(revision 1981)
@@ -0,0 +1,260 @@
+/**********************************************************************
+ * CSS for FCM documents.
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+a:visited
+{
+    color: purple;
+}
+
+a:active,
+a:hover
+{
+    color: blue;
+}
+
+code,
+kbd,
+var
+{
+    color: navy;
+}
+
+
+/**********************************************************************
+ * BACKGROUND COLOUR
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+code,
+kbd,
+pre,
+samp,
+.image
+{
+    background: #eee;
+}
+
+
+/**********************************************************************
+ * BORDER
+ *
+ **********************************************************************/
+dl,
+kbd,
+pre,
+samp,
+.image
+{
+    border: thin solid #ddd;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: sans-serif;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1
+{
+    font-size: 200%;
+}
+
+h2
+{
+    font-size: 175%;
+}
+
+h3
+{
+    font-size: 150%;
+}
+
+h4,
+h5
+{
+    font-size: medium;
+}
+
+address
+{
+    font-size: small;
+}
+
+address,
+var
+{
+    font-style: normal;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+#fcm-content ul li
+{
+    list-style: none;
+}
+
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT DECORATION
+ *
+ **********************************************************************/
+a:link
+{
+    text-decoration: underline;
+}
+
+a:visited
+{
+    text-decoration: none;
+}
+
+a:active,
+a:hover
+{
+    text-decoration: underline;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+#ukmo-logo,
+#document-info address
+{
+    text-align: right;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+dl,
+pre,
+.image
+{
+    border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+}
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/standards/fcm.js
===================================================================
--- vendors/fcm/1-5/doc/standards/fcm.js	(revision 1981)
+++ vendors/fcm/1-5/doc/standards/fcm.js	(revision 1981)
@@ -0,0 +1,212 @@
+//------------------------------------------------------------------------------
+// JS for displaying information of FCM documents.
+//------------------------------------------------------------------------------
+FCM = {
+    // List of heading tags for the contents
+    CONTENT_INDEX_OF: {'H2': 0, 'H3': 1, 'H4': 2, 'H5': 3, 'H6': 4},
+    // ID in a document, for the content
+    ID_OF_CONTENT: 'fcm-content',
+    // ID in a document, for the maintenance information
+    ID_OF_MAINTENANCE: 'fcm-js-maintenance',
+    // ID in a document, for the link to its PDF version
+    ID_OF_PDF: 'fcm-js-pdf',
+    // ID in a document, for the breadcrumb trail
+    ID_OF_TRAIL: 'fcm-js-trail',
+    // URL to the log browser of the FCM trunk
+    URL_OF_LOG_BROWSER: 'http://fcm1/projects/FCM/intertrac/log:/FCM/trunk/',
+    // URL to the FCM team page
+    URL_OF_TEAM: null,
+
+    // Making DOM manipulation less painful
+    DOM: {
+        // The current document
+        DOC: document,
+
+        // Adds an array of "nodes" (Node or String) to "node"
+        append: function(node, items) {
+            if (node == null || items == null) {
+                return;
+            }
+            for (var i = 0; i < items.length; i++) {
+                if (items[i] == null) {
+                    continue;
+                }
+                if (items[i].nodeType == null) {
+                    node.appendChild(this.DOC.createTextNode(items[i]));
+                }
+                else {
+                    node.appendChild(items[i]);
+                }
+            }
+        },
+
+        // Returns the base name of a URL, relative to current document
+        basename: function(url) {
+            if (url == null) {
+                url = this.DOC.URL;
+            }
+            return url.substr(this.DOC.URL.lastIndexOf('/') + 1);
+        },
+
+        // Creates and returns an element
+        create: function(tag, items, attribs) {
+            var element = this.DOC.createElement(tag);
+            this.append(element, items);
+            if (attribs == null) {
+                return element;
+            }
+            for (var i = 0; i < attribs.length; i++) {
+                element[attribs[i][0]] = attribs[i][1];
+            }
+            return element;
+        },
+
+        // Shorthand for root[attrib]
+        get_attrib: function(root, attrib) {
+            return root[attrib];
+        },
+
+        // Shorthand for this.DOC.getElementById(id)
+        get_by_id: function(id) {
+            return this.DOC.getElementById(id);
+        },
+
+        // Shorthand for root.getElementsByTagName(name)[i]
+        get_by_name: function(root, name, i) {
+            var elements = root.getElementsByTagName(name);
+            if (i == null) {
+                return elements;
+            }
+            if (elements == null || i >= elements.length) {
+                return null;
+            }
+            return elements[i];
+        },
+
+        map: function(action_on, items) {
+            var ret = [];
+            for (var i = 0; i < items.length; i++) {
+                ret = ret.concat(action_on(items[i], i));
+            }
+            return ret;
+        }
+    },
+
+    // Creates and returns the content
+    create_content: function() {
+        var root = $D.get_attrib($D.DOC, 'body');
+        var stack = [{content: null, item: null}];
+        for (var node = root.firstChild; node != null; node = node.nextSibling) {
+            if (
+                node.nodeType != 1
+                || node.id == null
+                || node.id == ''
+                || this.CONTENT_INDEX_OF[node.tagName] == null
+                || this.CONTENT_INDEX_OF[node.tagName] + 1 > stack.length
+            ) {
+                continue;
+            }
+            while (this.CONTENT_INDEX_OF[node.tagName] + 1 < stack.length) {
+                stack.shift();
+            }
+            if (stack[0].content == null) {
+                stack[0].content = $D.create('ul');
+                if (stack[0].item != null) {
+                    $D.append(stack[0].item, [stack[0].content]);
+                }
+            }
+            var item = $D.create(
+                'li',
+                [$D.create('a', [node.innerHTML], [['href', '#' + node.id]])]
+            );
+            $D.append(stack[0].content, [item]);
+            stack.unshift({content: null, item: item});
+        }
+        return [stack[stack.length - 1].content];
+    },
+
+    // Creates and returns the maintenance information for "path"
+    create_info_for_maintenance: function(path) {
+        if (this.URL_OF_TEAM == null) {
+            return [];
+        }
+        return [
+            'Maintained by: ',
+            $D.create('a', ['FCM team'], [['href', FCM.URL_OF_TEAM]]),
+            '. History: ',
+            $D.create(
+                'a',
+                ['log'],
+                [['href', this.URL_OF_LOG_BROWSER + path + $D.basename()]]
+            ),
+            '.'
+        ];
+    },
+
+    // Creates and returns the PDF link for "name"
+    create_info_for_pdf: function(name) {
+        return [
+            'For printing, please use the ',
+            $D.create('a', ['PDF'], [['href', 'fcm-' + name + '.pdf']]),
+            ' version of the document.'
+        ];
+    },
+
+    // Creates a breadcrumb trail from a list of [[href, text], ...]
+    create_trail: function(items) {
+        if (items == null) {
+            return [];
+        }
+        return [].concat(
+            $D.map(
+                function(item, i) {
+                    return [].concat(
+                        (i != 0 ? ' > ' : []),
+                        $D.create('a', [item[1]], [['href', item[0]]])
+                    );
+                },
+                items
+            ),
+            ' > ',
+            $D.get_by_name($D.DOC, 'title', 0).innerHTML
+        );
+    },
+
+    // A simple facade for doing the onload tasks
+    load: function(maintenance_path, pdf_name, trail_list) {
+        var tasks = [
+            {
+                id: this.ID_OF_CONTENT,
+                task: function() {
+                    return FCM.create_content();
+                }
+            },
+            {
+                id: this.ID_OF_MAINTENANCE,
+                task: function() {
+                    return FCM.create_info_for_maintenance(maintenance_path);
+                }
+            },
+            {
+                id: this.ID_OF_PDF,
+                task: function() {
+                    return FCM.create_info_for_pdf(pdf_name);
+                }
+            },
+            {
+                id: this.ID_OF_TRAIL,
+                task: function() {
+                    return FCM.create_trail(trail_list);
+                }
+            }
+        ];
+        for (var i = 0; i < tasks.length; i++) {
+            var node = $D.get_by_id(tasks[i].id);
+            if (node == null) {
+                continue;
+            }
+            $D.append(node, tasks[i].task());
+        }
+    }
+};
+$D = FCM.DOM;
Index: vendors/fcm/1-5/doc/standards/fortran_standard.html
===================================================================
--- vendors/fcm/1-5/doc/standards/fortran_standard.html	(revision 1981)
+++ vendors/fcm/1-5/doc/standards/fortran_standard.html	(revision 1981)
@@ -0,0 +1,998 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>Fortran coding standard for FCM</title>
+  <meta name="author" content="FCM team" />
+  <meta name="descriptions" content="Fortran coding standard for FCM" />
+  <meta name="keywords" content="Fortran, coding standard, FCM" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload="javascript: FCM.load('doc/standards/', 'fortran-standard');">
+  <div id="ukmo-logo">
+    <img src="logo.png" alt="UK Met Office" />
+  </div>
+
+  <h1>Fortran coding standard for FCM</h1>
+
+  <address>
+    Latest content update: 14 January 2010. <span id=
+    "fcm-js-maintenance"></span>
+  </address>
+
+  <address>
+    Questions regarding this document or permissions to quote from it should be
+    directed to:
+  </address>
+
+  <address>
+    <a href="mailto:iprmanager@metoffice.gov.uk">IPR Manager</a><br />
+    Met Office<br />
+    FitzRoy Road<br />
+    Exeter, Devon<br />
+    EX1 3PB<br />
+    United Kingdom
+  </address>
+
+  <address>
+    &copy; Crown Copyright 2006-10
+  </address>
+
+  <address id="fcm-js-pdf"></address>
+  <hr />
+
+  <div id="fcm-content"></div>
+
+  <h2 id="intro">1. Introduction</h2>
+
+  <p>Fortran is the standard programming language at the Met Office for
+  developing scientific and research applications, in particular, for programs
+  running on the supercomputers. This document describes some guidelines that
+  should be followed by developers when writing Fortran code, especially for
+  code in systems hosted by FCM.</p>
+
+  <h2 id="fcm">2. Programming Fortran for the FCM build system</h2>
+
+  <h3 id="fcm-general">2.1 General</h3>
+
+  <p>To get the most out of the FCM build system, you should follow these
+  guidelines when you develop your code:</p>
+
+  <ol>
+    <li>Each source file should contain one and no more than one top level
+    program unit, (such as a <code>PROGRAM</code>, a standalone
+    <code>SUBROUTINE</code>/<code>FUNCTION</code> or a <code>MODULE</code>).
+    All top level standalone program units in a source tree should be uniquely
+    named. ( <dfn>Top level</dfn> means a standalone program unit that is
+    compilable independently, i.e. this rule does not restrict the naming and
+    placements of sub-programs in a <code>CONTAINS</code> section.) FCM may
+    fail to set up the dependency tree of your source correctly if you do not
+    follow this rule.
+
+      <p>A clash of program unit names happens most often when you have
+      multiple versions of the same program unit in the same source tree. You
+      should design your code to avoid this situation. If it is not possible to
+      do so, you may have to use a pre-processor to ensure that there is only
+      one copy of each program unit in the source tree. Another situation where
+      clashes of program unit names may occur is when you are developing code
+      that is shared between several projects. In this case, you may want to
+      agree with the other projects a naming convention to define a unique
+      namespace for program units in each project. (E.g. some projects at the
+      Met Office have adopted a naming convention such that all shared program
+      units in a project are named with a unique prefix.)</p>
+    </li>
+
+    <li>All code should be written using the free source form. (At Fortran 95,
+    the free source form can have a maximum of 132 characters per line, and up
+    to 39 continuations in a Fortran statement.) The fixed source form is
+    obsolete, and is not supported by the interface file generators used by
+    FCM.</li>
+
+    <li>An interface should be provided when calling a <code>SUBROUTINE</code>
+    or a <code>FUNCTION</code>. Not only is this considered good practice, it
+    also allows FCM to determine the dependency relationship of your source
+    files. An interface can be provided in these ways:
+
+      <dl>
+        <dt>Internal sub-program</dt>
+
+        <dd>
+          <p>Place sub-programs 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.</p>
+
+          <p>Note: One way to share a sub-program unit between several top
+          level program units is to make use of the Fortran
+          <code>INCLUDE</code> statement. You can write the sub-program unit in
+          a separate file and place it in the <code>CONTAINS</code> section of
+          different program units using <code>INCLUDE</code> statements. The
+          disadvantage of this approach is that when the program is compiled, a
+          copy of the sub-program unit will be embedded within each of the top
+          level program units. This may lead to an increase in size of the
+          executable, and so this approach is still only suitable for small
+          sub-programs local to a small number of program units.</p>
+
+          <p>For example, if we have a <code>SUBROUTINE</code> in
+          <samp>sub_prog.inc</samp>:</p>
+        </dd>
+
+        <dd>
+          <pre>
+SUBROUTINE sub_prog(some, arg)
+! Some declarations ...
+! Some executable statements ...
+END SUBROUTINE sub_prog
+</pre>
+
+          <p>It can be placed in a <code>MODULE</code> in
+          <samp>bar.f90</samp>:</p>
+        </dd>
+
+        <dd>
+          <pre>
+SUBROUTINE bar(more, arg)
+! Some declarations ...
+! Some executable statements ...
+CALL sub_prog(some, arg)
+! More executable statements ...
+CONTAINS
+  INCLUDE 'sub_prog.inc'
+END SUBROUTINE bar
+</pre>
+        </dd>
+
+        <dt>Module procedures</dt>
+
+        <dd>
+          <p>Place sub-programs in the <code>CONTAINS</code> section of a
+          <code>MODULE</code>. Again, the sub-programs will have automatic
+          interfaces when the <code>MODULE</code> is compiled. If you give the
+          sub-programs the PUBLIC attribute (which is the default), you will be
+          able to call them from anywhere using the current
+          <code>MODULE</code>. You will also gain all the advantages offered by
+          a <code>MODULE</code>. (E.g. a <code>MODULE</code> will allow you to
+          design your code in a more object-oriented manner.) However,
+          <code>MODULE</code> dependency can have an impact on the efficiency
+          of incremental compilations. For example, if you modify items that
+          are local to the <code>MODULE</code>, it is very difficult for the
+          build system to detect that your change does not affect program units
+          using the <code>MODULE</code>, so the build system will end up
+          compiling the <code>MODULE</code> and all the program units that use
+          it.</p>
+
+          <p>For example, if we have a <code>MODULE</code> in
+          <samp>my_mod.f90</samp>:</p>
+          <pre>
+MODULE my_mod
+! Some module declarations
+CONTAINS
+  SUBROUTINE sub_prog(some, arg)
+  ! Some declarations ...
+  ! Some executable statements ...
+  END SUBROUTINE sub_prog
+END MODULE my_mod
+</pre>
+
+          <p>It can be imported to another program as such:</p>
+          <pre>
+SUBROUTINE foo(some, arg)
+USE my_mod, ONLY: sub_prog
+! Some declarations ...
+! Some executable statements ...
+CALL sub_prog(some, arg)
+! More executable statements ...
+END SUBROUTINE foo
+</pre>
+        </dd>
+
+        <dt>Interface files</dt>
+
+        <dd>
+          <p>For each source file containing a standalone
+          <code>SUBROUTINE</code> or <code>FUNCTION</code>, FCM generates a
+          file containing the interface of the <code>SUBROUTINE</code> or
+          <code>FUNCTION</code>. By default, the generated file is named after
+          the original source file, but with the file extension replaced by
+          <samp>*.interface</samp>. In the specification section of the caller
+          routine, you will then be able to declare the interface using a
+          Fortran <code>INCLUDE</code> statement to include the interface file.
+          This type of <code>INCLUDE</code> statement is detected automatically
+          by FCM, which will use it to set up the dependency tree.</p>
+
+          <p>The advantage of using an interface file is that the caller is now
+          dependent on the interface file, rather than the
+          <code>SUBROUTINE</code> or <code>FUNCTION</code> itself. If you
+          change the <code>SUBROUTINE</code> or <code>FUNCTION</code> without
+          modifying its interface, the build system will not re-compile the
+          caller in incremental build, (but it will be intelligent enough to
+          re-link the executable with the updated object).</p>
+
+          <p>Note: By default, an interface file is named after the original
+          source file. Bearing this in mind, it is worth noting that file names
+          in a Unix/Linux system are case-sensitive, and so the interface file
+          name declared by your <code>INCLUDE</code> statement is also case
+          sensitive. If you use an incorrect case in the <code>INCLUDE</code>
+          statement, the dependency tree will be set up incorrectly and the
+          compilation will fail. Another problem is that if you do not name
+          your file after the program unit, the dependency tree will be wrong.
+          To avoid this problem, it is recommended that all source files are
+          named in lower case after the program units they contain.
+          (Alternatively, you can use the <code>TOOL::INTERFACE</code> option
+          in the FCM build configuration file to allow you to alter the default
+          behaviour so that the interface file is named after the program unit
+          in lowercase. We may alter FCM in the future so that this will become
+          the default. In the mean time, it is highly recommended that you use
+          this option and design your new code accordingly.)</p>
+
+          <p>For example, if we have a <code>SUBROUTINE</code> in
+          <samp>sub_prog.f90</samp>:</p>
+          <pre>
+SUBROUTINE sub_prog(some, arg)
+! Some declarations ...
+! Some executable statements ...
+END SUBROUTINE sub_prog
+</pre>
+
+          <p>It can be imported to another program as such:</p>
+          <pre>
+SUBROUTINE egg(some, arg)
+! Some declarations ...
+INCLUDE 'sub_prog.interface'
+! More declarations ...
+! Some executable statements ...
+CALL sub_prog(some, arg)
+! More executable statements ...
+END SUBROUTINE egg
+</pre>
+        </dd>
+
+        <dt>Interfaces in a module</dt>
+
+        <dd>
+          <p>There is also a half-way house approach between the second and the
+          third options. You can have a dedicated <code>MODULE</code> where a
+          large number of <code>INCLUDE</code> interface file statements are
+          placed. Other program units get their interfaces by importing from
+          this <code>MODULE</code>. A major disadvantage of this approach is
+          that the sub-programs with their interfaces declared within this
+          <code>MODULE</code> will not be able to call any other sub-programs
+          declared within the same <code>MODULE</code>, as it will run into a
+          cyclic dependency problem. Another disadvantage is that if an
+          interface changes, the <code>MODULE</code> and all program units
+          depending on the <code>MODULE</code> will have to be re-compiled,
+          even though the change may be unrelated to some or all of these
+          program units. For these reasons, this approach is only good if you
+          have a bundle of sub-programs that have relatively stable interfaces
+          and are very much independent of one another.</p>
+
+          <p>Note: a similar approach can be useful when you have a library of
+          legacy or external code. In this situation, you will simply declare
+          the interfaces for all the library sub-programs in the
+          <code>MODULE</code>. Any programs that call sub-programs within the
+          library can then import their interfaces by using the
+          <code>MODULE</code>.</p>
+
+          <p>For example, if we have a <samp>MODULE my_i_mod</samp>:</p>
+          <pre>
+MODULE my_i_mod
+! Some declarations
+INCLUDE 'sub_prog.interface'
+! More declarations
+END MODULE my_i_mod
+</pre>
+
+          <p>Its <code>PUBLIC</code> procedures can be imported as such:</p>
+          <pre>
+SUBROUTINE ham(some, arguments)
+USE my_i_mod, ONLY: sub_prog
+! Some declarations ...
+! Some executable statements ...
+CALL sub_prog(some, arguments)
+! More executable statements ...
+END SUBROUTINE ham
+</pre>
+        </dd>
+      </dl>
+
+      <p>FCM also supports the use of a <code>! DEPENDS ON</code> directive for
+      users to specify a dependency from within a source file. This feature is
+      documented in the <a href=
+      "../user_guide/build.html#advanced_dependency">Further dependency
+      features</a> sub-section of the <a href="../user_guide/">FCM user
+      guide</a>. However, it is worth noting that this method is only included
+      in FCM to support legacy code. It is not a feature recommended for new
+      code, and its use should be gradually phased out from existing code.</p>
+    </li>
+
+    <li>Arguments and local variables should be declared in different
+    statements. It makes your declaration clearer, and it is friendlier to the
+    interface file generator.
+
+      <dl>
+        <dt>Common practice</dt>
+
+        <dd>
+          <pre>
+SUBROUTINE foo(a, b, c)
+INTEGER :: a, b, c, i, j, k
+! ...
+END SUBROUTINE foo
+</pre>
+        </dd>
+
+        <dt>Better approach</dt>
+
+        <dd>
+          <pre>
+SUBROUTINE foo(a, b, c)
+INTEGER :: a, b, c
+INTEGER :: i, j, k
+! ...
+END SUBROUTINE foo
+</pre>
+        </dd>
+      </dl>
+    </li>
+
+    <li>Use the <code>ONLY</code> clause in a <code>USE &lt;module&gt;</code>
+    statement to declare all imported symbols (i.e. parameters, variables,
+    functions, subroutines, etc). 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>. It is also friendlier to the
+    compiler and the interface file generator, as they will not have to import
+    modules and symbols that are unnecessary.</li>
+
+    <li>In its default settings, FCM recognises the following file extensions
+    as Fortran free format source files:
+
+      <ul>
+        <li><samp>*.f90, *.f95</samp>: regular Fortran free format source
+        files</li>
+
+        <li><samp>*.F90, *.F95</samp>: Fortran free format source files that
+        require pre-processing</li>
+
+        <li><samp>*.inc</samp>: Include files that can be added to a regular
+        Fortran free format source file with a Fortran <code>INCLUDE</code>
+        statement</li>
+      </ul>
+    </li>
+  </ol>
+
+  <h3 id="fcm-cpp">2.2 Use of C pre-processor with Fortran</h3>
+
+  <p>We do not recommend the use of C pre-processor with Fortran. However, it
+  is acknowledged that there are some situations when it is necessary to
+  pre-process Fortran code. FCM supports pre-processing in two ways.
+  Pre-processing can be left to the compiler or it can be done in a separate
+  early stage of the build process. A separate pre-process stage can be useful
+  if pre-processing changes any of the following in a program unit:</p>
+
+  <ul>
+    <li>its name</li>
+
+    <li>its calling interface</li>
+
+    <li>its dependencies</li>
+  </ul>
+
+  <p>However, using a separate pre-process stage is not the best way of
+  working, as it adds an overhead to the build process. If your code requires
+  pre-processing, you should try to design it to avoid changes in the
+  above.</p>
+
+  <p>In practice, the only reasonable use of C pre-processor with Fortran is
+  for code selection. For example, pre-processing 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 pre-processor to select the correct version for your build.</p>
+
+  <p>Avoid using the C pre-processor for code inclusion, as you should be able
+  to do the same via the Fortran <code>INCLUDE</code> statement. You should
+  also avoid embedding pre-processor macros within the continuations of a
+  Fortran statement, as it can make your code very confusing.</p>
+
+  <h2 id="fortran">3. Programming Fortran in general</h2>
+
+  <p>The guidelines in this section are recommended practices for programming
+  Fortran in general. These are guidelines you should try to adhere to when you
+  are developing new code. If you are modifying existing code, you should
+  adhere to its existing standard and style where possible. If you want to
+  change its standard and style, you should seek prior agreements with the
+  owner and the usual developers of the code. Where possible, you should try to
+  maintain the same layout and style within a source file, and preferably,
+  within all the source code in a particular project.</p>
+
+  <p>When reading these guidelines, it is assumed that you already have a good
+  understanding of modern Fortran terminology. It is understood that these
+  guidelines may not cover every aspect of your work. In such cases, you will
+  be a winner if you use a bit of common sense, and always bearing in mind that
+  some other people may have to maintain the code in the future.</p>
+
+  <p>Always test your code before releasing it. Do not ignore compiler
+  warnings, as they may point you to potential problems.</p>
+
+  <h3 id="fortran-layout">3.1 Layout and formatting</h3>
+
+  <p>The following is a list of recommended practices for layout and formatting
+  when you write code in Fortran.</p>
+
+  <ul>
+    <li>Indent blocks with space characters. The use of 2 spaces per
+    indentation level is wide spread at the Met Office. The use of 4 spaces per
+    indentation level is most popular in the open source community because it
+    is easier for the human eyes. If you are modifying existing scripts, you
+    should stick to their existing styles. Otherwise, the use of 2 and 4 spaces
+    per indentation level are both acceptable, as long as it is consistent
+    within a source file. Where possible, comments should be indented with the
+    code within a block.</li>
+
+    <li>Use space and blank lines where appropriate to format your code to
+    improve readability. (Use genuine spaces but avoid using tabs, as the <kbd>
+      tab</kbd> character is not in the Fortran character set.) In the
+      following example, the code on the right hand side is preferred:
+
+      <dl>
+        <dt>Common practice</dt>
+
+        <dd>
+          <pre>
+DO i=1,n
+  a(i)%c=10*i/n
+  b(i)%d=20+i
+ENDDO
+IF(this==that)THEN
+  distance=0
+  time=0
+ENDIF
+</pre>
+        </dd>
+
+        <dt>Better approach</dt>
+
+        <dd>
+          <pre>
+DO i = 1, n
+  a(i)%c = 10 * i / n
+  b(i)%d = 20 + i
+END DO
+
+IF (this == that) THEN
+  distance = 0
+  time     = 0
+END IF
+</pre>
+        </dd>
+      </dl>
+    </li>
+
+    <li>Try to confine your line width to 80 characters, so that your code can
+    be printed easily on A4 paper.</li>
+
+    <li>Line up your statements, where appropriate, to improve readability. For
+    example:
+
+      <dl>
+        <dt>Common practice</dt>
+
+        <dd>
+          <pre>
+REAL, INTENT(OUT) :: my_out(:)
+REAL, INTENT(INOUT) :: my_inout(:)
+REAL, INTENT(IN) :: my_in(:)
+! ...
+CHARACTER(LEN=256) :: my_char
+! ...
+my_char = 'This is a very very very very very very very very very very ' // &amp;
+   'very very very very very very very very very very very very ' // &amp;
+   'long character assignment'
+</pre>
+        </dd>
+
+        <dt>Better approach</dt>
+
+        <dd>
+          <pre>
+REAL, INTENT(  OUT) ::   my_out(:)
+REAL, INTENT(INOUT) :: my_inout(:)
+REAL, INTENT(IN   ) ::    my_in(:)
+! ...
+CHARACTER(LEN=256) :: my_char
+! ...
+my_char                                                                &amp;
+    =  'This is a very very very very very very very very very very '  &amp;
+    // 'very very very very very very very very very very very very '  &amp;
+    // 'long character assignment'
+</pre>
+        </dd>
+      </dl>
+    </li>
+
+    <li>Short and simple Fortran statements are easier to read and understand
+    than long and complex ones. Where possible, avoid using continuation lines
+    in a statement.</li>
+
+    <li>Avoid putting multiple statements on the same line. It is not good for
+    readability.</li>
+  </ul>
+
+  <h3 id="fortran-style">3.2 Style</h3>
+
+  <p>The following is a list of recommended styles when you write code in
+  Fortran.</p>
+
+  <ul>
+    <li>New code should be written using Fortran 95 syntax. Avoid unportable
+    vendor/compiler extensions. Avoid Fortran 2003 features for the moment, as
+    they will not become widely available in the near future. (Having said
+    that, there is no harm in designing your code with the future in mind. For
+    example, if there is a feature that is not in Fortran 95 and you know that
+    it is in Fortran 2003, you may want to write your Fortran 95 code to make
+    it easier for the future upgrade.)</li>
+
+    <li>Write your program in UK English, unless you have a very good reason
+    for not doing so. Write your comments in simple UK English and name your
+    program units and variables based on sensible UK English words, bearing in
+    mind that your code may be read by people who are not proficient English
+    speakers.</li>
+
+    <li>When naming your variables and program units, always bear in mind that
+    Fortran is a case-insensitive language. (E.g. <var>EditOrExit</var> is the
+    same as <var>EditorExit</var>.)</li>
+
+    <li>Use only characters in the Fortran character set. In particular, accent
+    characters and tabs are not allowed in code, although they are usually OK
+    in comments. If your editor inserts tabs automatically, you should
+    configure it to switch off the functionality when you are editing Fortran
+    source files.</li>
+
+    <li>Although Fortran has no reserved keywords, you should avoid naming your
+    program units and variables with names that match an intrinsic
+    <code>FUNCTION</code> or <code>SUBROUTINE</code>. Similarly, you should
+    avoid naming your program units and variables with names that match a
+    <em>keyword</em> in a Fortran statement.</li>
+
+    <li>Be generous with comments, but avoid repeating the Fortran logic in
+    words. State the reason for doing something instead.</li>
+
+    <li>To improve readability, write your program in mainly lower case
+    characters. Writing a program in mainly lower case also means that you will
+    not have to use the <kbd>Shift/Caps Lock</kbd> keys often, hence, improving
+    your code's accessibility. There is a lot of debate on using upper/lower
+    cases in a case insensitive language such as Fortran. There is no right or
+    wrong, but people have adopted the different approaches over time, each has
+    its own merit. If you are starting a new project, you should choose a
+    suitable option and stick to it. Otherwise, you should stick with the style
+    in the existing code. Some options are listed here:
+
+      <ul>
+        <li>The ALL CAPS Fortran keywords approach, like most of the examples
+        in this document, where all Fortran keywords and intrinsic procedures
+        are written in ALL CAPS. This approach has the advantage that Fortran
+        keywords stand out, but it does increase how often the Shift/Caps Lock
+        key is used. Programmers who are used to some other programming
+        languages may also find it difficult to read a program with a lot of
+        upper case characters.</li>
+
+        <li>The Title Case Fortran keywords approach, where all Fortran
+        keywords are written with an initial capital case letter.</li>
+
+        <li>The sentence case approach, where only the initial character in a
+        Fortran statements is written in capital case letter, like a normal
+        sentence.</li>
+
+        <li>The all lower case approach, where all Fortran keywords are written
+        in lower case letters.</li>
+
+        <li>Some people have also proposed a variable naming convention where
+        local variables start with an initial lower case letter, private module
+        level variables with an initial capital case letter and public module
+        variables written in all caps. However, this approach has been seen by
+        many as too restrictive, and so its use has not been widely
+        spread.</li>
+      </ul>
+    </li>
+
+    <li>Use the new and clearer syntax for <code>LOGICAL</code> comparisons,
+    i.e.:
+
+      <ul>
+        <li><code>==</code> instead of <code>.EQ.</code></li>
+
+        <li><code>/=</code> instead of <code>.NE.</code></li>
+
+        <li><code>&gt;</code> instead of <code>.GT.</code></li>
+
+        <li><code>&lt;</code> instead of <code>.LT.</code></li>
+
+        <li><code>&gt;=</code> instead of <code>.GE.</code></li>
+
+        <li><code>&lt;=</code> instead of <code>.LE.</code></li>
+      </ul>
+    </li>
+
+    <li>Where appropriate, simplify your <code>LOGICAL</code> assignments, for
+    example:
+
+      <dl>
+        <dt>Common practice</dt>
+
+        <dd>
+          <pre>
+IF (my_var == some_value) THEN
+  something      = .TRUE.
+  something_else = .FALSE.
+ELSE
+  something      = .FALSE.
+  something_else = .TRUE.
+END IF
+! ...
+IF (something .EQV. .TRUE.) THEN
+  CALL do_something()
+  ! ...
+END IF
+</pre>
+        </dd>
+
+        <dt>Better approach</dt>
+
+        <dd>
+          <pre>
+something      = (my_var == some_value)
+something_else = (my_var /= some_value)
+! ...
+IF (something) THEN
+  CALL do_something()
+  ! ...
+END IF
+</pre>
+        </dd>
+      </dl>
+    </li>
+
+    <li>Positive logic is usually easier to understand. When you have an <code>
+      IF</code>-<code>ELSE</code>-<code>END IF</code> construct, you should use
+      positive logic in the <code>IF</code> test, provided that the positive
+      and the negative blocks are about the same size. (However, it may be more
+      appropriate to use negative logic if the negative block is significantly
+      bigger than the positive block.) For example:
+
+      <dl>
+        <dt>Common practice</dt>
+
+        <dd>
+          <pre>
+IF (my_var != some_value) THEN
+  CALL do_this()
+ELSE
+  CALL do_that()
+END IF
+</pre>
+        </dd>
+
+        <dt>Better approach</dt>
+
+        <dd>
+          <pre>
+IF (my_var == some_value) THEN
+  CALL do_that()
+ELSE
+  CALL do_this()
+END IF
+</pre>
+        </dd>
+      </dl>
+    </li>
+
+    <li>To improve readability, you should always use the optional space to
+    separate the following Fortran keywords:
+      <pre>
+ELSE IF         END DO          END FORALL      END FUNCTION
+END IF          END INTERFACE   END MODULE      END PROGRAM
+END SELECT      END SUBROUTINE  END TYPE        END WHERE
+SELECT CASE
+</pre>
+    </li>
+
+    <li>If you have a large or complex code block embedding other code blocks,
+    you may consider naming some or all of them to improve readability.</li>
+
+    <li>If you have a large or complex interface block or if you have one or
+    more sub-program units in the <code>CONTAINS</code> section, you can
+    improve readability by using the full version of the <code>END</code>
+    statement (i.e. <code>END SUBROUTINE</code> &lt;name&gt; or <code>END
+    FUNCTION</code> &lt;name&gt; instead of just <code>END</code>) at the end
+    of each sub-program unit. For readability in general, the full version of
+    the <code>END</code> statement is recommended over the simple
+    <code>END</code>.</li>
+
+    <li>Where possible, consider using <code>CYCLE</code>, <code>EXIT</code> or
+    a <code>WHERE</code>-construct to simplify complicated
+    <code>DO</code>-loops.</li>
+
+    <li>When writing a <code>REAL</code> literal with an integer value, put a
+    <code>0</code> after the decimal point (i.e. <samp>1.0</samp> as opposed to
+    <samp>1.</samp>) to improve readability.</li>
+
+    <li>Where reasonable and sensible to do so, you should try to match the
+    names of dummy and actual arguments to a
+    <code>SUBROUTINE</code>/<code>FUNCTION</code>.</li>
+
+    <li>In an array assignment, it is recommended that you use array notations
+    to improve readability. E.g.:
+
+      <dl>
+        <dt>Common practice</dt>
+
+        <dd>
+          <pre>
+INTEGER :: array1(10, 20), array2(10, 20)
+INTEGER :: scalar
+! ...
+array1 = 1
+array2 = array1 * scalar
+</pre>
+        </dd>
+
+        <dt>Better approach</dt>
+
+        <dd>
+          <pre>
+INTEGER :: array1(10, 20), array2(10, 20)
+INTEGER :: scalar
+! ...
+array1(:, :) = 1
+array2(:, :) = array1(:, :) * scalar
+</pre>
+        </dd>
+      </dl>
+    </li>
+
+    <li>Where appropriate, use parentheses to improve readability. E.g.:
+    <code>a = (b * i) + (c / n)</code> is easier to read than <code>a = b * i +
+    c / n</code>.</li>
+  </ul>
+
+  <h3 id="fortran-feature">3.3 Fortran features</h3>
+
+  <p>The following is a list of Fortran features that you should use or
+  avoid.</p>
+
+  <ul>
+    <li>Use <code>IMPLICIT NONE</code> in all program units. It means that you
+    have declare all your variables explicitly. This helps to reduce bugs in
+    your program that will otherwise be difficult to track.</li>
+
+    <li>Design your derived data types carefully and use them to group related
+    variables. Appropriate use of derived data types will allow you to design
+    modules and procedures with simpler and cleaner interfaces.</li>
+
+    <li>Where possible, module variables and procedures should be declared
+    <code>PRIVATE</code>. This avoids unnecessary export of symbols, promotes
+    data hiding and may also help the compiler to optimise the code.</li>
+
+    <li>When you are passing an array argument to a
+    <code>SUBROUTINE</code>/<code>FUNCTION</code>, and the
+    <code>SUBROUTINE</code>/<code>FUNCTION</code> does not change the
+    <code>SIZE</code>/<code>DIMENSION</code> of the array, you should pass it
+    as an assumed shape array. Memory management of such an array is
+    automatically handled by the <code>SUBROUTINE</code>/<code>FUNCTION</code>,
+    and you do not have to worry about having to <code>ALLOCATE</code> or
+    <code>DEALLOCATE</code> your array. It also helps the compiler to optimise
+    the code.</li>
+
+    <li>Use an array <code>POINTER</code> when you are passing an array
+    argument to a <code>SUBROUTINE</code>, and the <code>SUBROUTINE</code> has
+    to alter the <code>SIZE</code>/<code>DIMENSION</code> of the array. You
+    should also use an array <code>POINTER</code> when you need a dynamic array
+    in a component of a derived data type. (Note: Fortran 2003 allows passing
+    <code>ALLOCATABLE</code> arrays as arguments as well as using
+    <code>ALLOCATABLE</code> arrays as components of a derived data type.
+    Therefore, the need for using an array <code>POINTER</code> should be
+    reduced once Fortran 2003 becomes more widely accepted.)</li>
+
+    <li>Where possible, an <code>ALLOCATE</code> statement for an
+    <code>ALLOCATABLE</code> array (or a <code>POINTER</code> used as a dynamic
+    array) should be coupled with a <code>DEALLOCATE</code> within the same
+    scope. If an <code>ALLOCATABLE</code> array is a <code>PUBLIC</code>
+    <code>MODULE</code> variable, it is highly desirable if its memory
+    allocation and deallocation are only performed in procedures within the
+    <code>MODULE</code> in which it is declared. You may consider writing
+    specific <code>SUBROUTINE</code>s within the <code>MODULE</code> to handle
+    these memory managements.</li>
+
+    <li>Always define a <code>POINTER</code> before using it. You can define a
+    <code>POINTER</code> in its declaration by pointing it to the intrinsic
+    function <code>NULL()</code>. Alternatively, you can make sure that your
+    <code>POINTER</code> is defined or nullified early on in the program unit.
+    Similarly, <code>NULLIFY</code> a <code>POINTER</code> when it is no longer
+    in use, either by using the <code>NULLIFY</code> statement or by pointing
+    your <code>POINTER</code> to <code>NULL()</code>.</li>
+
+    <li>Avoid the <code>DIMENSION</code> attribute or statement. Declare the
+    <code>DIMENSION</code> with the declared variables. E.g.:
+
+      <dl>
+        <dt>Common practice</dt>
+
+        <dd>
+          <pre>
+INTEGER, DIMENSION(10) :: array1
+INTEGER                :: array2
+DIMENSION              :: array2(20)
+</pre>
+        </dd>
+
+        <dt>Better approach</dt>
+
+        <dd>
+          <pre>
+INTEGER :: array1(10), array2(20)
+</pre>
+        </dd>
+      </dl>
+    </li>
+
+    <li>Avoid <code>COMMON</code> blocks and <code>BLOCK DATA</code> program
+    units. Use <code>PUBLIC</code> <code>MODULE</code> variables.</li>
+
+    <li>Avoid the <code>EQUIVALENCE</code> statament. Use a
+    <code>POINTER</code> or a derived data type, and the <code>TRANSFER</code>
+    intrinsic function to convert between types.</li>
+
+    <li>Avoid the <code>PAUSE</code> statement, as your program will hang in a
+    batch environment. If you need to halt your program for interactive use,
+    consider using a <code>READ*</code> statement instead.</li>
+
+    <li>Avoid the <code>ENTRY</code> statement. Use a <code>MODULE</code> or
+    internal <code>SUBROUTINE</code>.</li>
+
+    <li>Avoid the <code>GOTO</code> statement. The only commonly acceptable
+    usage of <code>GOTO</code> is for error trapping. In such case, the jump
+    should be to a commented <code>9999 CONTINUE</code> statement near the end
+    of the program unit. Typically, you will only find error handlers beyond
+    the <code>9999 CONTINUE</code> statement.</li>
+
+    <li>Avoid assigned <code>GOTO</code>, computed <code>GOTO</code>,
+    arithmetic <code>IF</code>, etc. Use the appropriate modern constructs such
+    as <code>IF</code>, <code>WHERE</code>, <code>SELECT CASE</code>,
+    etc..</li>
+
+    <li>Avoid numbered statement labels. <code>DO ... label CONTINUE</code>
+    constructs should be replaced by <code>DO ... END DO</code> constructs.
+    <code>FORMAT</code> statements should be replaced by format strings. (Tip:
+    a format string can be a <code>CHARACTER</code> variable.)</li>
+
+    <li>Avoid the <code>FORALL</code> statement/construct. Despite what it is
+    supposed to do, <code>FORALL</code> is often difficult for compilers to
+    optimise. (See, for example, <a href=
+    "http://www.fortran.bcs.org/2007/jubilee/f50.pdf">Implementing the
+    Standards including Fortran 2003</a> by <acronym title=
+    "Numerical Algorithms Group">NAG</acronym>.) Stick to the equivalent
+    <code>DO</code> construct, <code>WHERE</code> statement/construct or array
+    assignments unless there are actual performance benefits using
+    <code>FORALL</code> in your code.</li>
+
+    <li>A <code>FUNCTION</code> should be <code>PURE</code>, i.e. it should
+    have no side effects (e.g. altering an argument or module variable, or
+    performing I/O). If you need to perform a task with side effects, you
+    should use a <code>SUBROUTINE</code> instead.</li>
+
+    <li>Avoid using a statement <code>FUNCTION</code>. Use an internal
+    <code>FUNCTION</code> instead.</li>
+
+    <li>Avoid <code>RECURSIVE</code> procedures if possible.
+    <code>RECURSIVE</code> procedures are usually difficult to understand, and
+    are always difficult to optimise in a supercomputer environment.</li>
+
+    <li>Avoid using the specific names of intrinsic procedures. Use the generic
+    names of intrinsic procedures where possible.</li>
+  </ul>
+
+  <h2 id="template">4. Program templates</h2>
+
+  <p>The following is a basic template for a <code>FUNCTION</code>:</p>
+  <pre>
+[&lt;type&gt;] FUNCTION &lt;name&gt;(&lt;args, ...&gt;) [RESULT(&lt;result&gt;)]
+
+!-------------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! For further details please refer to the file COPYRIGHT.txt
+! which you should have received as part of this distribution.
+!-------------------------------------------------------------------------------
+! DESCRIPTION
+!   &lt;Explain what the function does.&gt;
+!
+!-------------------------------------------------------------------------------
+USE &lt;module&gt;, ONLY : &lt;symbols, ...&gt;
+
+IMPLICIT NONE
+
+! Function arguments:
+&lt;arguments with INTENT(IN) ...&gt;
+
+! Function result:
+&lt;declare the type returned by the function&gt;
+
+! Local declarations:
+&lt;parameters, derived data types, variables, ...&gt;
+
+! INTERFACE blocks
+&lt;INCLUDE interface files...&gt;
+&lt;other interface blocks...&gt;
+
+&lt;other specification statements ...&gt;
+!-------------------------------------------------------------------------------
+&lt;executable statements ...&gt;
+!-------------------------------------------------------------------------------
+CONTAINS
+  &lt;sub-programs&gt;
+END FUNCTION &lt;name&gt;
+</pre>
+
+  <p>The basic templates for other types of program units are similar to that
+  of a <code>FUNCTION</code>, with the following exceptions:</p>
+
+  <ul>
+    <li>A <code>PROGRAM</code> does not have arguments, so the arguments list
+    in the header and the <cite>Arguments</cite> section in the declaration
+    section should be removed. All declarations are local to a
+    <code>PROGRAM</code>, so the <cite>Local Declarations</cite> section should
+    be replaced by a simple <em>Declarations</em> section.</li>
+
+    <li>A <code>SUBROUTINE</code> may have <code>INTENT(OUT)</code> and
+    <code>INTENT(INOUT)</code> arguments.</li>
+
+    <li>A <code>MODULE</code> does not have arguments, so the arguments list in
+    the header and the <cite>Arguments</cite> section in the declaration
+    section should be removed. Where appropriate, the <cite>Local
+    Declarations</cite> section should be replaced by a <em>PUBLIC
+    declarations</em> section and a <em>PRIVATE declarations</em> section.</li>
+  </ul>
+
+  <p>When you are distributing your code, you should include a
+  <samp>COPYRIGHT.txt</samp> file at a top level directory in your source tree.
+  The file should contain the detailed copyright information:</p>
+
+  <ul>
+    <li>the copyright year, ranging from the year the code is first distributed
+    to the year the code is last distributed</li>
+
+    <li>the copyright statement</li>
+
+    <li>the owner of the code and his/her address</li>
+  </ul>
+
+  <p>For example:</p>
+  <pre>
+!------------------------------------------------------------------------------!
+!                                                                              !
+! (C) Crown copyright 2005-6 Met Office. All rights reserved.                  !
+!                                                                              !
+! Use, duplication or disclosure of this code is subject to the restrictions   !
+! as set forth in the contract. If no contract has been raised with this copy  !
+! of the code, the use, duplication or disclosure of it is strictly            !
+! prohibited. Permission to do so must first be obtained in writing from the   !
+! Head of Numerical Modelling at the following address:                        !
+!                                                                              !
+! Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB, United Kingdom             !
+!                                                                              !
+!------------------------------------------------------------------------------!
+</pre>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/standards/perl_standard.html
===================================================================
--- vendors/fcm/1-5/doc/standards/perl_standard.html	(revision 1981)
+++ vendors/fcm/1-5/doc/standards/perl_standard.html	(revision 1981)
@@ -0,0 +1,215 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>Perl coding standard for FCM</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="Perl coding standard for FCM" />
+  <meta name="keywords" content="Perl, coding standard, FCM" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload="javascript: FCM.load('doc/standards/', 'perl-standard');">
+  <div id="ukmo-logo">
+    <img src="logo.png" alt="UK Met Office" />
+  </div>
+
+  <h1>Perl coding standard for FCM</h1>
+
+  <address>
+    Latest content update: 22 January 2010. <span id=
+    "fcm-js-maintenance"></span>
+  </address>
+
+  <address>
+    Questions regarding this document or permissions to quote from it should be
+    directed to:
+  </address>
+
+  <address>
+    <a href="mailto:iprmanager@metoffice.gov.uk">IPR Manager</a><br />
+    Met Office<br />
+    FitzRoy Road<br />
+    Exeter, Devon<br />
+    EX1 3PB<br />
+    United Kingdom
+  </address>
+
+  <address>
+    &copy; Crown Copyright 2005-10
+  </address>
+
+  <address id="fcm-js-pdf"></address>
+  <hr />
+
+  <div id="fcm-content"></div>
+
+  <h2 id="introduction">1. Introduction</h2>
+
+  <p>Perl (Practical Extraction and Report Language) is the language we use to
+  implement the in-house components of the FCM system. This document contains
+  some basic guidelines that are followed by the Perl code in FCM.</p>
+
+  <h2 id="style">2. Style</h2>
+
+  <p>There are many Perl style guides available online. Some are listed
+  below:</p>
+
+  <ul>
+    <li><a href="http://perldoc.perl.org/perlstyle.html">perlstyle</a></li>
+
+    <li><a href=
+    "http://perldoc.perl.org/perlmodstyle.html">perlmodstyle</a></li>
+
+    <li><a href=
+    "http://www.kulnet.kuleuven.ac.be/perlcourse/perlingo.html">Perlingo</a></li>
+
+    <li><a href="http://perltidy.sourceforge.net/">Perltidy</a></li>
+
+    <li><a href=
+    "http://perl.apache.org/docs/2.0/devel/core/coding_style.html">mod_perl
+    Coding Style Guide</a></li>
+
+    <li>Damian Conway's <a href=
+    "http://books.google.co.uk/books?id=gJf9tI2mytIC&amp;dq=perl+best+practices&amp;printsec=frontcover&amp;source=bn&amp;hl=en&amp;ei=rnFZS7DPB4j80wTxi-T9BA&amp;sa=X&amp;oi=book_result&amp;ct=result&amp;resnum=4&amp;ved=0CBQQ6AEwAw#v=onepage&amp;q=&amp;f=false">
+    Perl Best Practices</a> at Google Books</li>
+  </ul>
+
+  <p>The <cite>Perl Best Practices</cite> book is the most comprehensive of
+  all. In FCM, we have begun to write new modules to follow its advice.
+  However, we recognise that it may not be possible to strictly follow every
+  detail, as Perl is a language designed to do things in different ways. The
+  most important point is that the style within a source file should be
+  consistent and easy to follow, so the next person who reads it will not have
+  to struggle with it. It is also worth nothing that many older modules in the
+  FCM source tree are still written to an old style which was derived before we
+  found out about this book.</p>
+
+  <h2 id="boilerplates">3. Boilerplates</h2>
+
+  <p>A typical Perl executable in FCM looks like:</p>
+  <pre>
+#!/usr/bin/env perl
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local(@ARGV) = @_;
+    # ...
+}
+
+1;
+__END__
+
+=head1 NAME
+
+fcm-example
+
+=head1 SYNOPSIS
+
+    fcm-example [OPTIONS] [ARGS]
+
+=head1 OPTIONS
+
+...
+
+=head1 ARGUMENTS
+
+...
+
+=head1 DESCRIPTION
+
+An example boiler plate for an FCM Perl executable.
+
+=head1 DIAGNOSTICS
+
+...
+
+=head1 CONFIGURATION AND ENVIRONMENT
+
+...
+
+=head1 COPYRIGHT
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
+</pre>
+
+  <p>A typical Perl module in FCM looks like:</p>
+  <pre>
+#-------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+#-------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+package FCM::Example;
+
+# For example...
+sub example {
+    my ($message) = @_;
+    sprintf('E.g. %s.', $message);
+}
+
+#-------------------------------------------------------------------------------
+1;
+__END__
+
+=head1 NAME
+
+FCM::Example
+
+=head1 SYNOPSIS
+
+    use FCM::Example;
+    # ... do something with it
+
+=head1 DESCRIPTION
+
+This class/module does ...
+
+=head1 METHODS
+
+... This section should be renamed FUNCTIONS for non-OO modules.
+
+=head1 DIAGNOSTICS
+
+...
+
+=head1 CONFIGURATION AND ENVIRONMENT
+
+...
+
+=head1 COPYRIGHT
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
+</pre>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/standards/style.css
===================================================================
--- vendors/fcm/1-5/doc/standards/style.css	(revision 1981)
+++ vendors/fcm/1-5/doc/standards/style.css	(revision 1981)
@@ -0,0 +1,260 @@
+/**********************************************************************
+ * CSS for FCM documents.
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+a:visited
+{
+    color: purple;
+}
+
+a:active,
+a:hover
+{
+    color: blue;
+}
+
+code,
+kbd,
+var
+{
+    color: navy;
+}
+
+
+/**********************************************************************
+ * BACKGROUND COLOUR
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+code,
+kbd,
+pre,
+samp,
+.image
+{
+    background: #eee;
+}
+
+
+/**********************************************************************
+ * BORDER
+ *
+ **********************************************************************/
+dl,
+kbd,
+pre,
+samp,
+.image
+{
+    border: thin solid #ddd;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: sans-serif;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1
+{
+    font-size: 200%;
+}
+
+h2
+{
+    font-size: 175%;
+}
+
+h3
+{
+    font-size: 150%;
+}
+
+h4,
+h5
+{
+    font-size: medium;
+}
+
+address
+{
+    font-size: small;
+}
+
+address,
+var
+{
+    font-style: normal;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+#fcm-content ul li
+{
+    list-style: none;
+}
+
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT DECORATION
+ *
+ **********************************************************************/
+a:link
+{
+    text-decoration: underline;
+}
+
+a:visited
+{
+    text-decoration: none;
+}
+
+a:active,
+a:hover
+{
+    text-decoration: underline;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+#ukmo-logo,
+#document-info address
+{
+    text-align: right;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+dl,
+pre,
+.image
+{
+    border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+}
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/standards/style.html2ps.css
===================================================================
--- vendors/fcm/1-5/doc/standards/style.html2ps.css	(revision 1981)
+++ vendors/fcm/1-5/doc/standards/style.html2ps.css	(revision 1981)
@@ -0,0 +1,137 @@
+/**********************************************************************
+ * CSS for html2ps for FCM documents.
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: Helvetica;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+@page
+{
+    margin: 2cm;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/user_guide/annex_bld_cfg.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/annex_bld_cfg.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/annex_bld_cfg.html	(revision 1981)
@@ -0,0 +1,899 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>Annex: Declarations in FCM build configuration file</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide Annex" />
+  <meta name="keywords" content="FCM, user guide, annex" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Annex: Declarations in FCM build configuration file</h1>
+
+  <p>The following is a list of supported declarations for the configuration
+  file used by the FCM build system. Unless otherwise stated, the fields in all
+  declaration labels are not case sensitive. Build declarations can be made
+  either in a build configuration file or in an extract configuration file. In
+  the latter case, the prefix <code>BLD::</code> must be added at the beginning
+  of each label to inform the extract system that the declaration is a build
+  system declaration. (In a build configuration file, the prefix
+  <code>BLD::</code> is optional.)</p>
+
+  <dl>
+    <dt>CFG::TYPE</dt>
+
+    <dd>
+      <p>The configuration file type, the value should always be
+      <samp>bld</samp> for a build configuration file. This declaration is
+      compulsory for all build configuration files. (This declaration is
+      automatic when the extract system creates a build configuration
+      file.)</p>
+
+      <p>Example:</p>
+      <pre>
+cfg::type  bld
+</pre>
+    </dd>
+
+    <dt>CFG::VERSION</dt>
+
+    <dd>
+      <p>The file format version, currently <samp>1.0</samp> - a version is
+      included so that we shall be able to read the configuration file
+      correctly should we decide to change its format in the future. (This
+      declaration is automatic when the extract system creates a build
+      configuration file.)</p>
+
+      <p>Example:</p>
+      <pre>
+cfg::version  1.0
+</pre>
+    </dd>
+
+    <dt>%&lt;name&gt;</dt>
+
+    <dd>
+      <p><code>%&lt;name&gt;</code> declares an internal variable
+      <var>&lt;name&gt;</var> that can later be re-used.</p>
+
+      <p>Example:</p>
+      <pre>
+%my_variable  -foo -bar
+tool::fflags  %my_variable
+tool::cflags  %my_variable
+</pre>
+    </dd>
+
+    <dt>INC</dt>
+
+    <dd>
+      <p>This declares the name of a file containing build configuration. The
+      lines in the declared file will be included inline to the current
+      configuration file.</p>
+
+      <p>Example:</p>
+      <pre>
+inc  ~frva/var_stable_22.0/cfg/bld.cfg
+# ... and then your changes ...
+</pre>
+    </dd>
+
+    <dt>
+      DEST[::ROOTDIR]<br />
+      <del>DIR::ROOT</del>
+    </dt>
+
+    <dd>
+      <p>The destination of the build. It must be declared for each build.
+      (This declaration is automatic when the extract system creates a build
+      configuration file. The value is normally the path of the extract
+      destination.)</p>
+
+      <p>Example:</p>
+      <pre>
+dest  $HOME/my_build
+</pre>
+    </dd>
+
+    <dt>USE</dt>
+
+    <dd>
+      <p>This inherits settings from a previous build. The value must be must
+      be either the configuration file or the root directory of a successful
+      build. Output of the build, the tools, the exclude dependency
+      declarations, the file type registers declarations are automatically
+      inherited from the declared build. Source directories and build targets
+      declarations may be inherited depending on the INHERIT declarations. (If
+      you have a USE declaration in an extract, the resulting build
+      configuration file will contain an automatic USE declaration, which
+      expects an inherited build at the extract destination.)</p>
+
+      <p>Example:</p>
+      <pre>
+# Use VAR build 22.0
+USE  ~frva/var_22.0
+</pre>
+    </dd>
+
+    <dt>INHERIT::&lt;name&gt;[::&lt;pcks&gt;]</dt>
+
+    <dd>
+      <p>This declares whether build targets (&lt;name&gt; =
+      <samp>target</samp>) or source directories (&lt;name&gt; =
+      <samp>src</samp>) can be inherited using the USE statement. By default,
+      source directories are inherited, while build targets are not. Use the
+      value <samp>true</samp> to switch on inheritance, or <samp>false</samp>
+      to switch off. For source directories declarations, the name of a
+      sub-package &lt;pcks&gt; can be specified. If a sub-package pcks is
+      specified, the declaration applies only to the files and directories
+      under the sub-package. Otherwise, the declaration applies globally.</p>
+
+      <p>Example:</p>
+      <pre>
+inherit::target   true
+inherit::src      false
+</pre>
+    </dd>
+
+    <dt>SRC[::&lt;pcks&gt;]</dt>
+
+    <dd>
+      <p>This declares a source file/directory. You must specify the
+      sub-package &lt;pcks&gt; if the source file/directory is located outside
+      of the <samp>src/</samp> sub-directory of the build destination or if you
+      want to redefine the sub-package name of the source file/directory. The
+      name of the sub-package &lt;pcks&gt; must be unique. Package names are
+      delimited by double colons <code>::</code> or double underscores
+      <code>__</code>. If you declare a relative path, it is assumed to be
+      relative to the <samp>src/</samp> sub-directory of the build destination.
+      (This declaration is automatic when the extract system creates the build
+      configuration file. The list of declared source directories will be the
+      list of extracted source directories.)</p>
+
+      <p>Example:</p>
+      <pre>
+src::var/code/VarMod_PF  $HOME/var/src/code/VarMod_PF
+</pre>
+    </dd>
+
+    <dt>SEARCH_SRC</dt>
+
+    <dd>
+      <p>This declares a flag to determine whether the build system should
+      search the <samp>src/</samp> sub-directory of the build root for a list
+      of source files. The automatic search is useful if the build system is
+      invoked standalone and the <samp>src/</samp> sub-directory contains the
+      full source tree of the build. The default is to search
+      (<samp>true</samp>). Set the flag to <samp>false</samp> to switch off the
+      behaviour. (When the extract system creates a build configuration file,
+      it declares all source files. Searching of the source sub-directory
+      should not be required, and so this flag is automatically set to
+      <samp>false</samp>.)</p>
+
+      <p>Example:</p>
+      <pre>
+search_src  false
+</pre>
+    </dd>
+
+    <dt>TARGET</dt>
+
+    <dd>
+      <p>Specify the targets for the build. Multiple targets can be declared in
+      one or more declarations. These targets become the dependencies of the
+      default <samp>all</samp> target in the <em>Makefile</em>. It is worth
+      noting that <code>TARGET</code> declarations are cumulative. A later
+      declaration does not override an earlier one - it simply adds more targets
+      to the list.</p>
+
+      <p>Example:</p>
+      <pre>
+target  VarScr_AnalysePF VarScr_CovAccStats
+target  VarScr_CovPFstats
+</pre>
+    </dd>
+
+    <dt>TOOL::&lt;label&gt;[::&lt;pcks&gt;]</dt>
+
+    <dd>
+      <p>This declaration is used to specify a build tool such as the Fortran
+      compiler or its flags. The &lt;label&gt; determines the tool you are
+      declaring. A TOOL declaration normally applies globally. However, where
+      it is sensible to do so, a sub-package &lt;pcks&gt; can be specified. In
+      which case, the declaration applies only to the files and directories
+      under the sub-package. A list of &lt;label&gt; fields is available
+      <a href="#tools-list">later in this annex</a>.</p>
+
+      <p>Example:</p>
+      <pre>
+tool::fc      sxmpif90
+tool::fflags  -Chopt -Pstack
+
+tool::cc      sxmpic++
+tool::cflags  -O nomsg -pvctl nomsg
+
+tool::ar      sxar
+</pre>
+    </dd>
+
+    <dt>EXE_DEP[::&lt;target&gt;]</dt>
+
+    <dd>
+      <p>This declares an extra dependency for either all main program targets
+      or only &lt;target&gt; if it is specified. If &lt;target&gt; is
+      specified, it must be the name of a main program target. The value of the
+      declaration is a space delimited list. Each item in the list can either
+      be a valid name of a sub-package or the name of a valid object target. If
+      a sub-package name is used, the <em>make</em> rule for the main program
+      will be set to depend on all (non-program) object files within the
+      sub-package.</p>
+
+      <p>Example:</p>
+      <pre>
+# Only foo.exe to depend on the package foo::bar and egg.o
+exe_dep::foo.exe  foo::bar egg.o
+
+# All executables to depend on the package foo::bar and egg.o
+exe_dep  foo::bar egg.o
+
+# Only foo.exe to depend on all objects
+exe_dep::foo.exe
+
+# All executables to depend on all objects
+exe_dep
+</pre>
+    </dd>
+
+    <dt>BLOCKDATA[::&lt;target&gt;]</dt>
+
+    <dd>
+      <p>This declares a BLOCKDATA dependency for either all main program
+      targets or only &lt;target&gt; if it is specified. If &lt;target&gt; is
+      specified, it must be the name of a main program target. The value of the
+      declaration is a space delimited list. Each item in the list must be the
+      name of a valid object target containing a Fortran BLOCKDATA program
+      unit.</p>
+
+      <p>Example:</p>
+      <pre>
+# Only foo.exe to depend on blkdata.o
+blockdata::foo.exe  blkdata.o
+
+# All executables to depend on fbd.o
+blockdata  fbd.o
+</pre>
+    </dd>
+
+    <dt>EXCL_DEP[::&lt;pcks&gt;]</dt>
+
+    <dd>
+      <p>This declaration is used to specify whether a particular dependency
+      should be ignored during the automatic dependency scan. If a sub-package
+      &lt;pcks&gt; is specified, the declaration applies only to the files and
+      directories under the sub-package. Otherwise, the declaration applies
+      globally. The value of this declaration must contain one or two fields
+      (separated by the double colon <code>::</code>). The first field denotes
+      the dependency type, and the second field is the dependency target. If
+      the second field is specified, it will only exclude the dependency to the
+      specified target. Otherwise, it will exclude all dependency to the
+      specified type. The following dependency types are supported:</p>
+
+      <dl id="dependency-types">
+        <dt>USE</dt>
+
+        <dd>The dependency target is a Fortran module.</dd>
+
+        <dt>INTERFACE</dt>
+
+        <dd>The dependency target is a Fortran 9X interface block file.</dd>
+
+        <dt>INC</dt>
+
+        <dd>The dependency target is a Fortran INCLUDE file.</dd>
+
+        <dt>H</dt>
+
+        <dd>The dependency target is a pre-processor #include header file.</dd>
+
+        <dt>OBJ</dt>
+
+        <dd>The dependency target is a compiled binary object file.</dd>
+
+        <dt>EXE</dt>
+
+        <dd>The dependency target is an executable binary or script.</dd>
+      </dl>
+
+      <p>N.B. The following dependency targets are in the default list of
+      excluded dependencies:</p>
+
+      <dl>
+        <dt>Intrinsic Fortran modules:</dt>
+
+        <dd>
+          <ul>
+            <li>USE::ISO_C_BINDING</li>
+
+            <li>USE::IEEE_EXCEPTIONS</li>
+
+            <li>USE::IEEE_ARITHMETIC</li>
+
+            <li>USE::IEEE_FEATURES</li>
+          </ul>
+        </dd>
+
+        <dt>Intrinsic Fortran subroutines:</dt>
+
+        <dd>
+          <ul>
+            <li>OBJ::CPU_TIME</li>
+
+            <li>OBJ::GET_COMMAND</li>
+
+            <li>OBJ::GET_COMMAND_ARGUMENT</li>
+
+            <li>OBJ::GET_ENVIRONMENT_VARIABLE</li>
+
+            <li>OBJ::MOVE_ALLOC</li>
+
+            <li>OBJ::MVBITS</li>
+
+            <li>OBJ::RANDOM_NUMBER</li>
+
+            <li>OBJ::RANDOM_SEED</li>
+
+            <li>OBJ::SYSTEM_CLOCK</li>
+          </ul>
+        </dd>
+
+        <dt>Dummy declarations:</dt>
+
+        <dd>
+          <ul>
+            <li>OBJ::NONE</li>
+
+            <li>EXE::NONE</li>
+          </ul>
+        </dd>
+      </dl>
+
+      <p>Example:</p>
+      <pre>
+excl_dep  USE::YourFortranMod
+excl_dep  INTERFACE::HerFortran.interface
+excl_dep  INC::HisFortranInc.inc
+excl_dep  H::TheirHeader.h
+excl_dep  OBJ
+excl_dep  EXE
+</pre>
+    </dd>
+
+    <dt>DEP::&lt;pcks&gt;</dt>
+
+    <dd>
+      <p>This declaration is used to specify a dependency for a source file in
+      &lt;pcks&gt;. The value of this declaration must contain two fields
+      (separated by the double colon <code>::</code>). The first field denotes
+      the dependency type, and the second field is the dependency target. The
+      dependency types are the same as those for EXCL_DEP described <a href=
+      "#dependency-types">above</a>.</p>
+
+      <p>Example:</p>
+      <pre>
+dep::foo/bar.f  USE::your_fortran_mod
+dep::foo/bar.f  INTERFACE::her_fortran.interface
+dep::foo/bar.f  INC::his_fortran_inc.inc
+dep::foo/bar.f  H::their_header.h
+dep::foo/bar.f  OBJ::its_object.o
+dep::foo/egg    EXE::ham
+</pre>
+    </dd>
+
+    <dt>NO_DEP::&lt;pcks&gt;</dt>
+
+    <dd>
+      <p>This declaration is used to switch off/on dependency checking. If
+      &lt;pcks&gt; is specified in the label, the declaration applies to the
+      specified sub-package only.</p>
+
+      <p>Example:</p>
+      <pre>
+# Switch on dependency checking only for "foo"
+no_dep      true
+no_dep::foo false
+</pre>
+    </dd>
+
+    <dt>EXE_NAME::&lt;name&gt;</dt>
+
+    <dd>
+      <p>This renames the executable target of a main program source file
+      &lt;name&gt; to the specified value.</p>
+
+      <p>Example:</p>
+      <pre>
+# Rename executable target of foo.f90 from "foo.exe" to "bar"
+exe_name::foo  bar
+</pre>
+    </dd>
+
+    <dt>LIB[::&lt;pcks&gt;]</dt>
+
+    <dd>
+      <p>This declares the name of a library archive target. If &lt;pcks&gt; is
+      specified in the label, the declaration applies to the library archive
+      target for that sub-package only. If set, the name of the library archive
+      target will be named <samp>lib&lt;value&gt;.a</samp>, where &lt;value&gt;
+      is the value of the declaration. If not specified, the default is to name
+      the global library <samp>libfcm_default.a</samp>. For a library archive
+      of a sub-package, the default is to name its library after the name of
+      the sub-package.</p>
+
+      <p>Example:</p>
+      <pre>
+# Rename the top level library "libfoo.a"
+lib  foo
+
+# Rename the library for the sub-package "egg::ham"
+# from "libegg__ham.a" to "libegg-ham.a"
+lib::egg/ham  egg-ham
+</pre>
+    </dd>
+
+    <dt>PP[::&lt;pcks&gt;]</dt>
+
+    <dd>
+      <p>This declares whether a pre-processing stage is required. To switch on
+      pre-processing, set the value to <samp>true</samp>. If &lt;pcks&gt; is
+      specified in the label, the flag applies to the files within that
+      sub-package only. Otherwise, the flag affects source directories in all
+      packages. The pre-processing stage is useful if the pre-processor changes
+      the dependency and/or the argument list of the source files. The default
+      behaviour is skip the pre-processing stage for all source.</p>
+
+      <p>Example:</p>
+      <pre>
+pp::gen true  # switch on pre-processing for "gen" only
+pp      true  # switch on pre-processing globally
+</pre>
+    </dd>
+
+    <dt>SRC_TYPE::&lt;pcks&gt;</dt>
+
+    <dd>
+      <p>This declaration is used to (re-)register the file type of the
+      sub-package &lt;pcks&gt; to associate with different file types. The
+      value of the declaration is a list of type flags delimited by the double
+      colon <code>::</code>. Each type flag is used internally to describe the
+      nature of the file. For example, a Fortran free source form containing a
+      main program is registered as
+      <code>FORTRAN::FORTRAN9X::SOURCE::PROGRAM</code>. A list of type flags is
+      available <a href="#infile-ext-types">later in this annex</a>.</p>
+
+      <p>Example:</p>
+      <pre>
+src_type::foo/bar.f  FORTRAN::FORTRAN9X::SOURCE::PROGRAM
+</pre>
+    </dd>
+
+    <dt>INFILE_EXT::&lt;ext&gt;</dt>
+
+    <dd>
+      <p>This declaration is used to re-register particular file name
+      extensions &lt;ext&gt; to associate with different file types. The value
+      of the declaration has a similar format to that of SRC_TYPE declaration
+      described above. A list of type flags is available <a href=
+      "#infile-ext-types">later in this annex</a>.</p>
+
+      <p>Example:</p>
+      <pre>
+infile_ext::h90  CPP::INCLUDE
+infile_ext::inc  FORTRAN::FORTRAN9X::INCLUDE
+</pre>
+    </dd>
+
+    <dt>OUTFILE_EXT::&lt;type&gt;</dt>
+
+    <dd>
+      <p>This declaration is used to re-register the output file extension for
+      a particular &lt;type&gt; of output files. The value must be a valid file
+      extension. The following is a list of output file types in-use by the
+      build system:</p>
+
+      <dl id="outfile-ext-types">
+        <dt>OBJ</dt>
+
+        <dd>compiled object files<br />
+        [default = .o]</dd>
+
+        <dt>MOD</dt>
+
+        <dd>compiled Fortran module information files<br />
+        [default = .mod]</dd>
+
+        <dt>EXE</dt>
+
+        <dd>binary executables<br />
+        [default = .exe]</dd>
+
+        <dt>DONE</dt>
+
+        <dd><em>done</em> files for compiled source<br />
+        [default = .done]</dd>
+
+        <dt>IDONE</dt>
+
+        <dd><em>done</em> files for included source<br />
+        [default = .idone]</dd>
+
+        <dt>FLAGS</dt>
+
+        <dd><em>flags</em> files, compiler flags config<br />
+        [default = .flags]</dd>
+
+        <dt>INTERFACE</dt>
+
+        <dd>interface files for F9X standalone subroutines/functions<br />
+        [default = .interface]</dd>
+
+        <dt>LIB</dt>
+
+        <dd>archive object library<br />
+        [default = .a]</dd>
+
+        <dt>TAR</dt>
+
+        <dd>TAR archive<br />
+        [default = .tar]</dd>
+      </dl>
+
+      <p>Example:</p>
+      <pre>
+# Output F9X interface files will now have ".foo" extension
+outfile_ext::interface  .foo
+</pre>
+    </dd>
+  </dl>
+
+  <p id="tools-list">The following is a list of &lt;label&gt; fields that can
+  be used with a <code>TOOL</code> declaration. Those marked with an asterisk
+  (*) accept declarations at sub-package levels.</p>
+
+  <dl>
+    <dt>FC</dt>
+
+    <dd>The Fortran compiler.<br />
+    [default = <samp>f90</samp>]</dd>
+
+    <dt>FFLAGS *</dt>
+
+    <dd>Options used by the Fortran compiler.<br />
+    [default = ""]</dd>
+
+    <dt>FC_COMPILE</dt>
+
+    <dd>The option used by the Fortran compiler to suppress the linking
+    stage.<br />
+    [default = <samp>-c</samp>]</dd>
+
+    <dt>FC_INCLUDE</dt>
+
+    <dd>The option used by the Fortran compiler to specify the include search
+    path.<br />
+    [default = <samp>-I</samp>]</dd>
+
+    <dt>FC_MODSEARCH</dt>
+
+    <dd>The option used by the Fortran compiler to specify the search
+    path for the compiled module definition files. This option is often
+    unnecessary as it is normally covered by the include search path.<br />
+    [default = ""]</dd>
+
+    <dt>FC_DEFINE</dt>
+
+    <dd>The option used by the Fortran compiler to define a pre-processor
+    definition macro.<br />
+    [default = <samp>-D</samp>]</dd>
+
+    <dt>FC_OUTPUT</dt>
+
+    <dd>The option used by the Fortran compiler to specify the output file
+    name.<br />
+    [default = <samp>-o</samp>]</dd>
+
+    <dt>CC</dt>
+
+    <dd>The C compiler.<br />
+    [default = <samp>cc</samp>]</dd>
+
+    <dt>CFLAGS *</dt>
+
+    <dd>Options used by the C compiler.<br />
+    [default = ""]</dd>
+
+    <dt>CC_COMPILE</dt>
+
+    <dd>The option used by the C compiler to suppress the linking stage.<br />
+    [default = <samp>-c</samp>]</dd>
+
+    <dt>CC_INCLUDE</dt>
+
+    <dd>The option used by the C compiler to specify the include search
+    path.<br />
+    [default = <samp>-I</samp>]</dd>
+
+    <dt>CC_DEFINE</dt>
+
+    <dd>The option used by the C compiler to define a pre-processor definition
+    macro.<br />
+    [default = <samp>-D</samp>]</dd>
+
+    <dt>CC_OUTPUT</dt>
+
+    <dd>The option used by the C compiler to specify the output file
+    name.<br />
+    [default = <samp>-o</samp>]</dd>
+
+    <dt>LD *</dt>
+
+    <dd>Name of the linker or loader for linking object files into an
+    executable. If not set, use the compiler of the source file containing the
+    main program.<br />
+    [default = ""]</dd>
+
+    <dt>LDFLAGS *</dt>
+
+    <dd>The flags used by the linker or loader.<br />
+    [default = ""]</dd>
+
+    <dt>LD_OUTPUT</dt>
+
+    <dd>The option used by the linker or loader for the output file name (other
+    than the default <samp>a.out</samp>).<br />
+    [default = <samp>-o</samp>]</dd>
+
+    <dt>LD_LIBSEARCH</dt>
+
+    <dd>The option used by the linker or loader for specifying the search path
+    for link libraries.<br />
+    [default = <samp>-L</samp>]</dd>
+
+    <dt>LD_LIBLINK</dt>
+
+    <dd>The option used by the linker or loader command for linking with a
+    library.<br />
+    [default = <samp>-l</samp>]</dd>
+
+    <dt>AR</dt>
+
+    <dd>The archive command.<br />
+    [default = <samp>ar</samp>]</dd>
+
+    <dt>ARFLAGS</dt>
+
+    <dd>The options used for the archive command to create a library.<br />
+    [default = <samp>rs</samp>]</dd>
+
+    <dt>FPP</dt>
+
+    <dd>The Fortran pre-processor command.<br />
+    [default = <samp>cpp</samp>]</dd>
+
+    <dt>FPPKEYS *</dt>
+
+    <dd>The Fortran pre-processor will pre-define each word in this setting as
+    a macro.<br />
+    [default = ""]</dd>
+
+    <dt>FPPFLAGS *</dt>
+
+    <dd>The options used by the Fortran pre-processor.<br />
+    [default = <samp>-P -traditional</samp>]</dd>
+
+    <dt>FPP_DEFINE</dt>
+
+    <dd>The option used by the Fortran pre-processor to define a macro.<br />
+    [default = <samp>-D</samp>]</dd>
+
+    <dt>FPP_INCLUDE</dt>
+
+    <dd>The option used by the Fortran pre-processor to specify the include
+    search path.<br />
+    [default = <samp>-I</samp>]</dd>
+
+    <dt>CPP</dt>
+
+    <dd>The C pre-processor command.<br />
+    [default = <samp>cpp</samp>]</dd>
+
+    <dt>CPPKEYS *</dt>
+
+    <dd>The C pre-processor will pre-define each word in this setting as a
+    macro.<br />
+    [default = ""]</dd>
+
+    <dt>CPPFLAGS *</dt>
+
+    <dd>The options used by the C pre-processor.<br />
+    [default = <samp>-C</samp>]</dd>
+
+    <dt>CPP_DEFINE</dt>
+
+    <dd>The option used by the C pre-processor to define a macro.<br />
+    [default = <samp>-D</samp>]</dd>
+
+    <dt>CPP_INCLUDE</dt>
+
+    <dd>The option used by the C pre-processor to specify the include search
+    path.<br />
+    [default = <samp>-I</samp>]</dd>
+
+    <dt>MAKE</dt>
+
+    <dd>The <code>make</code> command.<br />
+    [default = <samp>make</samp>]</dd>
+
+    <dt>MAKEFLAGS</dt>
+
+    <dd>The options used by the <code>make</code> command.<br />
+    [default = ""]</dd>
+
+    <dt>MAKE_SILENT</dt>
+
+    <dd>The option used by the <code>make</code> command to specify silent
+    operation.<br />
+    [default = <samp>-s</samp>]</dd>
+
+    <dt>MAKE_JOB</dt>
+
+    <dd>The option used by the <code>make</code> command to specify the number
+    jobs to run simultaneously.<br />
+    [default = <samp>-j</samp>]</dd>
+
+    <dt>GENINTERFACE *</dt>
+
+    <dd>The command/method to extract the calling interfaces of top level
+    subroutines and functions in a Fortran 9X source. Supported values are
+    <samp>f90aib</samp> and <samp>none</samp> (to switch off interface
+    generation). If not specified, the system will use its own internal logic.
+    <br />
+    [default = (not specified)]</dd>
+
+    <dt>INTERFACE *</dt>
+
+    <dd>Generate Fortran 9X interface files with root names according to either
+    the root name of the source <samp>file</samp> or the name of the
+    <samp>program</samp> unit.<br />
+    [default = <samp>file</samp>]</dd>
+  </dl>
+
+  <p id="infile-ext-types">The following is a list of type flags that are
+  currently in-use (or <dfn>* reserved</dfn>) by the build system for TYPE and
+  INFILE_EXT declarations:</p>
+
+  <dl>
+    <dt>SOURCE</dt>
+
+    <dd>a source file containing program code of a supported language
+    (currently Fortran, FPP, C and CPP).</dd>
+
+    <dt>INCLUDE</dt>
+
+    <dd>an include file containing program code of a supported language
+    (currently Fortran, FPP, C and CPP).</dd>
+
+    <dt>FORTRAN</dt>
+
+    <dd>a file containing Fortran code.</dd>
+
+    <dt>FORTRAN9X</dt>
+
+    <dd>a file containing the Fortran free source form. This word must be used
+    in conjunction with the word <code>FORTRAN</code>.</dd>
+
+    <dt>FPP</dt>
+
+    <dd>a file containing Fortran code requiring pre-processing.</dd>
+
+    <dt>FPP9X</dt>
+
+    <dd>a file containing Fortran free source form requiring pre-processing.
+    This word must be used in conjunction with the word <code>FPP</code>.</dd>
+
+    <dt>C</dt>
+
+    <dd>a file containing C code.</dd>
+
+    <dt>CPP</dt>
+
+    <dd>a file containing CPP include header.</dd>
+
+    <dt>INTERFACE</dt>
+
+    <dd>a file containing a Fortran 9X interface block.</dd>
+
+    <dt>PROGRAM</dt>
+
+    <dd>a file containing a main program.</dd>
+
+    <dt>MODULE</dt>
+
+    <dd>a file containing a Fortran 9X module.</dd>
+
+    <dt>BINARY</dt>
+
+    <dd>a binary file.</dd>
+
+    <dt>EXE</dt>
+
+    <dd>an executable file. This word must be used in conjunction with the word
+    <code>BINARY</code>.</dd>
+
+    <dt>LIB</dt>
+
+    <dd>an archive library. This word must be used in conjunction with the word
+    <code>BINARY</code>.</dd>
+
+    <dt>SCRIPT</dt>
+
+    <dd>a file containing source code of a scripting language.</dd>
+
+    <dt>PVWAVE</dt>
+
+    <dd>a file containing executable PVWAVE scripts. This word must be used in
+    conjunction with the word <code>SCRIPT</code>.</dd>
+
+    <dt>SQL</dt>
+
+    <dd>a file containing SQL scripts. This word must be used in conjunction
+    with the word <code>SCRIPT</code>.</dd>
+
+    <dt>GENLIST</dt>
+
+    <dd>a GEN List file.</dd>
+
+    <dt>OBJ</dt>
+
+    <dd><dfn>(* reserved)</dfn> an object file. This word must be used in
+    conjunction with the word <code>BINARY</code>.</dd>
+
+    <dt>SHELL</dt>
+
+    <dd><dfn>(* reserved)</dfn> a file containing executable shell scripts.
+    This word must be used in conjunction with the word
+    <code>SCRIPT</code>.</dd>
+
+    <dt>PERL</dt>
+
+    <dd><dfn>(* reserved)</dfn> a file containing executable Perl scripts. This
+    word must be used in conjunction with the word <code>SCRIPT</code>.</dd>
+
+    <dt>PYTHON</dt>
+
+    <dd><dfn>(* reserved)</dfn> a file containing executable Python scripts.
+    This word must be used in conjunction with the word
+    <code>SCRIPT</code>.</dd>
+
+    <dt>TCL</dt>
+
+    <dd><dfn>(* reserved)</dfn> a file containing executable TCL scripts. This
+    word must be used in conjunction with the word <code>SCRIPT</code>.</dd>
+  </dl>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/annex_ext_cfg.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/annex_ext_cfg.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/annex_ext_cfg.html	(revision 1981)
@@ -0,0 +1,364 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>Annex: Declarations in FCM extract configuration file</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide Annex" />
+  <meta name="keywords" content="FCM, user guide, annex" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Annex: Declarations in FCM extract configuration file</h1>
+
+  <p>The following is a list of supported declarations for the configuration
+  file used by the FCM extract system. Unless otherwise stated, the fields in
+  all declaration labels are not case sensitive.</p>
+
+  <dl>
+    <dt>CFG::TYPE</dt>
+
+    <dd>
+      <p>The configuration file type, the value should always be
+      <samp>ext</samp> for an extract configuration file. This declaration is
+      compulsory for all extract configuration files.</p>
+
+      <p>Example:</p>
+      <pre>
+cfg::type  ext
+</pre>
+    </dd>
+
+    <dt>CFG::VERSION</dt>
+
+    <dd>
+      <p>The file format version, currently <samp>1.0</samp> - a version is
+      included so that we shall be able to read the configuration file
+      correctly should we decide to change its format in the future.</p>
+
+      <p>Example:</p>
+      <pre>
+cfg::version  1.0
+</pre>
+    </dd>
+
+    <dt>%&lt;name&gt;</dt>
+
+    <dd>
+      <p><code>%&lt;name&gt;</code> declares an internal variable
+      <var>&lt;name&gt;</var> that can later be re-used.</p>
+
+      <p>Example:</p>
+      <pre>
+%my_variable   foo
+src::bar::base %my_variable
+src::egg::base %my_variable
+src::ham::base %my_variable
+</pre>
+    </dd>
+
+    <dt>INC</dt>
+
+    <dd>
+      <p>This declares the name of a file containing extract configuration. The
+      lines in the declared file will be included inline to the current
+      configuration file.</p>
+
+      <p>Example:</p>
+      <pre>
+inc  ~frva/var_stable_22.0/cfg/ext.cfg
+# ... and then your changes ...
+</pre>
+    </dd>
+
+    <dt>DEST[::ROOTDIR]</dt>
+
+    <dd>
+      <p>The <em>root</em> path of the destination of this extract. This
+      declaration is compulsory for all extract configuration files.</p>
+
+      <p>Example:</p>
+      <pre>
+dest  $HOME/project/my_project
+</pre>
+    </dd>
+
+    <dt>USE</dt>
+
+    <dd>
+      <p>This declares the location of a previous successful extract, which the
+      current extract will inherit from. If the previous extract is also a
+      build, the subsequent invocation of the build system on the current
+      extract will automatically trigger an inherited incremental build based
+      on that build.</p>
+
+      <p>Example:</p>
+      <pre>
+use  ~frva/var_stable_22.0
+# ... and then the settings for your current extract ...
+</pre>
+    </dd>
+
+    <dt>RDEST[::ROOTDIR]</dt>
+
+    <dd>
+      <p>The alternate destination of this extract. This declaration is
+      compulsory if this extract requires mirroring to an alternate
+      destination.</p>
+
+      <p>Example:</p>
+      <pre>
+rdest  /home/nwp/da/frva/project/my_project
+</pre>
+    </dd>
+
+    <dt>RDEST::LOGNAME</dt>
+
+    <dd>
+      <p>The login name of the user on the alternate destination machine. If
+      not specified, the current login name of the user on the local platform
+      is assumed.</p>
+
+      <p>Example:</p>
+      <pre>
+rdest::logname  frva
+</pre>
+    </dd>
+
+    <dt>RDEST::MACHINE</dt>
+
+    <dd>
+      <p>The destination machine for this extract. If not specified, the current
+      host name is assumed.</p>
+
+      <p>Example:</p>
+      <pre>
+rdest::machine  tx01
+</pre>
+    </dd>
+
+    <dt>
+      RDEST::MIRROR_CMD<br />
+      <del>MIRROR</del>
+    </dt>
+
+    <dd>
+      <p>The extract system can mirror the extracted source to an alternate
+      machine. Currently, it does this using either the <code>rdist</code> or
+      the <code>rsync</code> command. The default is <samp>rsync</samp>. This
+      declaration can be used to switch to using <samp>rdist</samp>.</p>
+
+      <p>Example:</p>
+      <pre>
+rdest::mirror_cmd  rdist
+</pre>
+    </dd>
+
+    <dt>
+      RDEST::RSH_MKDIR_RSH (<del>RDEST::REMOTE_SHELL</del>)<br />
+      RDEST::RSH_MKDIR_RSHFLAGS<br />
+      RDEST::RSH_MKDIR_MKDIR<br />
+      RDEST::RSH_MKDIR_MKDIRFLAGS
+    </dt>
+
+    <dd>
+      <p>If <code>rsync</code> is used to mirror an extract, the system needs to
+      issue a separate remote shell command to create the container directory of
+      the mirror destination. The default is to issue a shell command in the
+      form <samp>ssh -n -oBatchMode=yes LOGNAME@MACHINE mkdir -p DEST</samp>.
+      These declarations can be used to modify the command.</p>
+
+      <p>Example:</p>
+      <pre>
+# Examples using the default settings:
+rdest::rsh_mkdir_rsh         ssh
+rdest::rsh_mkdir_rshflags    -n -oBatchMode=yes
+rdest::rsh_mkdir_mkdir       mkdir
+rdest::rsh_mkdir_mkdirflags  -p
+</pre>
+    </dd>
+
+    <dt>
+      RDEST::RSYNC<br />
+      RDEST::RSYNCFLAGS
+    </dt>
+
+    <dd>
+      <p>These declarations are only useful if <code>rsync</code> is used to
+      mirror an extract. By default, the system issues the shell command
+      <samp>rsync -a --exclude='.*' --delete-excluded --timeout=900 --rsh='ssh
+      -oBatchMode=yes' SOURCE DEST</samp>. These declarations can be used to
+      modify the command.</p>
+
+      <p>Example:</p>
+      <pre>
+# Examples using the default settings:
+rdest::rsync       rsync
+rdest::rsyncflags  -a --exclude='.*' --delete-excluded --timeout=900 \
+                   --rsh='ssh -oBatchMode=yes'
+</pre>
+    </dd>
+
+    <dt>REPOS::&lt;pck&gt;::&lt;branch&gt;</dt>
+
+    <dd>
+      <p>This declares a URL or a local file system path for the container
+      <em>repository</em> of a branch named &lt;branch&gt; in a package named
+      &lt;pck&gt;. The package name &lt;pck&gt; must be the name of a top-level
+      package (i.e. it must not contain the double colon <code>::</code>
+      delimiter). The name &lt;branch&gt; is used internally within the extract
+      system, and so is independent of the branch name of the code management
+      system. However, it is usually desirable to use the same name of the
+      actual branch in the code management system. For declaration of a local
+      file system path, the convention is to name the branch <samp>user</samp>.
+      Please note that both &lt;pck&gt; and &lt;branch&gt; fields are case
+      sensitive. The declared URL must be a valid Subversion URL or a valid FCM
+      URL keyword.</p>
+
+      <p>Example:</p>
+      <pre>
+repos::var::base    fcm:var_tr
+repos::var::branch1 fcm:var_br/frsn/r4790_foobar
+repos::var::user    $HOME/var
+</pre>
+    </dd>
+
+    <dt>
+      REVISION::&lt;pck&gt;::&lt;branch&gt;<br />
+      <del>VERSION::&lt;pck&gt;::&lt;branch&gt;</del>
+    </dt>
+
+    <dd>
+      <p>The revision to be used for the URL of &lt;branch&gt; in the package
+      &lt;pck&gt;. If specified, the revision must be a revision where the
+      branch exists. If not specified, the revision defaults to last changed
+      revision at the HEAD of the branch. Please note that if the declared
+      <em>branch</em> is in the local file system, this declaration must not be
+      used. The value of the declaration can be a FCM revision keyword or any
+      revision argument acceptable by Subversion. You can use a valid revision
+      number, a date between a pair of curly brackets (e.g. <samp>{"2005-05-01
+      12:00"}</samp>) or the keyword HEAD. However, please do not use the
+      keywords BASE, COMMITTED or PREV as these are reserved for working copies
+      only. Again, please note that both &lt;pck&gt; and &lt;branch&gt; fields
+      are case sensitive.</p>
+
+      <p>Example:</p>
+      <pre>
+# Declare the revision with the FCM revision keyword "vn22.0"
+revision::var::base     vn22.0
+# Declare the revision with a {date}
+revision::var::branch1  {2006-01-01}
+</pre>
+    </dd>
+
+    <dt>REVMATCH</dt>
+
+    <dd>
+      <p>If set to true, the declared revision of a branch must be a changed
+      revision of that branch, (unless the keyword HEAD is used).</p>
+
+      <p>Example:</p>
+      <pre>
+revmatch  true
+</pre>
+    </dd>
+
+    <dt>SRC::&lt;pcks&gt;::&lt;branch&gt;</dt>
+
+    <dd>
+      <p>This declares a source directory for the sub-package &lt;pcks&gt; of
+      &lt;branch&gt;. If the repository is declared as a URL, the source
+      directory must be quoted as a relative path to the URL. If the repository
+      is declared as a path in the local file system, the source directory can
+      be declared as either a relative path to the <em>repository</em> or a
+      full path. If the source directory is a relative path and &lt;pcks&gt; is
+      a top-level package, the full name of the sub-package will be determined
+      automatically using the directory names of the relative path as the names
+      of the sub-packages. If the source directory is a full path, the full
+      sub-package name must be specified. The name of the sub-package
+      determines the destination path of the source directory in the
+      extract.</p>
+
+      <p>Example:</p>
+      <pre>
+src::var::base                    code/VarMod_PF
+src::var/code/VarMod_PF::user   $HOME/var/code/VarMod_PF 
+</pre>
+    </dd>
+
+    <dt>EXPSRC::&lt;pcks&gt;::&lt;branch&gt;</dt>
+
+    <dd>
+      <p>This declares an expandable source directory for the sub-package
+      &lt;pcks&gt; of &lt;branch&gt;. This declaration is essentially the same
+      as the SRC declaration, except that the system will attempt to search
+      recursively for sub-directories within the declared source directory.</p>
+
+      <p>Example:</p>
+      <pre>
+expsrc::var::base  code
+expsrc::var::user  code
+</pre>
+    </dd>
+
+    <dt>
+      CONFLICT<br />
+      <del>OVERRIDE</del>
+    </dt>
+
+    <dd>
+      <p>This declaration can be used to specify the conflict mode, which is
+      relevant when a file is modified by two different branches (or more)
+      relative to the base branch. The conflict mode can be <samp>fail</samp>,
+      <samp>merge</samp> (default) or <samp>override</samp> (or 0, 1 and 2
+      respectively). If <samp>fail</samp> is specified, the extract fails when
+      a file is modified by two branches (or more) relative to the base branch.
+      If <samp>merge</samp> is specified, the system will attempt to merge the
+      changes. It will fail only on unresolved conflicts. If
+      <samp>override</samp> is specified, the changes in the last branch takes
+      precedence and the changes in the earlier branches will be ignored. Note:
+      the old <code>override true|false</code> declaration is deprecated. If
+      declared, <code>override true</code> will be equivalent to <code>conflict
+      override</code>, and <code>override false</code> will be equivalent to
+      <code>conflict fail</code>.</p>
+
+      <p>Example:</p>
+      <pre>
+conflict  override
+</pre>
+    </dd>
+
+    <dt>BLD::&lt;fields&gt;</dt>
+
+    <dd>
+      <p>Declare a build configuration file declaration. The label
+      &lt;fields&gt; is the label of the declaration. On a successful extract,
+      &lt;fields&gt; will be added to the build configuration file. Please note
+      that some of the &lt;fields&gt; may be case sensitive.</p>
+
+      <p>Example:</p>
+      <pre>
+bld::target   VarScr_AnalysePF
+bld::tool::fc sxmpif90
+bld::tool::cc sxmpic++
+# ... and so on ...
+</pre>
+    </dd>
+  </dl>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/annex_fcm_cfg.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/annex_fcm_cfg.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/annex_fcm_cfg.html	(revision 1981)
@@ -0,0 +1,153 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>Annex: Declarations in FCM central/user configuration file</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide Annex" />
+  <meta name="keywords" content="FCM, user guide, annex" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Annex: Declarations in FCM central/user configuration file</h1>
+
+  <p>Please note that setting labels in both the central and the user
+  configuration files are case insensitive.</p>
+
+  <p>N.B. almost all settings in the Fcm::Config module can be modified using
+  the central/user configuration file. However, most users should only ever
+  need to use the following.</p>
+
+  <dl>
+    <dt>
+      SET::URL::&lt;pck&gt;<br />
+      <del>SET::REPOS::&lt;pck&gt;</del>
+    </dt>
+
+    <dd>
+      <p>This declares a URL keyword for the package &lt;pck&gt;. The value of
+      the declaration must be a valid Subversion &lt;URL&gt;. Once declared,
+      the URL keyword &lt;pck&gt; will be associated with the specified URL. In
+      subsequent invocations of the <code>fcm</code> command, the following
+      expansion may take place:</p>
+
+      <ul>
+        <li><samp>fcm:&lt;pck&gt;</samp>: replaced by
+        <samp>&lt;URL&gt;</samp>.</li>
+
+        <li><samp>fcm:&lt;pck&gt;_tr</samp> or <samp>fcm:&lt;pck&gt;-tr</samp>:
+        replaced by <samp>&lt;URL&gt;/trunk</samp></li>
+
+        <li><samp>fcm:&lt;pck&gt;_br</samp> or <samp>fcm:&lt;pck&gt;-br</samp>:
+        replaced by <samp>&lt;URL&gt;/branches</samp></li>
+
+        <li><samp>fcm:&lt;pck&gt;_tg</samp> or <samp>fcm:&lt;pck&gt;-tg</samp>:
+        replaced by <samp>&lt;URL&gt;/tags</samp></li>
+      </ul>
+
+      <p>Example:</p>
+      <pre>
+# Associate "var" with "svn://server/VAR_svn/var"
+set::url::var  svn://server/VAR_svn/var
+
+# "fcm:var" is now the same as "svn://server/VAR_svn/var"
+</pre>
+    </dd>
+
+    <dt>SET::REVISION::&lt;pck&gt;::&lt;keyword&gt;</dt>
+
+    <dd>
+      <p>This declares &lt;keyword&gt; to be the revision number for the
+      package &lt;pck&gt;. The &lt;keyword&gt; string can contain any
+      characters except spaces. It must not contain only digits (as digits are
+      treated as revision numbers). It must not be the Subversion revision
+      keywords HEAD, BASE, COMMITTED and PREV. It cannot begin and end with a
+      pair of curly brackets (as this will be parsed as a revision date). The
+      package &lt;pck&gt; must be associated with a URL using the
+      SET::URL::&lt;pck&gt; declaration described above before this declaration
+      can make sense. Once defined, &lt;keyword&gt; can be used anywhere in
+      place the defined revision number.</p>
+
+      <p>Example:</p>
+      <pre>
+set::revision::var::v22.0  8410
+
+# E.g. "fcm list -r v22.0 fcm:var" is now the same as
+#      "fcm list -r 8410 fcm:var".
+</pre>
+    </dd>
+
+    <dt>SET::URL_BROWSER_MAPPING_DEFAULT::&lt;key&gt;</dt>
+
+    <dd>
+      <p>These declarations are used to change the global default for mapping a
+      version control system URL to its corresponding web browser URL.
+      &lt;key&gt; can be LOCATION_COMPONENT_PATTERN, BROWSER_URL_TEMPLATE or
+      BROWSER_REV_TEMPLATE.</p>
+
+      <p>Example:</p>
+      <pre>
+set::url_browser_mapping_default::location_component_pattern ^//([^/]+)/(.*)$
+set::url_browser_mapping_default::browser_url_template http://{1}/intertrac/source:{2}{3}
+set::url_browser_mapping_default::browser_rev_template @{1}
+</pre>
+    </dd>
+
+    <dt>SET::URL_BROWSER_MAPPING::&lt;pck&gt;::&lt;key&gt;</dt>
+
+    <dd>
+      <p>Similar to SET::URL_BROWSER_MAPPING_DEFAULT::&lt;key&gt;, but settings
+      only apply to the specified &lt;pck&gt;.</p>
+
+      <p>Example:</p>
+      <pre>
+set::url_browser_mapping::var::location_component_pattern ^//([^/]+)/(.*)$
+set::url_browser_mapping::var::browser_url_template http://{1}/intertrac/source:{2}{3}
+set::url_browser_mapping::var::browser_rev_template @{1}
+</pre>
+    </dd>
+
+    <dt>SET::MISC::WEB_BROWSER</dt>
+
+    <dd>
+      <p>This declares a default web browser that can be used by some FCM
+      commands to browse files and documents online. The default is
+      <samp>firefox</samp>.</p>
+
+      <p>Example:</p>
+      <pre>
+# Use Netscape instead of Firefox
+set::misc::web_browser  netscape
+</pre>
+    </dd>
+
+    <dt>INC</dt>
+
+    <dd>
+      <p>This declares the name of a file containing user configuration. The
+      lines in the declared file will be included inline.</p>
+
+      <p>Example:</p>
+      <pre>
+inc  ~fred/etc/fcm.cfg
+# ... and then your changes ...
+</pre>
+    </dd>
+  </dl>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/annex_quick_ref.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/annex_quick_ref.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/annex_quick_ref.html	(revision 1981)
@@ -0,0 +1,204 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>Annex: Quick reference</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide Annex" />
+  <meta name="keywords" content="FCM, user guide, annex" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Annex: Quick reference</h1>
+
+  <p>Note: some sub-commands can be invoked with alternate names. For example,
+  <code>fcm help</code> is the same as <code>fcm ?</code>. In this annex, some
+  favourite alternate names are listed, separated by a pipe, i.e. the above
+  example will be given as <samp>fcm help|?</samp>.</p>
+
+  <h2 id="help">Getting help</h2>
+
+  <dl>
+    <dt><code>fcm help|?</code></dt>
+
+    <dd>get list of subcommands</dd>
+
+    <dt><code>fcm help|? SUBCOMMAND</code></dt>
+
+    <dd>get help on SUBCOMMAND</dd>
+  </dl>
+
+  <h2 id="maintaining-wc">Maintaining the working copy</h2>
+
+  <dl>
+    <dt><code>fcm checkout|co [OPTIONS] URL [DEST]</code></dt>
+
+    <dd>Checkout URL (and create a working copy at DEST)</dd>
+
+    <dt><code>fcm checkout|co -r N URL [DEST]</code></dt>
+
+    <dd>Checkout revision N of URL (and create a working copy at DEST)</dd>
+
+    <dt><code>fcm info</code></dt>
+
+    <dd>Print working copy information</dd>
+
+    <dt><code>fcm status|st [OPTIONS]</code></dt>
+
+    <dd>Print status of working copy</dd>
+
+    <dt><code>fcm status|st -u</code></dt>
+
+    <dd>Show update information</dd>
+
+    <dt><code>fcm status|st -v</code></dt>
+
+    <dd>Show verbose information</dd>
+
+    <dt><code>fcm update|up</code></dt>
+
+    <dd>Update working copy with repository changes</dd>
+
+    <dt><code>fcm switch|sw URL</code></dt>
+
+    <dd>Switch your working copy to point to a branch specified by URL</dd>
+
+    <dt><code>fcm commit|ci</code></dt>
+
+    <dd>Commit local changes back into the repository</dd>
+  </dl>
+
+  <h2 id="preparing-changes">Preparing changes</h2>
+
+  <dl>
+    <dt><code>fcm diff|di [OPTIONS]</code></dt>
+
+    <dd>Display working copy changes in unified diff format</dd>
+
+    <dt><code>fcm diff|di -b</code></dt>
+
+    <dd>Show differences relative to the base of the branch</dd>
+
+    <dt><code>fcm diff|di -g</code></dt>
+
+    <dd>Display working copy changes with a graphical diff tool</dd>
+
+    <dt><code>fcm diff|di -r N</code></dt>
+
+    <dd>Display working copy changes against revision N</dd>
+
+    <dt><code>fcm diff|di -t</code></dt>
+
+    <dd>Display differences in Trac, (with -b only)</dd>
+
+    <dt><code>fcm revert [OPTIONS] PATH</code></dt>
+
+    <dd>Restore the file PATH to the pristine copy</dd>
+
+    <dt><code>fcm revert -R PATH</code></dt>
+
+    <dd>Descend PATH recursively, restoring any modified files to the pristine
+    copy</dd>
+
+    <dt><code>fcm mkdir [PATH]</code></dt>
+
+    <dd>Add a directory PATH under revision control</dd>
+
+    <dt><code>fcm add [OPTIONS] [PATH]</code></dt>
+
+    <dd>Add PATH under revision control</dd>
+
+    <dt><code>fcm add -c [PATH]</code></dt>
+
+    <dd>Check for items not under revision control and add them</dd>
+
+    <dt><code>fcm delete|del|rm [OPTIONS] [PATH]</code></dt>
+
+    <dd>Remove PATH from revision control</dd>
+
+    <dt><code>fcm delete|del|rm -c [PATH]</code></dt>
+
+    <dd>Check for missing items and remove them</dd>
+
+    <dt><code>fcm copy|cp SRC DST</code></dt>
+
+    <dd>Duplicate SRC to DST, remembering history</dd>
+
+    <dt><code>fcm move|mv SRC DST</code></dt>
+
+    <dd>Move or rename SRC to DST, remembering history</dd>
+  </dl>
+
+  <h2 id="browse">Browsing</h2>
+
+  <dl>
+    <dt><code>fcm log [OPTIONS] [TARGET]</code></dt>
+
+    <dd>Show the log message of a TARGET that can either be working copy or
+    URL</dd>
+
+    <dt><code>fcm log -r N[:M] [TARGET]</code></dt>
+
+    <dd>Show the log message of a range of reivsions</dd>
+
+    <dt><code>fcm list|ls [OPTIONS] [TARGET]</code></dt>
+
+    <dd>List directory entries in TARGET</dd>
+
+    <dt><code>fcm list|ls -r N [TARGET]</code></dt>
+
+    <dd>List directory entries of revision N</dd>
+
+    <dt><code>fcm list|ls -v [TARGET]</code></dt>
+
+    <dd>List directory entries in verbose mode</dd>
+
+    <dt><code>fcm list|ls -R [TARGET]</code></dt>
+
+    <dd>List directory entries recursively down the directories</dd>
+
+    <dt><code>fcm trac [TARGET]</code></dt>
+
+    <dd>Open a WWW browser to browse TARGET with Trac</dd>
+  </dl>
+
+  <h2 id="branch">Branching</h2>
+
+  <dl>
+    <dt><code>fcm branch|br [OPTIONS] [URL]</code></dt>
+
+    <dd>Show branch information of URL or local working copy</dd>
+
+    <dt><code>fcm branch|br -d [URL]</code></dt>
+
+    <dd>Show branch information and delete the branch</dd>
+
+    <dt><code>fcm branch|br -c -n NAME [URL]</code></dt>
+
+    <dd>Create a branch</dd>
+
+    <dt><code>fcm merge [SOURCE]</code></dt>
+
+    <dd>Merge changes from SOURCE to your working copy</dd>
+
+    <dt><code>fcm conflicts|cf</code></dt>
+
+    <dd>Use xxdiff to resolve conflicts in your working copy</dd>
+  </dl>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/build.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/build.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/build.html	(revision 1981)
@@ -0,0 +1,1618 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: The Build System</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - The Build System" />
+  <meta name="keywords" content="FCM, user guide, extract, build" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>The Build System</h1>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="introduction">Introduction</h2>
+
+  <p>The build system analyses the directory tree containing a set of source
+  code, processes the configuration, and invokes <code>make</code> to
+  compile/build the source code into the project executables. In this chapter,
+  we shall use many examples to explain how to use the build system. At the end
+  of this chapter, you should be able to use the build system, either by
+  defining the build configuration file directly or by using the extract system
+  to generate a suitable build configuration file.</p>
+
+  <h2 id="command">The Build Command</h2>
+
+  <p>To invoke the build system, simply issue the command:</p>
+  <pre>
+fcm build
+</pre>
+
+  <p>By default, the build system searches for a build configuration file
+  <samp>bld.cfg</samp> in <samp>$PWD</samp> and then <samp>$PWD/cfg</samp>. If
+  a build configuration file is not found in these directories, the command
+  fails with an error. If a build configuration file is found, the system will
+  use the configuration specified in the file to perform the build. If you use
+  the extract system to extract your source tree, a build configuration should
+  be written for you automatically at the <samp>cfg/</samp> sub-directory of
+  the destination root directory.</p>
+
+  <p>If the root directory of the build does not exist, the system performs a
+  new full build at this directory. If a previous build already exists at this
+  directory, the system performs an incremental build. If a full (fresh) build
+  is required for whatever reason, you can invoke the build system using the
+  <code>-f</code> option, (i.e. the command becomes <code>fcm build -f</code>).
+  If you simply want to remove all the items generated by a previous build in
+  the destination, you can invoke the build system using the
+  <code>--clean</code> option.</p>
+
+  <p>The build system uses GNU <code>make</code> to perform the majority of the
+  build. GNU <code>make</code> has a <code>-j jobs</code> option to specify the
+  number of <var>jobs</var> to run simultaneously. Invoking the build system
+  with the same option triggers this option when the build system invokes the
+  <code>make</code> command. The argument to the option <var>jobs</var> must be
+  an integer. The default is <samp>1</samp>. For example, the command <code>fcm
+  build -j 4</code> will allow <code>make</code> to perform 4 jobs
+  simultaneously.</p>
+
+  <p>For further information on the build command, please see <a href=
+  "command_ref.html#fcm_bld">FCM Command Reference &gt; fcm build</a>.</p>
+
+  <h2 id="basic">Basic Features</h2>
+
+  <p>The build configuration file is the user interface of the build system. It
+  is a line based text file. You can create your own build configuration file
+  or you can use the extract system to create one for you. For a complete set
+  of build configuration file declarations, please refer to the <a href=
+  "annex_bld_cfg.html">Annex: Declarations in FCM build configuration
+  file</a>.</p>
+
+  <h3 id="basic_build">Basic build configuration</h3>
+
+  <p>Suppose we have a directory at <samp>$HOME/example</samp>. Its
+  sub-directory at <samp>$HOME/example/src</samp> contains a source tree to be
+  built. You may want to have a build configuration file
+  <samp>$HOME/example/cfg/bld.cfg</samp>, which may contain:</p>
+  <pre id="example_1">
+# Example 1
+# ----------------------------------------------------------------------
+cfg::type     bld                           # line 1
+cfg::version  1.0                           # line 2
+
+dest          $HOME/example                 # line 4
+
+target        foo.exe bar.exe               # line 6
+
+tool::fc      ifort                         # line 8
+tool::fflags  -O3                           # line 9
+tool::cc      gcc                           # line 10
+tool::cflags  -O3                           # line 11
+
+tool::ldflags -O3 -L$(HOME)/lib -legg -lham # line 13
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 1</dfn>: the label <code>CFG::TYPE</code> declares the type
+    of the configuration file. The value <samp>bld</samp> tells the system that
+    it is a build configuration file.</li>
+
+    <li><dfn>line 2</dfn>: the label <code>CFG::VERSION</code> declares the
+    version of the build configuration file. The current default is
+    <samp>1.0</samp>. Although it is not currently used, if we have to change
+    the format of the configuration file at a later stage, we shall be able to
+    use this number to determine whether we are reading a file with an older
+    format or one with a newer format.</li>
+
+    <li><dfn>line 4</dfn>: the label <code>DEST</code> declares the root
+    directory of the current build.</li>
+
+    <li><dfn>line 6</dfn>: the label <code>TARGET</code> declares a list of
+    <em>default</em> targets. The default targets of the current build will be
+    <samp>foo.exe</samp> and <samp>bar.exe</samp>.</li>
+
+    <li><dfn>line 8</dfn>: the label <code>TOOL::FC</code> declares the Fortran
+    compiler command.</li>
+
+    <li><dfn>line 9</dfn>: the label <code>TOOL::FFLAGS</code> declares the
+    options to be used when invoking the Fortran compiler command.</li>
+
+    <li><dfn>line 10</dfn>: the label <code>TOOL::CC</code> declares the C
+    compiler command.</li>
+
+    <li><dfn>line 11</dfn>: the label <code>TOOL::CFLAGS</code> declares the
+    options to be used when invoking the C compiler command.</li>
+
+    <li><dfn>line 13</dfn>: the label <code>TOOL::LDFLAGS</code> declares the
+    options to be used when invoking the linker command.</li>
+  </ul>
+
+  <p>When we invoke the build system, it reads the above configuration file. It
+  will go through various internal processes, such as dependency generations,
+  to obtain the required information to prepare the <samp>Makefile</samp> of
+  the build. (All of which will be described in later sections.) The
+  <samp>Makefile</samp> of the build will be placed at
+  <samp>$HOME/example/bld</samp>. The system will then invoke <code>make</code>
+  to build the targets specified in line 6, i.e. <samp>foo.exe</samp> and
+  <samp>bar.exe</samp> using the build tools specified between line 8 to line
+  13. On a successful build, the target executables will be sent to
+  <samp>$HOME/example/bin/</samp>. The build system also creates a shell script
+  called <samp>fcm_env.sh</samp> in <samp>$HOME/example/</samp>. If you source
+  the shell script, it will export your <var>PATH</var> environment variable to
+  search the <samp>$HOME/example/bin/</samp> directory for executables.</p>
+
+  <p>N.B. You may have noticed that the <code>-c</code> (compile to object file
+  only) option is missing from the compiler flags declarations. This is because
+  the option is inserted automatically by the build system, unless it is
+  already declared.</p>
+
+  <p>N.B. You can declare the linker using <code>TOOL::LD</code>. If it is not
+  specified, the default is to use the compiler command for the source file
+  containing the main program.</p>
+
+  <dl>
+    <dt>Note - declaration of source files for build</dt>
+
+    <dd>
+      <p>Source files do not have to reside in the <samp>src/</samp>
+      sub-directory of the build root directory. They can be anywhere, but you
+      will have to declare them using the label <code>SRC::&lt;pcks&gt;</code>,
+      where <code>&lt;pcks&gt;</code> is the sub-package name in which the
+      source belongs. If a directory is specified then the build system
+      automatically searches for all source files in this directory. E.g.</p>
+      <pre>
+# Declare a source in the sub-package "foo/bar"
+src::foo/bar  $HOME/foo/bar
+</pre>
+
+      <p>By default, the build system searches the <samp>src/</samp>
+      sub-directory of the build root directory for source files. If all source
+      files are already declared explicitly, you can switch off the automatic
+      directory search by setting the <code>SEARCH_SRC</code> flag to false.
+      E.g.</p>
+      <pre>
+search_src  false
+</pre>
+
+      <p>As mentioned in the previous chapter, the name of a sub-package
+      &lt;pcks&gt; provides a unique namespace for a file. The name of a
+      sub-package is a list of words delimited by a slash <code>/</code>. (The
+      system uses the double colons <code>::</code> and the double underscores
+      <code>__</code> internally. Please avoid using <code>::</code> and
+      <code>__</code> for naming your files and directories.)</p>
+
+      <p>Currently, the build system only supports non-space characters in the
+      package name, as the space character is used as a delimiter between the
+      declaration label and its value. If there are spaces in the path name to
+      a file or directory, you should explicity re-define the package name of
+      that path to a package name with no space using the above method.
+      However, we recommend that only non-space characters are used for naming
+      directories and files to make life simple.</p>
+
+      <p>In the build system, the sub-package name also provides an
+      <em>inheritance</em> relationship for sub-packages. For instance, we may
+      have a sub-package called <samp>foo/bar/egg</samp>, which belongs to the
+      sub-package <samp>foo/bar</samp>, which belongs to the package
+      <samp>foo</samp>.</p>
+
+      <ul>
+        <li>If we declare a global build tool, it applies to all packages.</li>
+
+        <li>If we declare a build tool for <samp>foo</samp>, it applies also to
+        the sub-package <samp>foo/bar</samp> and <samp>foo/bar/egg</samp>.</li>
+
+        <li>If we declare a build tool for <samp>foo/bar</samp>, it applies
+        also to <samp>foo/bar/egg</samp>, but not to other sub-packages in
+        <samp>foo</samp>.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h3 id="basic_extract">Build configuration via the extract system</h3>
+
+  <p>As mentioned earlier, you can obtain a build configuration file through
+  the extract system. The following example is what you may have in your
+  extract configuration in order to obtain a similar configuration as <a href=
+  "#example_1">example 1</a>:</p>
+  <pre id="example_2">
+# Example 2
+# ----------------------------------------------------------------------
+cfg::type          ext                           # line 1
+cfg::version       1.0                           # line 2
+
+dest               $HOME/example                 # line 4
+
+bld::target        foo.exe bar.exe               # line 6
+
+bld::tool::fc      ifort                         # line 8
+bld::tool::fflags  -O3                           # line 9
+bld::tool::cc      gcc                           # line 10
+bld::tool::cflags  -O3                           # line 11
+
+bld::tool::ldflags -O3 -L$(HOME)/lib -legg -lham # line 13
+
+# ... and other declarations for source locations ...
+</pre>
+
+  <p>It is easy to note the similarities and differences between <a href=
+  "#example_1">example 1</a> and <a href="#example_2">example 2</a>. <a href=
+  "#example_2">Example 2</a> is an extract configuration file. It extracts to a
+  destination root directory that will become the root directory of the build.
+  Line 6 to line 13 are the same declarations, except that they are now
+  prefixed with <code>BLD::</code>. In an extract configuration file, any lines
+  prefixed with <code>BLD::</code> means that they are build configuration
+  setting. These lines are ignored by the extract system but are parsed down to
+  the output build configuration file, with the <code>BLD::</code> prefix
+  removed. (Note: the <code>BLD::</code> prefix is optional for declarations in
+  a build configuration file.)</p>
+
+  <p>N.B. If you use the extract system to mirror an extract to an alternate
+  location, the extract system will assume that the root directory of the
+  alternate destination is the root directory of the build, and that the build
+  will be carried out in that destination.</p>
+
+  <h3 id="basic_exename">Naming of executables</h3>
+
+  <p>If a source file called <samp>foo.f90</samp> contains a main program, the
+  default behaviour of the system is to name its executable
+  <samp>foo.exe</samp>. The root name of the executable is the same as the
+  original file name, but its file extension is replaced with
+  <samp>.exe</samp>. The output extension can be altered by re-registering the
+  extension for output EXE files. How this can be done will be discussed later
+  in the sub-section <a href="#advanced_file-type">File Type</a>.</p>
+
+  <p>If you need to alter the full name of the executable, you can use the
+  <code>EXE_NAME::</code> declaration. For example, the declaration:</p>
+  <pre>
+bld::exe_name::foo  bar
+</pre>
+
+  <p>will rename the executable of <samp>foo.f90</samp> from
+  <samp>foo.exe</samp> to <samp>bar</samp>.</p>
+
+  <p>Note: the declaration label is <code>bld::exe_name::foo</code> (not
+  <code>bld::exe_name::foo.exe</code>) and the executable will be named
+  <samp>bar</samp> (not <samp>bar.exe</samp>).</p>
+
+  <h3 id="basic_flags">Setting the compiler flags</h3>
+
+  <p>As discussed in the first example, the compiler commands and their flags
+  can be set via the <code>TOOL::</code> declarations. A simple
+  <code>TOOL::FFLAGS</code> declaration, for example, alters the compiler
+  options for compiling all Fortran source files in the build. If you need to
+  alter the compiler options only for the source files in a particular
+  sub-package, it is possible to do so by adding the sub-package name to the
+  declaration label. For example, the declaration label
+  <code>TOOL::FFLAGS::foo/bar</code> will ensure that the declaration only
+  applies to the code in the sub-package <samp>foo/bar</samp>. You can even
+  make declarations down to the individual source file level. For example, the
+  declaration label <code>TOOL::FFLAGS::foo/bar/egg.f90</code> will ensure that
+  the declaration applies only for the file <samp>foo/bar/egg.f90</samp>.</p>
+
+  <p>N.B. Although the prefix <code>TOOL::</code> and the tool names are
+  case-insensitive, sub-package names are case sensitive in the declarations.
+  Internally, tool names are turned into uppercase, and the sub-package
+  delimiters are changed from the slash <code>/</code> (or double colons
+  <code>::</code>) to the double underscores <code>__</code>. When the system
+  generates the <samp>Makefile</samp> for the build, each <code>TOOL</code>
+  declaration will be exported as an environment variable. For example, the
+  declaration <code>tool::fflags/foo/bar</code> will be exported as
+  <samp>FFLAGS__foo__bar</samp>.</p>
+
+  <p>N.B. <code>TOOL</code> declarations for sub-packages are only accepted by
+  the system when it is sensible to do so. For example, it allows you to
+  declare different compiler flags, linker commands and linker flags for
+  different sub-packages, but it does not accept different compilers for
+  different sub-packages. If you attempt to make a <code>TOOL</code>
+  declaration for a sub-package that does not exist, the build system will exit
+  with an error.</p>
+
+  <p>The following is an example setting in an extract configuration file based
+  on <a href="#example_2">example 2</a>:</p>
+  <pre id="example_3">
+# Example 3
+# ----------------------------------------------------------------------
+cfg::type              ext
+cfg::version           1.0
+
+dest                   $HOME/example
+
+bld::target            foo.exe bar.exe
+
+bld::tool::fc          ifort
+bld::tool::fflags      -O3    # line 9
+bld::tool::cc          gcc
+bld::tool::cflags      -O3
+
+bld::tool::ldflags     -L$(HOME)/lib -legg -lham
+
+bld::tool::fflags::ops -O1 -C # line 15
+bld::tool::fflags::gen -O2    # line 16
+
+# ... and other declarations for repositories and source directories ...
+</pre>
+
+  <p>In the example above, line 15 alters the Fortran compiler flags for
+  <samp>ops</samp>, so that all source files in <samp>ops</samp> will be
+  compiled with optimisation level 1 and will have runtime error checking
+  switched on. Line 16, alters the Fortran compiler flags for <samp>gen</samp>,
+  so that all source files in <samp>gen</samp> will be compiled with
+  optimisation level 2. All other Fortran source files will use the global
+  setting declared at line 9, so they they will all be compiled with
+  optimisation level 3.</p>
+
+  <dl>
+    <dt>Note - changing compiler flags in incremental builds</dt>
+
+    <dd>
+      <p>Suppose you have performed a successful build using the configuration
+      in <a href="#example_3">example 3</a>, and you have decided to change
+      some of the compiler flags, you can do so by altering the appropriate
+      flags in the build configuration file. When you trigger an incremental
+      build, the system will detect changes in compiler flags automatically,
+      and update only the required targets. The following hierarchy is
+      followed:</p>
+
+      <ul>
+        <li>If the compiler flags for a particular source file change, only
+        that source file and any targets depending on that source file are
+        re-built.</li>
+
+        <li>If the compiler flags for a container package change, only source
+        files within that container package and any targets depending on those
+        source files are re-built.</li>
+
+        <li>If the global compiler flags change, all source files are
+        re-built.</li>
+
+        <li>If the compiler command changes, all source files are
+        re-built.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <p>N.B. For a full list of build tools declarations, please see <a href=
+  "annex_bld_cfg.html#tools-list">Annex: Declarations in FCM build
+  configuration file &gt; list of tools</a>.</p>
+
+  <h3 id="basic_interface">Automatic Fortran 9X interface block</h3>
+
+  <p>For each Fortran 9X source file containing standalone subroutines and/or
+  functions, the system generates an interface file and sends it to the
+  <samp>inc/</samp> sub-directory of the build root. An interface file contains
+  the interface blocks for the subroutines and functions in the original source
+  file. In an incremental build, if you have modified a Fortran 9X source file,
+  its interface file will only be re-generated if the content of the interface
+  has changed.</p>
+
+  <p>Consider a source file <samp>foo.f90</samp> containing a subroutine called
+  <samp>foo</samp>. In a normal operation, the system writes the interface file
+  to <samp>foo.interface</samp> in the <samp>inc/</samp> sub-directory of the
+  build root. By default, the root name of the interface file is the same as
+  that of the source file, and is case sensitive. You can change this behaviour
+  using a <code>TOOL::INTERFACE</code> declaration. E.g.:</p>
+  <pre>
+bld::tool::interface  program # The default is "file"
+</pre>
+
+  <p>In such case, the root name of the interface file will be named in lower
+  case after the first program unit in the file.</p>
+
+  <p>The default extension for an interface file is <samp>.interface</samp>.
+  This can be modified through the input and output file type register, which
+  will be discussed in a later section on <a href="#advanced_file-type">File
+  Type</a>.</p>
+
+  <p>In most cases, we modify procedures without altering their calling
+  interfaces. Consider another source file <samp>bar.f90</samp> containing a
+  subroutine <samp>bar</samp>. If <samp>bar</samp> calls <samp>foo</samp>, it
+  is good practice for <samp>bar</samp> to have an explicit interface for
+  <samp>foo</samp>. This can be achieved if the subroutine <samp>bar</samp> has
+  the following within its declaration section:</p>
+  <pre>
+INCLUDE 'foo.interface'
+</pre>
+
+  <p>The source file <samp>bar.f90</samp> is now dependent on the interface
+  file <samp>foo.interface</samp>. This can make incremental build very
+  efficient, as changes in the <samp>foo.f90</samp> file will not normally
+  trigger the re-compilation of <samp>bar.f90</samp>, provided that the
+  interface of the <code>subroutine foo</code> remains unchanged. (However, the
+  system is clever enough to know that it needs to re-link any executables that
+  are dependent on the object file for the <code>subroutine bar</code>.)</p>
+
+  <p>By default, the system uses its own internal logic to extract the calling
+  interfaces of top level subroutines and functions in a Fortran source file to
+  generate an interface block. However, the system can also work with the
+  interface generator <code>f90aib</code>, which is a freeware obtained from
+  <a href=
+  "http://www.ifremer.fr/ditigo/molagnon/fortran90/contenu.html">Fortran 90
+  texts and programs, assembled by Michel Olagnon</a> at the French Research
+  Institute for Exploitation of the Sea. To do so, you need to make a
+  declaration in the build configuration file using the label
+  <code>TOOL::GENINTERFACE</code>. As for any other <code>TOOL</code>
+  declarations, you can attach a sub-package name to the label. The change will
+  then apply only to source files within that sub-package. If
+  <code>TOOL::GENINTERFACE</code> is declared to have the value
+  <code>NONE</code>, interface generation will be switched off. The following
+  are some examples:</p>
+  <pre id="example_4">
+# Example 4
+# ----------------------------------------------------------------------
+# This is an EXTRACT configuration file ...
+
+# ... some other declarations ...
+
+bld::tool::geninterface       f90aib # line 5
+bld::tool::geninterface::bar  none   # line 6
+
+# ... some other declarations ...
+</pre>
+
+  <p>In line 5, the global interface generator is now set to
+  <code>f90aib</code>. In line 6, by setting the interface generator for the
+  package <samp>bar</samp> to the <code>none</code> keyword, no interface file
+  will be generated for source files under the package <samp>bar</samp>.</p>
+
+  <p>Switching off the interface block generator can be useful in many
+  circumstances. For example, if the interface block is already provided
+  manually within the source tree, or if the interface block is never used by
+  other program units, it is worth switching off the interface generator for
+  the source file to speed up the build process.</p>
+
+  <h3 id="basic_dependency">Automatic dependency</h3>
+
+  <p>The build system has a built-in dependency scanner, which works out the
+  dependency relationship between source files, so that they can be built in
+  the correct order. The system scans all source files of known types for all
+  supported dependency patterns. Dependencies of source files in a sub-package
+  are written in a cache, which can be retrieved for incremental builds. (In an
+  incremental build, only changed source files need to be re-scanned for
+  dependency information. Dependency information for other files are retrieved
+  from the cache.) The dependency information is passed to the
+  <code>make</code> rule generator, which writes the <samp>Makefile</samp>.</p>
+
+  <p>The <code>make</code> rule generator generates different <code>make</code>
+  rules for different dependency types. The following dependency patterns are
+  automatically detected by the current system:</p>
+
+  <ul>
+    <li>The <code>USE &lt;module&gt;</code> statement in a Fortran source file
+    is the first pattern. The statement has two implications: 1) The current
+    file compiles only if the module has been successfully compiled, and needs
+    to be re-compiled if the module has changed. 2) The executable depending on
+    the current file can only resolve all its externals by linking with the
+    object file of the compiled module. The executable needs to be re-linked if
+    the module and its dependencies has changed.</li>
+
+    <li>The <code>INCLUDE '&lt;name&gt;.interface'</code> statement in a
+    Fortran source file is the second pattern. (The default extension for an
+    interface file is <samp>.interface</samp>. This can be modified through the
+    input and output file type register, which will be discussed in a later
+    section on <a href="#advanced_file-type">File Type</a>.) It has two
+    implications: 1) The current file compiles only if the included interface
+    file is in the INCLUDE search path, and needs to be re-compiled if the
+    interface file changes. 2) The executable depending on the current file can
+    only resolve all its externals by linking with the object file of the
+    source file that generates the interface file. The executable needs to be
+    re-linked if the source file (and its dependencies) associated with the
+    interface file has changed. It is worth noting that for this dependency to
+    work, the root &lt;name&gt; of the interface file should match with that of
+    the source file associated with the interface file. (Please note that you
+    can use pre-processor [#include "&lt;name&gt;.interface] instead of Fortran
+    INCLUDE, but it will not work if you switch on the <a href=
+    "#advanced_pp">pre-processing</a> stage, which will be discussed in a later
+    section.)</li>
+
+    <li>The <code>INCLUDE '&lt;file&gt;'</code> statement (excluding the
+    INCLUDE interface file statement) in a Fortran source file is the third
+    pattern. It has two implications: 1) The current file compiles only if the
+    included file is in the INCLUDE search path, and needs to be re-compiled if
+    the include file changes. 2) The executable needs to be linked with any
+    objects the include file is dependent on. It needs to be re-linked if these
+    objects have changed.</li>
+
+    <li>The <code>#include '&lt;file&gt;'</code> statement in a Fortran/C
+    source or header file is the fourth pattern. It has similar implications as
+    the Fortran INCLUDE statement. However, they have to be handled differently
+    because <code>#include</code> statements are processed by the
+    pre-processor, which may be performed in a separate stage of the FCM build
+    process. This will be further discussed in a later sub-section on <a href=
+    "#advanced_pp">Pre-processing</a>.</li>
+  </ul>
+
+  <p>If you want your code to be built automatically by the FCM build system,
+  you should also design your code to conform to the following rules:</p>
+
+  <ol>
+    <li>Single compilable program unit, (i.e. program, subroutine, function or
+    module), per file.</li>
+
+    <li>Unique name for each compilable program unit.</li>
+
+    <li>Always supply an interface for subroutines and functions, i.e.:
+
+      <ul>
+        <li>Put them in modules.</li>
+
+        <li>Put them in the CONTAINS section within the main program unit.</li>
+
+        <li>Use interface files.</li>
+      </ul>
+    </li>
+
+    <li>If interface files are used, it is good practise to name each source
+    file after the program unit it contains. It will make life a lot simpler
+    when using the <a href="#basic_interface">Automatic Fortran 9X interface
+    block</a> feature, which has already been discussed in the previous
+    section.
+
+      <ul>
+        <li>The problem is that, by default, the root name of the interface
+        file is the same as that of the source file rather than the program
+        unit. If they differ then the build system will create a dependency on
+        the wrong object file (since the object files are named according to
+        the program unit).</li>
+
+        <li>This problem can be avoided by changing the behaviour of the
+        interface file generator to use the name of the program unit instead
+        (using a <code>TOOL::INTERFACE</code> declaration).</li>
+      </ul>
+    </li>
+  </ol>
+
+  <dl>
+    <dt>Note - setting build targets</dt>
+
+    <dd>
+      <p>The <samp>Makefile</samp> generated by the build system contains a
+      list of targets that can be built. The build system allows you to build
+      (or perform the actions of) any targets that are present in the generated
+      <samp>Makefile</samp>. There are two ways to specify the targets to be
+      built.</p>
+
+      <p>Firstly, you can use the <code>TARGET</code> declarations in your
+      build configuration file to specify the default targets to be built.
+      These targets will be set as dependencies of the <samp>all</samp> target
+      in the generated <samp>Makefile</samp>, which is the default target to be
+      built when <code>make</code> is invoked by FCM. It is worth noting that
+      <code>TARGET</code> declarations are cumulative. A later declaration does
+      not override an earlier one - it simply adds more targets to the
+      list.</p>
+
+      <p>Alternatively, you can use the <code>-t</code> option when you invoke
+      the <code>fcm build</code> command. The option takes an argument, which
+      should be a colon <code>:</code> separated list of targets to be built.
+      When the <code>-t</code> option is set, FCM invokes <code>make</code> to
+      build these targets instead. (E.g. if we invoke the build system with the
+      command <code>fcm build -t foo.exe:bar.exe</code>, it will invoke
+      <code>make</code> to build <samp>foo.exe</samp> and
+      <samp>bar.exe</samp>.)</p>
+
+      <p>If you do not specify any explicit targets, the system will search
+      your source tree for main programs:</p>
+
+      <ul>
+        <li>If there are main programs in your source tree, they will be set as
+        the default targets automatically.</li>
+
+        <li>Otherwise, the default is to build the top level library archive
+        containing objects compiled from the source files in the current source
+        tree. (For more information on building library archives, please see
+        the section on <a href="#advanced_library">Creating library
+        archives</a>.)</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h2 id="advanced">Advanced Features</h2>
+
+  <h3 id="advanced_dependency">Further dependency features</h3>
+
+  <p>Apart from the usual dependency patterns described in the previous
+  sub-section, the automatic dependency scanner also recognises two special
+  directives when they are inserted into a source file:</p>
+
+  <ul>
+    <li>The directive <code>DEPENDS ON: &lt;object&gt;</code> in a comment line
+    of a Fortran/C source file: It states that the current file is dependent on
+    the declared external object. The executable depending on the current file
+    needs to link with this external object in order to resolve all its
+    external references. It needs to be re-linked if the declared external
+    object (and its dependencies) has changed.</li>
+
+    <li>The directive <code>CALLS: &lt;executable&gt;</code> in a comment line
+    of a script: It states that the current script is dependent on the declared
+    executable file, which can be another script or a binary executable. The
+    current script can only function correctly if the declared executable is
+    found in the search path. This directive is useful to ensure that all
+    dependent executables are built or copied to the correct path.</li>
+  </ul>
+
+  <p>Another way to specify external dependency is to use the
+  <code>EXE_DEP</code> declaration to declare extra dependencies. The
+  declaration normally applies to all main programs, but if the the form
+  <code>EXE_DEP::&lt;target&gt;</code> is used, it will only apply to
+  &lt;target&gt;, (which must be the name of a main program target). If the
+  declaration is made without a value, the main programs will be set to depend
+  on all object files. Otherwise, the value can be supplied as a space
+  delimited list of items. Each item can be either the name of a sub-package or
+  an object target. For the former, the main programs will be set to depend on
+  all object files within the sub-package. For the latter, the main programs
+  will be set to depend on the object target. The following are some
+  examples:</p>
+  <pre id="example_5">
+# Example 5
+# ----------------------------------------------------------------------
+cfg::type          ext
+cfg::version       1.0
+
+bld::exe_dep::foo.exe  foo/bar egg.o # line 4
+bld::exe_dep                         # line 5
+# ... some other declarations ...
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 4</dfn>: this line declares the dependency on the sub-package
+    <samp>foo/bar</samp> and the object target <samp>egg.o</samp> for building
+    the main program target <samp>foo.exe</samp>. The target
+    <samp>foo.exe</samp> will now depends on all object files in the
+    <samp>foo/bar</samp> sub-package as well as the object target
+    <samp>egg.o</samp>.</li>
+
+    <li><dfn>line 5</dfn>: this line declares that all other main program
+    targets will depend on all (non-program) object files in the build.</li>
+  </ul>
+
+  <dl>
+    <dt>Note - naming of object files</dt>
+
+    <dd>
+      <p>By default, object files are named with the suffix <samp>.o</samp>.
+      For a Fortran source file, the build system uses the lower case name of
+      the first program unit within the file to name its object file. For
+      example, if the first program unit in the Fortran source file
+      <samp>foo.f90</samp> is <code>PROGRAM Bar</code>, the object file will be
+      <samp>bar.o</samp>. For a C source file, the build system uses the lower
+      case root name of the source file to name its object file. For example, a
+      C source file called <samp>egg.c</samp> will have its object file named
+      <samp>egg.o</samp>.</p>
+
+      <p>The reason for using lower case to name the object files is because
+      Fortran is a case insensitive language. Its symbols can either be in
+      lower or upper case. E.g. the <code>SUBROUTINE Foo</code> is the same as
+      the <code>SUBROUTINE foo</code>. It can be rather confusing if the
+      subroutines are stored in different files. When they are compiled and
+      archived into a library, there will be a clash of namespace, as the
+      Fortran compiler thinks they are the same. However, this type of error
+      does not normally get reported. If <samp>Foo</samp> and <samp>foo</samp>
+      are very different code, the user may end up using the wrong subroutine,
+      which may lead to a very long debugging session. By naming all object
+      files in lower case, this type of situation can be avoided. If there is a
+      clash in names due to the use of upper/lower cases, it will be reported
+      as warnings by the build system, (as <em>duplicated targets</em> for
+      building <samp>foo.o</samp>).</p>
+    </dd>
+  </dl>
+
+  <p>It is realised that there are situations when an automatically detected
+  dependency should not be written into the <samp>Makefile</samp>. For example,
+  the dependency may be a standard module provided by the Fortran compiler, and
+  does not need to be built in the usual way. In such case, we need to have a
+  way to exclude this module during an automatic dependency scan.</p>
+
+  <p>The <code>EXCL_DEP</code> declaration can be used to do just that. The
+  following extract configuration contains some examples of the basic usage of
+  the <code>EXCL_DEP</code> declaration:</p>
+  <pre id="example_6">
+# Example 6
+# ----------------------------------------------------------------------
+cfg::type          ext
+cfg::version       1.0
+
+bld::excl_dep  USE::YourFortranMod             # line 4
+bld::excl_dep  INTERFACE::HerFortran.interface # line 5
+bld::excl_dep  INC::HisFortranInc.inc          # line 6
+bld::excl_dep  H::TheirHeader.h                # line 7
+bld::excl_dep  OBJ::ItsObject.o                # line 8
+
+# ... some other declarations ...
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 4</dfn>: this line declares that the Fortran module
+    <samp>YourFortranMod</samp> should be excluded. The value of each
+    <code>EXCL_DEP</code> declaration has two parts. The first part is a label
+    that is used to define the type of dependency to be excluded. For a full
+    list of these labels, please see the <a href=
+    "annex_bld_cfg.html#dependency-types">dependency types table</a> in the
+    <a href="annex_bld_cfg.html">Annex: Declarations in FCM build configuration
+    file</a>. The label <code>USE</code> denotes a Fortran module. The second
+    part of the label is the dependency itself. For instance, if a Fortran
+    source file contains the line: <code>USE YourFortranMod</code>, the
+    dependency scanner will ignore it.</li>
+
+    <li><dfn>line 5</dfn>: this line declares that the include statement for
+    the Fortran 9X interface file <samp>HerFortran.interface</samp> should be
+    excluded. The label <code>INTERFACE</code> denotes a Fortran INCLUDE
+    statement for a Fortran 9X interface block file. For example, if a Fortran
+    source file contains the line: <code>INCLUDE 'HerFortran.interface'</code>,
+    the dependency scanner will ignore it.</li>
+
+    <li><dfn>line 6</dfn>: this line declares that the include statement for
+    <samp>HisFortranInc.inc</samp> should be excluded. The label
+    <code>INC</code> denotes a Fortran INCLUDE statement other than an INCLUDE
+    statement for an interface block file. For example, if a Fortran source
+    file contains the line: <code>INCLUDE 'HisFortranInc.inc'</code>, the
+    dependency scanner will ignore it.</li>
+
+    <li><dfn>line 7</dfn>: this line declares that the header include statement
+    <samp>TheirHeader.h</samp> should be excluded. The label <code>H</code>
+    denotes a pre-processing #include statement. For example, if a source file
+    contains the line: <code>#include 'TheirHeader.h'</code>, the dependency
+    scanner will ignore it.</li>
+
+    <li><dfn>line 8</dfn>: this line declares that the external dependency for
+    <samp>ItsObject.o</samp> should be excluded. The label <code>OBJ</code>
+    denotes a compiled binary object. These dependencies are normally inserted
+    into the source files as special comments. For example, if a source file
+    contains the line: <code>! depends on: ItsObject.o</code>, the dependency
+    scanner will ignore it.</li>
+  </ul>
+
+  <p>An <code>EXCL_DEP</code> declaration normally applies to all files in the
+  build. However, you can suffix it with the name of a sub-package, i.e.
+  <code>EXCL_DEP::&lt;pcks&gt;</code>. In such case, the declaration will only
+  apply while scanning for dependencies in the source files in the sub-package
+  named &lt;pcks&gt;.</p>
+
+  <p>You can also exclude all dependency scan of a particular type. To do so,
+  simply declare the type in the value. For example, if you do not want the
+  build system to scan for the <code>CALLS: &lt;executable&gt;</code> directive
+  in the comment lines of your scripts, you can make the following
+  declaration:</p>
+  <pre>
+bld::excl_dep  EXE
+</pre>
+
+  <p>The opposite of the <code>EXCL_DEP</code> declaration is the
+  <code>DEP::&lt;pcks&gt;</code> declaration, which you can use to add a
+  dependency to a source file (in the package name <code>&lt;pcks&gt;</code>).
+  The syntax of the declaration is similar to that of <code>EXCL_DEP</code>,
+  but you must specify the package name of a source file for DEP declarations.
+  Please also note that a <code>DEP</code> declaration only works if the
+  particular dependency is supported for the particular source file - as it
+  makes no sense, for example, to specify a USE dependency for a shell
+  script.</p>
+
+  <p>If you need to switch off dependency checking completely, you can use the
+  <code>NO_DEP</code> declaration. For example, to switch off dependency
+  checking for all but the <samp>foo/bar</samp> sub-package, you can do:</p>
+  <pre>
+bld::no_dep           true
+bld::no_dep::foo/bar  false
+</pre>
+
+  <h3 id="advanced_blockdata">Linking a Fortran executable with a BLOCKDATA
+  program unit</h3>
+
+  <p>If it is required to link Fortran executables with BLOCKDATA program
+  units, you must declare the executable targets and the objects containing the
+  BLOCKDATA program units using the <code>BLOCKDATA::&lt;target&gt;</code>
+  declarations. For example, if <samp>foo.exe</samp> is an executable target
+  depending on the objects of the BLOCKDATA program units
+  <samp>blkdata.o</samp> and <samp>fbk.o</samp>, you will make the following
+  declarations:</p>
+  <pre>
+bld::blockdata::foo.exe  blkdata fbk
+</pre>
+
+  <p>If all your executables are dependent on <samp>blkdata.o</samp> and
+  <samp>fbk.o</samp>, you will make the following declarations:</p>
+  <pre>
+bld::blockdata  blkdata fbk
+</pre>
+
+  <h3 id="advanced_library">Creating library archives</h3>
+
+  <p>If you are interested in building library archives, the build system
+  allows you to do it in a relatively simple way. For each sub-package in the
+  source tree, there is a target to build a library containing all the objects
+  compiled from the source files (that are not main programs) within the
+  sub-package. If the sub-package contains children sub-packages, the object
+  files of the children will also be included recursively. By default, the
+  library archive is named after the sub-package, in the format
+  <code>lib&lt;pcks&gt;.a</code>. (For example, the library archive for the
+  package <samp>foo/bar/egg</samp> will be named
+  <samp>libfoo__bar__egg.a</samp> by default.) If you do not like the default
+  name for the sub-package library, you can use the
+  <code>LIB::&lt;pcks&gt;</code> declaration to rename it, as long as the new
+  name does not clash with other targets. For example, to rename
+  <samp>libfoo__bar__egg.a</samp> to <samp>libham.a</samp>, you will make the
+  following declaration in your extract configuration file:</p>
+  <pre>
+bld::lib::foo/bar/egg  ham
+</pre>
+
+  <p>In addition to sub-package libraries, you can also build a global library
+  archive for the whole source tree. By default, the library is named
+  <samp>libfcm_default.a</samp>, but you can rename it using the
+  <code>LIB</code> declaration as above. For example, to rename the library to
+  <samp>libmy-lib.a</samp>, you will make the following declaration in your
+  extract configuration file:</p>
+  <pre>
+bld::lib  my-lib
+</pre>
+
+  <p>When a library archive is created successfully, the build system will
+  automatically generate the relevant exclude dependency configurations in the
+  <samp>etc/</samp> sub-directory of the build root. You will be able to
+  include these configurations in subsequent builds that utilise the library.
+  The root names of the configuration files match those of the library archives
+  that you can create in the current build, but the extension <samp>*.a</samp>
+  is replaced with <samp>*.cfg</samp>. For example, the exclude dependency
+  configuration for <samp>libmy-lib.a</samp> is <samp>libmy-lib.cfg</samp>.</p>
+
+  <h3 id="advanced_pp">Pre-processing</h3>
+
+  <p>As most modern compilers can handle pre-processing, the build system
+  leaves pre-processing to the compiler by default. However, it is recognised
+  that there are code written with pre-processor directives that can alter the
+  argument list of procedures and/or their dependencies. If a source file
+  requires pre-processing in such a way, we have to pre-process before running
+  the interface block generator and the dependency scanner. The <code>PP</code>
+  declaration can be used to switch on this pre-processing stage. The
+  pre-processing stage can be switched on globally or for individual
+  sub-packages only. The following is an example, using an extract
+  configuration file:</p>
+  <pre id="example_7">
+# Example 7
+# ----------------------------------------------------------------------
+cfg::type          ext
+cfg::version       1.0
+
+bld::pp::gen       true                  # line 4
+bld::pp::var/foo   true                  # line 5
+
+bld::tool::cppkeys GOOD WEATHER FORECAST # line 7
+bld::tool::fppkeys FOO BAR EGG HAM       # line 8
+
+# ... some other declarations ...
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 4-5</dfn>: these switches on the pre-processing stage for all
+    sub-packages under <samp>gen</samp> and <samp>var/foo</samp>.</li>
+
+    <li><dfn>line 7</dfn>: this declares a list of pre-defined macros
+    <samp>GOOD</samp>, <samp>WEATHER</samp> and <samp>FORECAST</samp> for
+    pre-processing all C files.</li>
+
+    <li><dfn>line 8</dfn>: this declares a list of pre-defined macros
+    <samp>FOO</samp>, <samp>BAR</samp>, <samp>EGG</samp> and <samp>HAM</samp>
+    for pre-processing all Fortran files that require processing.</li>
+  </ul>
+
+  <p>Source files requiring pre-processing may contain <code>#include</code>
+  statements to include header files. For including a local file, its name
+  should be embedded within a pair of quotes, i.e. <samp>'file.h'</samp> or
+  <samp>"file.h"</samp>. If the header file is embedded within a pair of
+  &lt;file.h&gt; angle brackets, the system will assume that the file can be
+  found in a standard location.</p>
+
+  <p>The build system allows header files to be placed anywhere within the
+  declared source tree. The system uses the dependency scanner, as described in
+  the previous sub-section to scan for any header file dependencies. All source
+  files requiring pre-processing and all header files are scanned. Header files
+  that are required are copied to the <samp>inc/</samp> subdirectory of the
+  build root, which is automatically added to the pre-processor search path via
+  the <code>-I&lt;dir&gt;</code> option. The build system uses an internal
+  logic similar to <code>make</code> to perform pre-processing. Header files
+  are only copied to the <samp>inc/</samp> sub-directory if they are used in
+  <code>#include</code> statements.</p>
+
+  <p>Unlike <code>make</code>, which only uses the timestamp to determine
+  whether an item is out of date, the internal logic of the build system does
+  this by inspecting the content of the file as well. In an incremental build,
+  the pre-processed file is only updated if its content has changed. This
+  avoids unnecessary updates (and hence unnecessary re-compilation) in an
+  incremental build if the changed section of the code does not affect the
+  output file.</p>
+
+  <p>Pre-processed code generated during the pre-processing stage are sent to
+  the <samp>ppsrc/</samp> sub-directory of the build root. It will have a
+  relative path that reflects the name of the declared sub-package. The
+  pre-processed source file will have the same root name as the original source
+  file. For C files, the same extension <samp>.c</samp> will be used. For
+  Fortran files, the case of the extension will normally be dropped, e.g. from
+  <samp>.F90</samp> to <samp>.f90</samp>.</p>
+
+  <p>Following pre-processing, the system will use the pre-processed source
+  file as if it is the original source file. The interface generator will
+  generate the interface file using the pre-processed file, the dependency
+  scanner will scan the pre-processed file for dependencies, and the compiler
+  will compile the pre-processed source.</p>
+
+  <p>The <code>TOOL::CPPKEYS</code> and <code>TOOL::FPPKEYS</code> declarations
+  are used to pre-define macros in the C and Fortran pre-processor
+  respectively. This is implemented by the build system using the pre-processor
+  <code>-D</code> option on each word in the list. The use of these
+  declarations are not confined to the pre-process stage. If any source files
+  requiring pre-processing are left to the compiler, the declarations will be
+  used to set up the commands for compiling these source files.</p>
+
+  <p>The <code>TOOL::CPPKEYS</code> and <code>TOOL::FPPKEYS</code> declarations
+  normally applies globally, but like any other <code>TOOL</code> declarations,
+  they can be suffixed with sub-package names. In such cases, the declarations
+  will apply only to the specified sub-packages.</p>
+
+  <dl>
+    <dt>Note - changing pre-processor flags</dt>
+
+    <dd>
+      <p>As for compiler flags, the build system detects changes in
+      pre-processor flags (<code>TOOL::CPPFLAGS</code> and
+      <code>TOOL::FPPFLAGS</code>) and macro definitions
+      (<code>TOOL::CPPKEYS</code> and <code>TOOL::FPPKEYS</code>). If the
+      pre-processor flags or the macro definitions have changed in an
+      incremental build, the system will re-do all the necessary
+      pre-processing. The following hierarchy is followed:</p>
+
+      <ul>
+        <li>If the pre-processor flags or macro definitions for a particular
+        source file change, only that source file will be pre-processed
+        again.</li>
+
+        <li>If the pre-processor flags or macro definitions for a particular
+        container package change, only source files within that container will
+        be pre-processed again.</li>
+
+        <li>If the global pre-processor flags or macro definitions change, all
+        source files will be pre-processed again.</li>
+
+        <li>If the pre-processor command changes, all source files are
+        pre-processed again.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h3 id="advanced_file-type">File type</h3>
+
+  <p>The build system only knows what to do with an input source file if it
+  knows what type of file it is. The type of a source file is normally
+  determined automatically using one of the following three methods (in
+  order):</p>
+
+  <ol>
+    <li>If the file is named with an extension, its extension will be matched
+    against a set of registered file extensions. If a match is found, the file
+    type will be set according to the register.</li>
+
+    <li>If a file does not have an extension or does not match with a
+    registered extension, its name is compared with a set of pre-defined
+    patterns. If a match is found, the file type will be set according to the
+    file type associated with the pattern.</li>
+
+    <li>If the above two methods failed and if the file is a text file, the
+    system will attempt to read the first line of the file. If the first line
+    begins with a <code>#!</code> pattern, the line will be compared with a set
+    of pre-defined patterns. If a match is found, the file type will be set
+    according to the file type associated with the pattern.</li>
+  </ol>
+
+  <p>In addition to the above, if a file is a Fortran or C source file, the
+  system will attempt to open the source file to determine whether it contains
+  a main program, module (Fortran only) or just standalone procedures. All
+  these information will be used later by the build system to process the
+  source file.</p>
+
+  <p>The build system registers a file type with a set of type flags delimited
+  by the double colons <code>::</code>. For example, a Fortran 9X source file
+  is registered as <code>FORTRAN::FORTRAN9X::SOURCE</code>. (Please note that
+  the order of the type flags in the list is insignificant. For example,
+  <code>FORTRAN::SOURCE</code> is the same as <code>SOURCE::FORTRAN</code>.)
+  For a list of all the type flags used by the build system, please see the
+  <a href="annex_bld_cfg.html#infile-ext-types">input file extension type flags
+  table</a> in the <a href="annex_bld_cfg.html">Annex: Declarations in FCM
+  build configuration file</a>.</p>
+
+  <p>The following is a list of default input file extensions and their
+  associated types:</p>
+
+  <dl>
+    <dt><samp>.f .for .ftn .f77</samp></dt>
+
+    <dd><code>FORTRAN::SOURCE</code> Fortran 77 source file (assumed to be
+    fixed format)</dd>
+
+    <dt><samp>.f90 .f95</samp></dt>
+
+    <dd><code>FORTRAN::FORTRAN9X::SOURCE</code> Fortran 9X source file (assumed
+    to be free format)</dd>
+
+    <dt><samp>.F .FOR .FTN .F77</samp></dt>
+
+    <dd><code>FPP::SOURCE</code> Fortran 77 source file (assumed to be fixed
+    format) that requires pre-processing</dd>
+
+    <dt><samp>.F90 .F95</samp></dt>
+
+    <dd><code>FPP::FPP9X::SOURCE</code> Fortran 9X source file (assumed to be
+    free format) that requires pre-processing</dd>
+
+    <dt><samp>.c</samp></dt>
+
+    <dd><code>C::SOURCE</code> C source file</dd>
+
+    <dt><samp>.h .h90</samp></dt>
+
+    <dd><code>CPP::INCLUDE</code> Pre-processor <code>#include</code> header
+    file</dd>
+
+    <dt><samp>.o .obj</samp></dt>
+
+    <dd><code>BINARY::OBJ</code> Compiled binary object</dd>
+
+    <dt><samp>.exe</samp></dt>
+
+    <dd><code>BINARY::EXE</code> Binary executable</dd>
+
+    <dt><samp>.a</samp></dt>
+
+    <dd><code>BINARY::LIB</code> Binary object library archive</dd>
+
+    <dt><samp>.sh .ksh .bash .csh</samp></dt>
+
+    <dd><code>SHELL::SCRIPT</code> Unix shell script</dd>
+
+    <dt><samp>.pl .pm</samp></dt>
+
+    <dd><code>PERL::SCRIPT</code> Perl script</dd>
+
+    <dt><samp>.py</samp></dt>
+
+    <dd><code>PYTHON::SCRIPT</code> Python script</dd>
+
+    <dt><samp>.tcl</samp></dt>
+
+    <dd><code>TCL::SCRIPT</code> Tcl/Tk script</dd>
+
+    <dt><samp>.pro</samp></dt>
+
+    <dd><code>PVWAVE::SCRIPT</code> IDL/PVWave program</dd>
+
+    <dt><samp>.cfg</samp></dt>
+
+    <dd><code>CFGFILE</code> FCM configuration file</dd>
+
+    <dt><samp>.inc</samp></dt>
+
+    <dd><code>FORTRAN::FORTRAN9X::INCLUDE</code> Fortran INCLUDE file</dd>
+
+    <dt><samp>.interface</samp></dt>
+
+    <dd><code>FORTRAN::FORTRAN9X::INCLUDE::INTERFACE</code> Fortran 9X INCLUDE
+    interface block file</dd>
+  </dl>
+
+  <p>N.B. The extension must be unique. For example, the system does not
+  support the use of <samp>.inc</samp> files for both <code>#include</code> and
+  Fortran <code>INCLUDE</code>.</p>
+
+  <p>The following is a list of supported file name patterns and their
+  associated types:</p>
+
+  <dl>
+    <dt><samp>*Scr_* *Comp_* *IF_* *Suite_* *Interface_*</samp></dt>
+
+    <dd><code>SHELL::SCRIPT</code> Unix shell script, GEN-based project naming
+    conventions</dd>
+
+    <dt><samp>*List_*</samp></dt>
+
+    <dd><code>SHELL::SCRIPT::GENLIST</code> Unix shell script, GEN list
+    file</dd>
+
+    <dt><samp>*Sql_*</samp></dt>
+
+    <dd><code>SCRIPT::SQL</code> SQL script, GEN-based project naming
+    conventions</dd>
+  </dl>
+
+  <p>The following is a list of supported <code>#!</code> line patterns and
+  their associated types:</p>
+
+  <dl>
+    <dt><samp>*sh* *ksh* *bash* *csh*</samp></dt>
+
+    <dd><code>SHELL::SCRIPT</code> Unix shell script</dd>
+
+    <dt><samp>*perl*</samp></dt>
+
+    <dd><code>PERL::SCRIPT</code> Perl script</dd>
+
+    <dt><samp>*python*</samp></dt>
+
+    <dd><code>PYTHON::SCRIPT</code> Python script</dd>
+
+    <dt><samp>*tclsh* *wish*</samp></dt>
+
+    <dd><code>TCL::SCRIPT</code> Tcl/Tk script</dd>
+  </dl>
+
+  <p>The build system allows you to add or modify the register for input file
+  extensions and their associated type using the
+  <code>INFILE_EXT::&lt;ext&gt;</code> declaration, where &lt;ext&gt; is a file
+  name extension without the leading dot. If file extension alone is
+  insufficient for defining the type of your source file, you can use the
+  <code>SRC_TYPE::&lt;pcks&gt;</code> declaration, (where &lt;pcks&gt; is the
+  package name of the source file). For example, in an extract configuration
+  file, you may have:</p>
+  <pre id="example_8">
+# Example 8
+# ----------------------------------------------------------------------
+cfg::type                ext
+cfg::version             1.0
+
+bld::infile_ext::foo     CPP::INCLUDE                # line 4
+bld::infile_ext::bar     FORTRAN::FORTRAN9X::INCLUDE # line 5
+bld::src_type::egg/ham.f FORTRAN::FORTRAN9X::INCLUDE # line 6
+
+# ... some other declarations ...
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 4</dfn>: this line registers the extension <samp>.foo</samp>
+    to be of type <code>CPP::INCLUDE</code>. This means that any input files
+    with <samp>.foo</samp> extension will be treated as if they are
+    pre-processor header files.</li>
+
+    <li><dfn>line 5</dfn>: this line registers the extension <samp>.bar</samp>
+    to be of type <code>FORTRAN::FORTRAN9X::INCLUDE</code>. This means that any
+    input file with <samp>.bar</samp> extension will be treated as if they are
+    Fortran 9X INCLUDE files.</li>
+
+    <li><dfn>line 6</dfn>: this line declares the type for the source file in
+    the package <samp>egg::ham.f</samp> to be
+    <code>FORTRAN::FORTRAN9X::INCLUDE</code>. Without this declaration, this
+    file would normally be given the type <code>FORTRAN::SOURCE</code>.</li>
+  </ul>
+
+  <p>The <code>INFILE_EXT</code> declarations deal with extensions of input
+  files. There is also a <code>OUTFILE_EXT::&lt;type&gt;</code> declaration
+  that deals with extensions of output files. The declaration is opposite that
+  of <code>INFILE_EXT</code>. The file &lt;type&gt; is now declared with the
+  label, and the extension is declared as the value. It is worth noting that
+  <code>OUTFILE_EXT</code> declarations use very different syntax for
+  &lt;type&gt;, and the declared extension must include the leading dot. For a
+  list of output types used by the build system, please see the <a href=
+  "annex_bld_cfg.html#outfile-ext-types">output file extension types table</a>
+  in the <a href="annex_bld_cfg.html">Annex: Declarations in FCM build
+  configuration file</a>. An example is given below:</p>
+  <pre id="example_9">
+# Example 9
+# ----------------------------------------------------------------------
+cfg::type                   ext
+cfg::version                1.0
+
+bld::outfile_ext::mod       .MOD   # line 4
+bld::outfile_ext::interface .intfb # line 5
+
+# ... some other declarations ...
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 4</dfn>: this line modifies the extension of compiled Fortran
+    9X module information files from the default <samp>.mod</samp> to
+    <samp>.MOD</samp>.</li>
+
+    <li><dfn>line 5</dfn>: this line modifies the extension of INCLUDE Fortran
+    9X interface block files from the default <samp>.interface</samp> to
+    <samp>.intfb</samp>.</li>
+  </ul>
+
+  <p>N.B. If you have made changes to the file type registers, whether it is
+  for input files or output files, it is always worth re-building your code in
+  full-build mode to avoid unexpected behaviour.</p>
+
+  <h3 id="advanced_inherit">Inherit from a previous build</h3>
+
+  <p>As you can inherit from previous extracts, you can inherit from previous
+  builds. The very same <code>USE</code> statement can be used to declare a
+  build, which the current build will depend on. The only difference is that
+  the declared location must contain a valid build configuration file. In fact,
+  if you use the extract system to obtain your build configuration file, any
+  <code>USE</code> declarations in the extract configuration file will also be
+  <code>USE</code> declarations in the output build configuration file.</p>
+
+  <p>By declaring a previous build with a <code>USE</code> statement, the
+  current build automatically inherits settings from it. The following points
+  are worth noting:</p>
+
+  <ul>
+    <li>Build targets are not normally inherited. However, you can switch on
+    inheritance of build targets using an <code>INHERIT::TARGET</code>
+    declaration, such as:
+      <pre>
+inherit::target  true
+</pre>
+    </li>
+
+    <li>The build root directory and its sub-directories of the inherited build
+    are placed into the search paths. For example, if we have an inherited
+    build at <samp>/path/to/inherited</samp>, and it is used by a build at
+    <samp>/path/to/my_build</samp>, the search path of executable files will
+    become <samp>/path/to/my_build/bin:/path/to/inherited/bin</samp>, so that
+    the <samp>bin/</samp> sub-directory of the current build is searched before
+    the <samp>bin/</samp> sub-directory of the inherited build. If two or more
+    <code>USE</code> statements are declared, the <code>USE</code> statement
+    declared last will have higher priority. For example, if the current build
+    is <var>C</var>, and it USEs build <var>A</var> before build <var>B</var>,
+    the search path will be <samp>C:B:A</samp>.</li>
+
+    <li>Source files are inherited by default. If a source file is declared in
+    the current build that has the same package name as a source file of the
+    inherited build, it will override that in the inherited build. Any source
+    files missing from the current build will be taken from the inherited
+    build.
+
+      <p>You can switch off inheritance of source files using an
+      <code>INHERIT::SRC</code> declaration. This declaration can be suffixed
+      with the name of a sub-package. In such case, the declaration applies
+      only to the inheritance of the sub-package. Otherwise, it applies
+      globally. For example:</p>
+      <pre>
+# Switch off inheritance of source files in the <samp>gen</samp> sub-package
+inherit::src::gen  false
+</pre>
+    </li>
+
+    <li><code>BLOCKDATA</code>, <code>DEP</code>, <code>EXCL_DEP</code>,
+    <code>EXE_DEP</code>, <code>INFILE_EXT</code>, <code>LIB</code>,
+    <code>OUTFILE_EXT</code>, <code>PP</code>, <code>TOOL</code> and
+    <code>SRC_TYPE</code> declarations are automatically inherited. If the same
+    setting is declared in the current incremental build, it overrides the
+    inherited declaration.</li>
+  </ul>
+
+  <p>As an example, suppose we have already performed an extract and build
+  based on the configuration in <a href="#example_2">example 2</a>, we can set
+  up an extract configuration file as follows:</p>
+  <pre id="example_10">
+# Example 10
+# ----------------------------------------------------------------------
+cfg::type            ext
+cfg::version         1.0
+
+use                  $HOME/example               # line 4
+
+dest                 $HOME/example10             # line 6
+
+bld::inherit::target true                        # line 8 
+bld::target          ham.exe egg.exe             # line 9
+
+bld::tool::fflags    -O2 -w                      # line 11
+bld::tool::cflags    -O2                         # line 12
+
+# ... and other declarations for repositories and source directories ...
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 4</dfn>: this line declares a previous extract at
+    <samp>$HOME/example</samp> which the current extract will inherit from. The
+    same line will be written to the output build configuration file. The
+    subsequent build will then inherit from the build at
+    <samp>$HOME/example</samp>.</li>
+
+    <li><dfn>line 6</dfn>: this declares the destination root directory of the
+    current extract, which will become the root directory of the current build.
+    Search paths of the build sub-directories will be set automatically. For
+    example, the search path for executable files created by the current build
+    will be <samp>$HOME/example10/bin:$HOME/example/bin</samp>.</li>
+
+    <li><dfn>line 8</dfn>: this line switches on inheritance of build targets.
+    The build targets in <a href="#example_1">example 1</a>, i.e.
+    <samp>foo.exe</samp> and <samp>bar.exe</samp> will be built as part of the
+    current build.</li>
+
+    <li><dfn>line 9</dfn>: this declares two new build targets
+    <samp>ham.exe</samp> and <samp>egg.exe</samp> to be added to the inherited
+    ones. The default build targets of the current build will now be
+    <samp>foo.exe</samp>, <samp>bar.exe</samp>, <samp>ham.exe</samp> and
+    <samp>egg.exe</samp>.</li>
+
+    <li><dfn>line 11-12</dfn>: these lines modify options used by the Fortran
+    and the C compilers, overriding those inherited from <a href=
+    "#example_1">example 1</a>.</li>
+  </ul>
+
+  <dl>
+    <dt>Build inheritance limitation: handling of include files</dt>
+
+    <dd>
+      <p>The build system uses the compiler/pre-processor's <code>-I</code>
+      option to specify the search path for include files. For example, it uses
+      the option to specify the <samp>inc/</samp> sub-directories of the
+      current build and its inherited build.</p>
+
+      <p>However, some compilers/pre-processors (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>Some compilers (e.g. <code>gfortran</code>) do not behave this way and
+      others (e.g. <code>ifort</code>) have options to prevent include file
+      search in the container directory of the source file. If you are using
+      such a compiler you can avoid the problem for Fortran compilation
+      although this does not fix the problem entirely if you have switched on
+      the pre-processing stage. Otherwise you may have to work around the
+      problem, (e.g. by making a comment change in the source file, or by not
+      using an inherited build at all).</p>
+    </dd>
+  </dl>
+
+  <h3 id="advanced_data">Building data files</h3>
+
+  <p>While the usual targets to be built are the executables associated with
+  source files containing main programs, libraries or scripts, the build system
+  also allows you to build <em>data</em> files. All files with no registered
+  type are considered to be <em>data</em> files. For each container
+  sub-package, there is an automatic target for copying all <em>data</em> files
+  to the <samp>etc/</samp> sub-directory of the build root. The name of the
+  target has the form <code>&lt;pcks&gt;.etc</code>, where &lt;pcks&gt; is the
+  name of the sub-package (with package names delimited by the double
+  underscore <code>__</code>). For example, the target name for sub-package
+  <samp>foo/bar</samp> is <samp>foo__bar.etc</samp>. This target is
+  particularly useful for copying, say, all namelists in a sub-package to the
+  <samp>etc/</samp> sub-directory of the build root.</p>
+
+  <p>At the end of a successful build, if the <samp>etc/</samp> sub-directory
+  is not empty, the <samp>fcm_env.sh</samp> script will export the environment
+  variable <var>FCM_ETCDIR</var> to point to the <samp>etc/</samp>
+  sub-directory. You should be able to use this environment variable to locate
+  your data files.</p>
+
+  <h2 id="verbose">Diagnostic verbose level</h2>
+
+  <p>The amount of diagnostic messages generated by the build system is
+  normally set to a level suitable for normal everyday operation. This is the
+  default diagnostic verbose level 1. If you want a minimum amount of
+  diagnostic messages, you should set the verbose level to 0. If you want more
+  diagnostic messages, you can set the verbose level to 2 or 3. You can modify
+  the verbose level in two ways. The first way is to set the environment
+  variable <var>FCM_VERBOSE</var> to the desired verbose level. The second way
+  is to invoke the build system with the <code>-v &lt;level&gt;</code> option.
+  (If set, the command line option overrides the environment variable.)</p>
+
+  <p>The following is a list of diagnostic output at each verbose level:</p>
+
+  <dl>
+    <dt>Level 0</dt>
+
+    <dd>
+      <ul>
+        <li>Report the time taken at the end of each stage of the build
+        process.</li>
+
+        <li>Run the <code>make</code> command in silent mode.</li>
+      </ul>
+    </dd>
+
+    <dt>Level 1</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at verbose level 0.</li>
+
+        <li>Report the name of the build configuration file.</li>
+
+        <li>Report the location of the build destination.</li>
+
+        <li>Report date/time at the beginning of each stage of the build
+        process.</li>
+
+        <li>Report removed directories.</li>
+
+        <li>Report number of pre-processed files.</li>
+
+        <li>Report number of generated F9X interface files.</li>
+
+        <li>Report number of source files scanned for dependencies.</li>
+
+        <li>Report name of updated <samp>Makefile</samp>.</li>
+
+        <li>Print compiler/linker commands.</li>
+
+        <li>Report total time.</li>
+      </ul>
+    </dd>
+
+    <dt>Level 2</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at verbose level 1.</li>
+
+        <li>For incremental build in archive mode, report the commands used to
+        extract the archives.</li>
+
+        <li>Report creation and removal of directories.</li>
+
+        <li>Report pre-processor commands.</li>
+
+        <li>Print compiler/linker commands with timestamps.</li>
+      </ul>
+    </dd>
+
+    <dt>Level 3</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at verbose level 2.</li>
+
+        <li>Report update of dummy files.</li>
+
+        <li>Report all shell commands.</li>
+
+        <li>Report pre-processor commands with timestamps.</li>
+
+        <li>Report any F9X interface files generated.</li>
+
+        <li>Report number of lines and number of automatic dependencies for
+        each source file which is scanned.</li>
+
+        <li>Run <code>make</code> on normal mode (as opposed to silent
+        mode).</li>
+
+        <li>Report start date/time and time taken of <code>make</code>
+        commands.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h2 id="overview">Overview of the build process</h2>
+
+  <p>The FCM build process can be summarised in five stages. Here is a summary
+  of what is done in each stage:</p>
+
+  <ol>
+    <li><dfn>Parse configuration and setup destination</dfn>: in this
+    pre-requisite stage, the build system parses the configuration file. The
+    <samp>src/</samp> sub-directory is searched recursively for source files.
+    For full builds, it ensures that the sub-directories and files created by
+    the build system are removed. If you invoke <code>fcm build</code> with a
+    <code>--clean</code> option, the system will not go any further.</li>
+
+    <li><dfn>Setup build</dfn>: in this first stage, the system determines
+    whether any settings have changed by using the cache. If so, the cache is
+    updated with the current settings.</li>
+
+    <li><dfn>Pre-process</dfn>: if any files in any source files require
+    pre-processing, they will be pre-processed at this stage. The resulting
+    pre-processed source files will be sent to the <samp>ppsrc/</samp>
+    sub-directory of the build root.</li>
+
+    <li><dfn>Generate dependency</dfn>: the system scans source files of
+    registered types for dependency information. For an incremental build, the
+    information is only updated if a source file is changed. The system then
+    uses the information to write a <samp>Makefile</samp> for the main
+    build.</li>
+
+    <li><dfn>Generate interface</dfn>: if there are Fortran 9X source files
+    with standalone subroutines and functions, the build system generates
+    interface blocks for them. The result of which will be written to the
+    interface files in the <samp>inc/</samp> sub-directory of the build
+    root.</li>
+
+    <li>
+      <dfn>Make</dfn>: the system invokes <code>make</code> on the
+      <samp>Makefile</samp> generated in the previous stage to perform the main
+      build. Following a build, the <em>root</em> directory of the build may
+      contain the following sub-directories (empty ones are removed
+      automatically at the end of the build process):
+
+      <dl>
+        <dt><samp>.cache/.bld/</samp></dt>
+
+        <dd>Cache files, used internally by FCM.</dd>
+
+        <dt><samp>bin/</samp></dt>
+
+        <dd>Executable binaries and scripts.</dd>
+
+        <dt><samp>cfg/</samp></dt>
+
+        <dd>Configuration files.</dd>
+
+        <dt><samp>done/</samp></dt>
+
+        <dd>Dummy <em>done</em> files used internally by the
+        <samp>Makefile</samp> generated by FCM.</dd>
+
+        <dt><samp>etc/</samp></dt>
+
+        <dd>Miscellaneous data files.</dd>
+
+        <dt><samp>flags/</samp></dt>
+
+        <dd>Dummy <em>flags</em> files used internally by the
+        <samp>Makefile</samp> generated by FCM.</dd>
+
+        <dt><samp>inc/</samp></dt>
+
+        <dd>Include files, such as <samp>*.h</samp>, <samp>*.inc</samp>,
+        <samp>*.interface</samp>, and <samp>*.mod</samp>.</dd>
+
+        <dt><samp>lib/</samp></dt>
+
+        <dd>Object library archives.</dd>
+
+        <dt><samp>obj/</samp></dt>
+
+        <dd>Compiled object files.</dd>
+
+        <dt><samp>ppsrc/</samp></dt>
+
+        <dd>Source directories with pre-processed files.</dd>
+
+        <dt><samp>src/</samp></dt>
+
+        <dd>Source directories. This directory is not changed by the build
+        system.</dd>
+
+        <dt><samp>tmp/</samp></dt>
+
+        <dd>Temporary objects and binaries. Files generated by the
+        compiler/linker may be left here.</dd>
+      </dl>
+    </li>
+  </ol>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/code_management.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/code_management.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/code_management.html	(revision 1981)
@@ -0,0 +1,1159 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: Code Management</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - Code Management" />
+  <meta name="keywords" content="FCM, user guide" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Code Management</h1>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="svn">Using Subversion</h2>
+
+  <p>One of the key strengths of Subversion is its documentation. <a href=
+  "http://svnbook.red-bean.com/en/1.4/">Version Control with Subversion</a>
+  (which we'll just refer to as the <cite>Subversion book</cite> from now on)
+  is an excellent book which explains in detail how to use Subversion and also
+  provides a good introduction to all the basic concepts of version control.
+  Rather than trying to write our own explanations (and not doing as good a
+  job) we will simply refer you to the <cite>Subversion book</cite>, where
+  appropriate, for the relevant information.</p>
+
+  <p>In general, the approach taken in this section is to make sure that you
+  first understand how to perform a particular action using the Subversion
+  tools and then describe how this differs using FCM.</p>
+
+  <h3 id="svn_concepts">Basic Concepts</h3>
+
+  <p>In order to use FCM you need to have a basic understanding of version
+  control. If you're not already familiar with Subversion or CVS then please
+  read the chapter <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.basic.html">Fundamental Concepts</a>
+  from the <cite>Subversion book</cite>. In particular, make sure that you
+  understand:</p>
+
+  <ul>
+    <li>The <q title=
+    "http://svnbook.red-bean.com/en/1.4/svn.basic.vsn-models.html#svn.basic.vsn-models.copy-merge">
+    Copy-Modify-Merge</q> approach to file sharing.</li>
+
+    <li>Global Revision Numbers.</li>
+  </ul>
+
+  <p>Note that this chapter states that <q title=
+  "http://svnbook.red-bean.com/en/1.4/svn.basic.in-action.html#svn.basic.in-action.wc">
+  working copies do not always correspond to any single revision in the
+  repository</q>. However, the FCM working practises do not encourage this and
+  the wrapper scripts provided by FCM should ensure that your working copy (a
+  local copy of the repository's files and directories where you can prepare
+  changes) always corresponds to exactly one revision.</p>
+
+  <p><acronym title="Concurrent Versions System">CVS</acronym> users should
+  already be familiar with all the basic concepts. This is not surprising since
+  Subversion was designed as a replacement for CVS and it uses the same
+  development model. However, there are some important differences which may
+  confuse those more familiar with CVS. Fortunately, the appendix in the
+  <cite>Subversion book</cite> <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.forcvs.html">Subversion for CVS
+  Users</a> is specifically written for those moving from CVS to Subversion and
+  you should read this if you are a CVS user.</p>
+
+  <h3 id="svn_basic">Basic Command Line Usage</h3>
+
+  <p>Before we discuss the FCM system you need to have a good understanding of
+  how to perform most of the normal day-to-day tasks using Subversion.
+  Therefore, unless you are already familiar with Subversion, please read the
+  chapter <a href="http://svnbook.red-bean.com/en/1.4/svn.tour.html">Basic
+  Usage</a> from the <cite>Subversion book</cite>.</p>
+
+  <p>So, now you have an understanding of how to do basic tasks using
+  Subversion (you did read the <cite>Basic Usage</cite> chapter didn't you?),
+  how is using FCM different? Well, the key thing to remember is that, instead
+  of using the command <code>svn</code> you need to use the command
+  <code>fcm</code>. The advantages of this are as follows:</p>
+
+  <ul>
+    <li><code>fcm</code> implements all of the commands that <code>svn</code>
+    does (including all the command abbreviations).</li>
+
+    <li>In some cases <code>fcm</code> does very little and basically passes on
+    the command to <code>svn</code>.</li>
+
+    <li>In other cases <code>fcm</code> has a lot of additional functionality
+    compared with the equivalent <code>svn</code> command.</li>
+
+    <li><code>fcm</code> also implements several commands not provided by
+    <code>svn</code>.</li>
+
+    <li><code>fcm</code> provides support for URL and revision keywords.</li>
+
+    <li>Most of the additional features and commands are discussed later in
+    this section or in the following sections.</li>
+  </ul>
+
+  <p>Full details of all the <code>fcm</code> commands available are provided
+  in the <a href="command_ref.html">FCM Command Reference</a> section.</p>
+
+  <h4 id="svn_basic_keywords">URL &amp; Revision Keywords</h4>
+
+  <p>URL keywords can be used to specify URLs in <code>fcm</code> commands. The
+  syntax is <code>fcm:&lt;keyword&gt;</code>. Keywords can be defined globally
+  (see the file <samp>../etc/fcm.cfg</samp> where-ever the <code>fcm</code>
+  command has been installed) or individually in a user configuration file
+  <samp>$HOME/.fcm</samp>. See the <a href="command_ref.html#fcm_config">FCM
+  Command Reference</a> for further details about configuration files.</p>
+
+  <p>For example, if you define a keyword in your configuration file as
+  follows:</p>
+  <pre>
+set::url::fcm      svn://fcm1/FCM_svn/FCM
+</pre>
+
+  <p>then you can abbreviate the URL as in the following examples:</p>
+  <pre>
+# fcm ls svn://fcm1/FCM_svn/FCM
+fcm ls fcm:fcm
+
+# fcm ls svn://fcm1/FCM_svn/FCM/trunk
+fcm ls fcm:fcm_tr  # OR: fcm ls fcm:fcm-tr
+
+# fcm ls svn://fcm1/FCM_svn/FCM/branches
+fcm ls fcm:fcm_br  # OR: fcm ls fcm:fcm-br
+
+# fcm ls svn://fcm1/FCM_svn/FCM/tags
+fcm ls fcm:fcm_tg  # OR: fcm ls fcm:fcm-tg
+</pre>
+
+  <p>Using URL keywords has two advantages.</p>
+
+  <ul>
+    <li>They are shorter and easier to remember.</li>
+
+    <li>If the repository needs to be moved then only the keyword definitions
+    need to be updated (although any working copies you have will still need to
+    be <em>relocated</em> by issuing a <a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.switch.html"><code>fcm
+    switch --relocate</code></a> command).</li>
+  </ul>
+
+  <p>In a similar way, revision keywords can be used to specify revision
+  numbers in <code>fcm</code> commands. The keyword can be used anywhere a
+  revision number can be used. Each keyword is associated with a URL keyword
+  and can only be used when referring to that repository.</p>
+
+  <p>For example, if you define a keyword in your configuration file as
+  follows:</p>
+  <pre>
+set::revision::fcm::vn1.0  112
+</pre>
+
+  <p>then the following commands are equivalent:<br />
+  <code>fcm log -r 112 svn://fcm1/FCM_svn/trunk</code><br />
+  <code>fcm log -r vn1.0 fcm:fcm_tr</code></p>
+
+  <p>You can use the <code>fcm keyword-print</code> command to print all
+  registered URL keywords. You can also print the URL keyword, its implied
+  keywords and the revision keywords of a particular project. For example, to
+  print the keywords for the <samp>UM</samp> project, you can type <code>fcm
+  keyword-print fcm:um</code>.</p>
+
+  <h4 id="svn_basic_diff">Examining Changes</h4>
+
+  <p>Code differences can be displayed graphically using <code>xxdiff</code> by
+  using the <code>--graphical</code> (or <code>-g</code>) option to <code>fcm
+  diff</code>. This option can be used in combination with any other options
+  which are accepted by <code>svn diff</code>.</p>
+
+  <p>An example display from <code>xxdiff</code> is shown below.</p>
+
+  <p class="image"><img src="xxdiff1.png" alt="xxdiff 2-way display" /><br />
+  <code>xxdiff</code> 2-way display</p>
+
+  <p>Points to note:</p>
+
+  <ul>
+    <li>By default <code>xxdiff</code> is configured to show horizontal
+    differences. This means that the parts of the line which have changed are
+    highlighted (e.g. the text <samp>useful</samp> is highlighted in the
+    example above).</li>
+
+    <li>The number shown to the right of each file name shows the current line
+    number. The number on the far right is the number of differences found (2
+    in the example above).</li>
+
+    <li>You may find the following keyboard shortcuts useful.
+
+      <ul>
+        <li><kbd>N</kbd> - move to the next difference</li>
+
+        <li><kbd>P</kbd> - move to the previous difference</li>
+
+        <li><kbd>Ctrl-Q</kbd> - exit</li>
+      </ul>
+    </li>
+
+    <li>If you want to use another diff tool instead of <code>xxdiff</code> to
+    examine changes, you can either set the <var>FCM_GRAPHIC_DIFF</var>
+    environment variable or define the <code>set::tool::graphic_diff</code>
+    setting in your <samp>$HOME/.fcm</samp> file. For example, to use
+    <code>tkdiff</code>, you will do:
+      <pre>
+# EITHER: in bash/ksh:
+(SHELL PROMPT)$ export FCM_GRAPHIC_DIFF=tkdiff
+
+# OR: in your $HOME/.fcm:
+set::tool::graphic_diff  tkdiff
+</pre>
+    </li>
+  </ul>
+
+  <h4 id="svn_basic_conflicts">Resolving Conflicts</h4>
+
+  <p>Your working copy may contain files <em>in conflict</em> as a result of an
+  update or a merge (covered later). Conflicts arise from the situation where
+  two changes being applied to a file <em>overlap</em>. For conflicts in text
+  files, the command <code>fcm conflicts</code> can be used to help resolve
+  them. (A discussion on binary files is given in the section <a href=
+  "working_practices.html#binary">Working with Binary Files</a> later in this
+  document.) The <code>fcm conflicts</code> command calls a graphical merge
+  tool (i.e. <code>xxdiff</code> by default) to display a 3-way diff for each
+  of the files in conflict.</p>
+
+  <p>An example display from <code>xxdiff</code> is shown below.</p>
+
+  <p class="image"><img src="xxdiff2.png" alt="xxdiff 3-way display" /><br />
+  <code>xxdiff</code> 3-way display</p>
+
+  <p>Points to note:</p>
+
+  <ul>
+    <li>The file in the middle is the common ancestor from the merge. The file
+    on the left is your original file and the file on the right is the file
+    containing the changes which you are merging in.</li>
+
+    <li><code>xxdiff</code> is configured to automatically select regions that
+    would end up being selected by an automatic merge (e.g. there are only
+    changes in one of the files). Any difference <em>hunks</em> which cannot be
+    resolved automatically are left <em>unselected</em>.</li>
+
+    <li>Before you can save a merged version you need to go through each
+    unselected difference hunk and decide which text you wish to use.
+
+      <ul>
+        <li>Selecting a diff hunk can be carried out by clicking on it with the
+        left mouse button (or refer to the keyboard shortcuts shown under the
+        <kbd>Region</kbd> menu). The colours update to display which side is
+        selected for output. You can select individual lines with the middle
+        mouse button.</li>
+
+        <li>If you want to select more than one side, you have to invoke the
+        <kbd>Region-&gt;Split/swap/join</kbd> command (keyboard shortcut:
+        <kbd>S</kbd>). This will split the current diff hunk so you can select
+        the pieces you want from both sides. Further invocations of this
+        command will cause swapping of the regions, looping through all the
+        different ordering possibilities, and finally joining the regions again
+        (preserving selections where it is possible).</li>
+      </ul>
+    </li>
+
+    <li>The number on the far right is the number of unselected difference
+    hunks (1 in the example above). Once this number is 0 then you are ready to
+    save the merged file.</li>
+
+    <li>If you want to see how the merged file will look with the current
+    selections then select <kbd>Windows-&gt;Toggle Merged View</kbd> (keyboard
+    shortcut: <kbd>Alt+Y</kbd>). An extra window then appears showing the
+    merged output that updates interactively as you make selections.</li>
+
+    <li>You may find the following keyboard shortcuts useful.
+
+      <ul>
+        <li><kbd>B</kbd> - move to the next unselected hunk</li>
+
+        <li><kbd>O</kbd> - move to the previous unselected hunk</li>
+      </ul>
+    </li>
+
+    <li>There are several different ways to exit the 3-way diff (available from
+    the <kbd>File</kbd> menu):
+
+      <ul>
+        <li>Exit with MERGE (keyboard shortcut: <kbd>M</kbd>) - This saves the
+        merge result. If there are any unselected difference hunks remaining
+        then you will be warned and given the option of saving the file with
+        conflict markers.</li>
+
+        <li>Exit with ACCEPT (keyboard shortcut: <kbd>A</kbd>) - This saves the
+        file you are merging in (i.e. the middle one) as the merge result (i.e.
+        you have <em>accepted</em> all the changes).</li>
+
+        <li>Exit with REJECT (keyboard shortcut: <kbd>R</kbd>) - This saves the
+        original working copy file (i.e. the left one) as the merge result
+        (i.e. you have <em>rejected</em> all the changes).</li>
+      </ul>
+
+      <p>If you just want to exit without making any decisions you can also
+      just close the window.</p>
+    </li>
+
+    <li>For further details please read the <a href=
+    "http://furius.ca/xxdiff/doc/xxdiff-doc.html"><code>xxdiff</code> users
+    manual</a> (available from the <kbd>Help</kbd> menu). In particular, read
+    the section <a href=
+    "http://furius.ca/xxdiff/doc/xxdiff-doc.html#merging-files-and-resolving-conflicts">
+    <em>Merging files and resolving conflicts</em></a>.</li>
+  </ul>
+
+  <p>If you have resolved all the conflicts in a file then you will be prompted
+  on whether to run <code>svn resolved</code> on the file to signal that the
+  file is no longer in conflict.</p>
+  <pre>
+(SHELL PROMPT)$ fcm conflicts
+Conflicts in file: Gen_setup_local1.proc
+You have chosen to ACCEPT all the changes
+Would you like to run "svn resolved"?
+Enter "y" or "n" (or just press &lt;return&gt; for "n"): y
+Resolved conflicted state of 'Gen_setup_local1.proc'
+Conflicts in file: Gen_setup_remote2.proc
+Merge conflicts were not all resolved
+Conflicts in file: Gen_setup_remote3.proc
+All merge conflicts resolved
+Would you like to run "svn resolved"?
+Enter "y" or "n" (or just press &lt;return&gt; for "n"): y
+Resolved conflicted state of 'Gen_setup_remote3.proc'
+</pre>
+
+  <p>It is important to realise that there are some types of merge that
+  <em>xxdiff</em> will not be able to help you with.</p>
+
+  <ul>
+    <li>It you have 2 versions of a file, both with substantial changes to the
+    same piece of code, then the <code>xxdiff</code> display will be extremely
+    colourful and not very helpful.</li>
+
+    <li>In these cases it is often easier to start with one version of the file
+    and manually re-apply the changes from the other version. It might not be
+    obvious how to do this and you may need to speak to the author of the other
+    change to agree how this can be done. Fortunately this situation should be
+    very rare.</li>
+
+    <li>For a more detailed discussion please refer to <a href=
+    "http://software.ericsink.com/scm/scm_file_merge.html">Chapter 3: File
+    Merge</a> in the online book called <a href=
+    "http://software.ericsink.com/scm/source_control.html">Source Control
+    HOWTO</a>.</li>
+  </ul>
+
+  <h4 id="svn_basic_check">Adding and Removing Files</h4>
+
+  <p>If your working copy contains files which are not under version control
+  then you can use the command <code>fcm add --check</code> to add them. This
+  will go through each of the files and prompt to see if you wish to put that
+  file under version control using <code>svn add</code>. For each file you can
+  enter <kbd>y</kbd> for yes, <kbd>n</kbd> for no or <kbd>a</kbd> to assume yes
+  for all following files.</p>
+  <pre>
+(SHELL PROMPT)$ fcm add -c
+?      xxdiff1.png
+?      xxdiff2.png
+?      xxdiff3.png
+?      xxdiff4.png
+Add file 'xxdiff1.png'?
+Enter "y", "n" or "a" (or just press &lt;return&gt; for "n"): y
+A         xxdiff1.png
+Add file 'xxdiff2.png'?
+Enter "y", "n" or "a" (or just press &lt;return&gt; for "n"): n
+Add file 'xxdiff3.png'?
+Enter "y", "n" or "a" (or just press &lt;return&gt; for "n"): a
+A         xxdiff3.png
+A         xxdiff4.png
+</pre>
+
+  <p>Similarly, if your working copy contains files which are missing (i.e. you
+  have deleted them without using <code>svn delete</code>) then you can use the
+  command <code>fcm delete --check</code> to delete them. This will go through
+  each of the files and prompt to see if you wish to remove that file from
+  version control using <code>svn delete</code>.</p>
+
+  <p>As noted in the <a href=
+  "http://subversion.tigris.org/faq.html#wc-change-detection">Subversion
+  FAQ</a>, it can be dangerous using these commands. If you have moved or
+  copied a file then simply adding them would cause the history to be lost.
+  Therefore take care to only use these commands on files which really are new
+  or deleted.</p>
+
+  <h4 id="svn_basic_commit">Committing Changes</h4>
+
+  <p>The command <code>fcm commit</code> should be used for committing changes
+  back to the repository. It differs from the <code>svn commit</code> command
+  in a number of important ways:</p>
+
+  <ul>
+    <li>Your working copy <em>must</em> be up to date. <code>fcm commit</code>
+    will abort if it finds that any files are out of date with respect to the
+    repository. This ensures that your working copy reflects how the repository
+    will be after you have committed your changes.
+
+      <ul>
+        <li>This helps to ensure that any tests you have done prior to
+        committing are valid.</li>
+
+        <li><code>fcm commit</code> is not suitable if you need to commit
+        changes from a working copy containing mixed revisions. However, you
+        are very unlikely to need to do this.</li>
+
+        <li>Actually there is a small chance that your working copy might not
+        be up to date when you commit if someone else is committing some
+        changes at the same time. However, this should very seldom happen and,
+        even if it does, the commit would fail if any of the files being
+        changed became out of date (i.e. it is not possible to lose any
+        changes).</li>
+      </ul>
+    </li>
+
+    <li>If it discovers a file named <samp>#commit_message#</samp> in the top
+    level of your working copy it uses this to provide a template commit
+    message (which you can then edit).
+
+      <ul>
+        <li>If you have performed a merge then a message describing the merge
+        will have been added to this file. It is important that you leave this
+        included in the commit message and do not change its format, as it is
+        used by the <code>fcm branch</code> command.</li>
+
+        <li>You can, if you wish, add entries to this file as you go along to
+        record what changes you have prepared in your working copy. You can
+        also use the command <code>fcm commit --dry-run</code> to allow you to
+        edit the commit message without committing any changes.</li>
+
+        <li><samp>#commit_message#</samp> is ignored by Subversion (so you
+        won't see it show up as an unversioned files when you run <code>fcm
+        status</code>).</li>
+      </ul>
+    </li>
+
+    <li>It always operates from the top of your working copy. If you issue the
+    <code>fcm commit</code> command from a sub-directory of your working copy
+    then it will automatically work out the top directory and work from there.
+
+      <ul>
+        <li>This ensures that any template commit message gets picked up and
+        that you do not, for example, accidently commit a partial set of
+        changes from a merge.</li>
+      </ul>
+    </li>
+
+    <li>It always commits <em>all</em> the changes in your working copy (it
+    does not accept a list of files to commit).
+
+      <ul>
+        <li>Once again, this avoids any danger of accidently committing a
+        partial set of changes.</li>
+
+        <li>You should only work on one change within a working copy. If you
+        need to prepare another, unrelated change then use a separate working
+        copy.</li>
+      </ul>
+    </li>
+
+    <li>It runs <code>svn update</code> after the commit to ensure that your
+    working copy is at the latest revision and to avoid any confusion caused by
+    your working copy containing mixed revisions.</li>
+  </ul>
+  <pre>
+(SHELL PROMPT)$ fcm commit
+Starting editor to create commit message ...
+Change summary:
+------------------------------------------------------------------------
+[Project: GEN]
+[Branch : branches/test/frsn/r123_foo_bar]
+[Sub-dir: &lt;top&gt;]
+
+M      src/code/GenMod_Control/GenMod_Control.f90
+M      src/code/GenMod_Control/Gen_SetupControl.f90
+------------------------------------------------------------------------
+Commit message is as follows:
+------------------------------------------------------------------------
+An example commit.
+------------------------------------------------------------------------
+Would you like to commit this change?
+Enter "y" or "n" (or just press &lt;return&gt; for "n"): y
+Sending        src/code/GenMod_Control/GenMod_Control.f90
+Sending        src/code/GenMod_Control/Gen_SetupControl.f90
+Transmitting file data ..
+Committed revision 170.
+=&gt; svn update
+At revision 170.
+</pre>
+
+  <h3 id="svn_branching">Branching &amp; Merging</h3>
+
+  <p>Branching is a fundamental concept common to most version control systems.
+  For a good introduction please read the chapter <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.branchmerge.html">Branching and
+  Merging</a> from the <cite>Subversion book</cite>. Even if you are already
+  familiar with branching using other version control systems you should still
+  read this chapter to see how branching is implemented in Subversion.</p>
+
+  <p>Having read this chapter from the <cite>Subversion book</cite> you should
+  understand:</p>
+
+  <ul>
+    <li>Why each project directory has sub-directories called <em>trunk</em>,
+    <em>branches</em> and <em>tags</em>. This structure is assumed by
+    <code>fcm</code> (Subversion recommends it but doesn't insist on it).</li>
+
+    <li>That when you make a branch you are taking a copy of the entire project
+    file tree. Fortunately, the design of the Subversion repository means that
+    these copies are <q title=
+    "http://svnbook.red-bean.com/en/1.4/svn.branchmerge.using.html#svn.branchmerge.using.create">
+    cheap</q> - they are quick to create and take very little space.</li>
+
+    <li>That Subversion doesn't (currently) track merge information for you -
+    this has to be done manually.</li>
+
+    <li>That each revision of your repository can also be thought of as a
+    <em>changeset</em>.</li>
+
+    <li>That once a change is committed to a repository it cannot be removed
+    (only reversed). Therefore you must take care not to committ a sensitive
+    document or a large data file unintentionally.</li>
+  </ul>
+
+  <p>FCM provides various commands which make working with branches easier (as
+  described in the following sections).</p>
+
+  <h4 id="svn_branching_create">Creating Branches</h4>
+
+  <p>The command <code>fcm branch --create</code> should be used for creating
+  new branches. It provides a number of features:</p>
+
+  <ul>
+    <li>It applies a standard naming convention for branches. The branch name
+    is automatically constructed for you depending on the option(s) supplied to
+    the command. The full detail of these options are described in the <a href=
+    "command_ref.html#fcm_svn_br">FCM Command Reference &gt; fcm branch</a>
+    section.</li>
+
+    <li>By default, it assumes that you are branching from the last changed
+    revision of the <em>trunk</em>.
+
+      <ul>
+        <li>You can use the <code>--branch-of-branch</code> option if you need
+        to create a branch of a branch. A branch of a branch can be useful in
+        many situations. For example, consider a shared branch used by several
+        members of your team to develop, say, a new science scheme, and you
+        have come up with some different ideas of implementing the scheme. You
+        may want to create a branch of the shared branch to develop your idea
+        before merging it back to the shared branch. Note that you can only
+        merge a branch of a branch with it's parent or with another branch
+        created from the same parent. You can't, for example, merge it with the
+        trunk.</li>
+
+        <li>You can use the <code>--revision &lt;rev&gt;</code> option if you
+        need to create a branch from an earlier revision of the source.</li>
+      </ul>
+    </li>
+
+    <li>Each branch always contains a full copy of the trunk (or its parent
+    branch) - you cannot create a branch from a sub-tree.
+
+      <ul>
+        <li>There would be no reason to only include a sub-tree in a
+        branch.</li>
+      </ul>
+    </li>
+
+    <li>It applies a standard commit message which defines how the branch has
+    been created. If a Trac ticket is specified using the <code>--ticket
+    &lt;number&gt;</code> option, it is added to the commit log message. If you
+    need to add anything to the commit log message, please do so
+    <strong>above</strong> the line that says <samp>--Add your commit message
+    ABOVE - do not alter this line or those below--</samp>.</li>
+  </ul>
+
+  <p>The following is a list of the different types of branches available:</p>
+
+  <dl>
+    <dt>User development branches</dt>
+
+    <dd><samp>branches/dev/&lt;Userid&gt;/&lt;Branch_Name&gt;</samp> These are
+    for changes which are intended to be merged back to the trunk once they are
+    complete. Most branches will belong to this type. e.g.
+    branches/dev/frdm/vn6.1_ImprovedDeepConvection,
+    branches/dev/frdm/r2134_NewBranchNamingConvention.</dd>
+
+    <dt>Shared development branches</dt>
+
+    <dd><samp>branches/dev/Share/&lt;Branch_Name&gt;</samp></dd>
+
+    <dt>User test branches</dt>
+
+    <dd><samp>branches/test/&lt;Userid&gt;/&lt;Branch_Name&gt;</samp> These are
+    for changes which are <em>not</em> intended for the trunk. e.g. Proof of
+    concept work, temporary code written for dealing with a one-off problem,
+    etc.</dd>
+
+    <dt>Shared test branches</dt>
+
+    <dd><samp>branches/test/Share/&lt;Branch_Name&gt;</samp></dd>
+
+    <dt>User packages</dt>
+
+    <dd><samp>branches/pkg/&lt;Userid&gt;/&lt;Branch_Name&gt;</samp> These are
+    branches which combine together a number of different development branches.
+    Sometimes this will simply be for testing purposes (i.e. for testing a
+    branch in combination with other branches). Other times it may be the
+    package which eventually gets merged to the trunk (rather than the
+    development branches). e.g.
+    branches/pkg/frdm/vn6.1_TestImprovedDeepConvection</dd>
+
+    <dt>Shared packages</dt>
+
+    <dd><samp>branches/pkg/Share/&lt;Branch_Name&gt;</samp> E.g.
+    branches/pkg/Share/vn6.1_NewConvectionScheme.</dd>
+
+    <dt>Configurations</dt>
+
+    <dd><samp>branches/pkg/Config/&lt;Branch_Name&gt;</samp> These are major
+    packages which combine together a number of different packages and
+    development branches. e.g. branches/pkg/Config/vn6.1_HadGEM1a.</dd>
+
+    <dt>Releases</dt>
+
+    <dd><samp>branches/pkg/Rel/&lt;Branch_Name&gt;</samp> These may be bug-fix
+    branches for system releases, if required. They can also be branches on
+    which stable releases are prepared if you don't do this on the trunk
+    (although you lose the ability to branch from stable releases if you work
+    this way). e.g. branches/pkg/Rel/vn6.1_BugFixes.</dd>
+  </dl>
+  <pre>
+(SHELL PROMPT)$ fcm br -c -n my_test_branch -k 23 fcm:test
+Starting nedit to create commit message ...
+Change summary:
+------------------------------------------------------------------------
+A    svn://fcm1/repos/OPS/branches/dev/frsn/r118_my_test_branch
+------------------------------------------------------------------------
+Commit message is as follows:
+------------------------------------------------------------------------
+Create an example branch to demonstrate branch creation for the user guide.
+Created /OPS/branches/dev/frsn/r118_my_test_branch from /OPS/trunk@118.
+Relates to ticket #23.
+------------------------------------------------------------------------
+Would you like to go ahead and create this branch?
+Enter "y" or "n" (or just press &lt;return&gt; for "n"): y
+Creating branch svn://fcm1/repos/OPS/branches/dev/frsn/r118_my_test_branch ...
+
+Committed revision 169.
+</pre>
+
+  <h4 id="svn_branching_list">Listing Branches Created by You or Other
+  Users</h4>
+
+  <p>The command <code>fcm branch --list</code> can be used to list the
+  branches you have created at the HEAD of a repository. If you specify the
+  <code>--user &lt;userid&gt;</code> option, the branches created by
+  &lt;userid&gt; are listed instead. You can specify multiple users with
+  multiple <code>--user &lt;userid&gt;</code> options, or with a colon (:)
+  separated list to a single <code>--user &lt;userid:list&gt;</code> option.
+  Note that you can also list shared branches by specifying &lt;userid&gt; as
+  <code>Share</code>, configuration branches by specifying &lt;userid&gt; as
+  <code>Config</code> and release branches by specifying &lt;userid&gt; as
+  <code>Rel</code>. The command returns 0 (success) if one or more branches is
+  found for the specified users, or 1 (failure) if no branch is found.</p>
+  <pre>
+(SHELL PROMPT)$ fcm branch --list fcm:gen
+1 branch found for frsn in svn://fcm1/GEN_svn/GEN
+fcm:GEN-br/dev/frsn/r1191_clean_up/
+(SHELL PROMPT)$ echo $?
+0
+(SHELL PROMPT)$ fcm branch --list --user frbj --user frsn fcm:gen
+2 branches found for frbj, frsn in svn://fcm1/GEN_svn/GEN
+fcm:GEN-br/dev/frbj/r1177_gen_ui_for_scs/
+fcm:GEN-br/dev/frsn/r1191_clean_up/
+(SHELL PROMPT)$ echo $?
+0
+(SHELL PROMPT)$ fcm branch --list --user frva fcm:gen
+0 branch found for frva in svn://fcm1/GEN_svn/GEN
+(SHELL PROMPT)$ echo $?
+1
+</pre>
+
+  <h4 id="svn_branching_info">Getting Information About Branches</h4>
+
+  <p>The command <code>fcm branch --info</code> can be used to get various
+  information about a branch. In particular, it summarises information about
+  merges to and from the branch and its parent.</p>
+  <pre>
+(SHELL PROMPT)$ fcm branch --info
+URL: svn://fcm1/FCM_svn/FCM/branches/dev/frsn/r1346_merge
+Repository Root: svn://fcm1/FCM_svn
+Revision: 1385
+Last Changed Author: frsn
+Last Changed Rev: 1385
+Last Changed Date: 2006-04-20 11:08:45 +0100 (Thu, 20 Apr 2006)
+--------------------------------------------------------------------------------
+Branch Create Author: frsn
+Branch Create Rev: 1354
+Branch Create Date: 2006-04-04 14:27:47 +0100 (Tue, 04 Apr 2006)
+Branch Parent: svn://fcm1/FCM_svn/FCM/trunk@1346
+Last Merge From Parent, Revision: 1444
+Last Merge From Parent, Delta: /FCM/trunk@1439 cf. /FCM/trunk@1395
+Merges Avail From Parent: 1445
+Merges Avail Into Parent: 1453 1452 1449 1446 1444 1443 1441 1434 1397 1396 ...
+</pre>
+
+  <p>If you need information on the current children of the branch, use the
+  <code>--show-children</code> option of the <code>fcm branch --info</code>
+  command. If you need information on recent merges to and from the branch and
+  its siblings, use the <code>--show-siblings</code> option of the <code>fcm
+  branch --info</code> command.</p>
+
+  <p>To find out what changes have been made on a branch relative to its parent
+  you can use the command <code>fcm diff --branch</code>.</p>
+
+  <ul>
+    <li>You can combine this with the options:
+
+      <dl>
+        <dt><code>--graphical</code></dt>
+
+        <dd>to display the differences using a graphical <em>diff</em>
+        tool</dd>
+
+        <dt><code>--trac</code></dt>
+
+        <dd>to display the differences using Trac</dd>
+
+        <dt><code>--wiki</code></dt>
+
+        <dd>to print a wiki syntax suitable for inserting into Trac</dd>
+      </dl>
+    </li>
+
+    <li>The base of the difference is adjusted to account for any merges from
+    the branch to its parent or vice-versa.</li>
+  </ul>
+
+  <h4 id="svn_branching_switch">Switching your working copy to point to another
+  branch</h4>
+
+  <p>The command <code>fcm switch</code> can be used to switch your working
+  copy to point to another branch. For example, if you have a working copy at
+  <samp>$HOME/work</samp>, currently pointing to the trunk or a branch of a
+  project at <samp>svn://fcm1/FCM_svn/FCM/trunk</samp>, you can switch the
+  working copy to point to another branch of same project:</p>
+  <pre>
+(Shell prompt)$ cd $HOME/work
+(Shell prompt)$ fcm sw dev/frsn/r959_blockdata
+-&gt; svn switch --revision HEAD svn://fcm1/FCM_svn/FCM/branches/dev/frsn/r959_blockdata
+U    doc/user_guide/getting_started.html
+U    doc/user_guide/code_management.html
+U    doc/user_guide/command_ref.html
+U    src/lib/Fcm/SrcFile.pm
+U    src/lib/Fcm/Util.pm
+U    src/lib/Fcm/Build.pm
+U    src/lib/Fcm/Cm.pm
+U    src/lib/Fcm/SrcPackage.pm
+U    src/bin/fcm_internal
+U    src/bin/fcm_gui
+Updated to revision 1009.
+</pre>
+
+  <p>Unlike <code>svn switch</code>, <code>fcm switch</code> does extra
+  checking to ensure that your whole working copy is switched to the new branch
+  at the correct level of sub-directory. In addition, you can specify only the
+  <em>branch</em> part of the URL, such as <samp>trunk</samp>,
+  <samp>branches/dev/fred/r1234_bob</samp> or even
+  <samp>dev/fred/r1234_bob</samp> and the command will work out the full URL
+  for you.</p>
+
+  <h4 id="svn_branching_delete">Deleting Branches</h4>
+
+  <p>The command <code>fcm branch --delete</code> can be used to delete
+  branches which are no longer required. Before being asked to confirm that you
+  want to delete the branch, you will first see the same output as from
+  <code>fcm branch --info</code>. This allows you to check, for example,
+  whether your branch is being used anywhere else or whether the latest changes
+  on your branch have been merged to the trunk. You will be prompted to edit
+  your commit log message. If you need to add anything to the commit log
+  message, please do so <strong>above</strong> the line that says <samp>--Add
+  your commit message ABOVE - do not alter this line or those below--</samp>.
+  </p>
+
+  <h4 id="svn_branching_merge">Merging</h4>
+
+  <p>As mentioned earlier, Subversion doesn't track merge information
+  (although, in the longer term, there are plans to add this feature). However,
+  <code>fcm</code> <em>does</em> track a limited amount of merge information.
+  It does this by making a number of assumptions:</p>
+
+  <ul>
+    <li>That all merges are performed using FCM and are identified using a
+    standard template in the commit log message.</li>
+
+    <li>That you only ever merge all the changes available on the source branch
+    up to a chosen point (i.e. you can't only include a subset of the changes
+    made to the branch).</li>
+
+    <li>That the source and target are both branches (or the trunk) in the same
+    FCM project.</li>
+
+    <li>That the source and target are directly related, i.e. they must either
+    have a parent/child relationship or they are siblings from the same parent
+    branch.</li>
+  </ul>
+
+  <p>Note that the term <em>source branch</em> and <em>target branch</em>
+  referred to above can also mean the trunk.</p>
+
+  <p>To perform a merge, use the command <code>fcm merge &lt;source&gt;</code>.
+  This includes a number of important features:</p>
+
+  <ul>
+    <li>If it finds any local modifications in your working copy then it checks
+    whether you wish to continue (in most cases you won't want to mix a merge
+    with other changes).</li>
+
+    <li>It determines the base revision and path of the <em>common
+    ancestor</em> to be used for the merge, taking into account any merges from
+    the <em>source</em> to the <em>target</em> or vice-versa.</li>
+
+    <li>Before doing the merge, (unless you specify the
+    <code>--non-interactive</code> option), it reports what changes will result
+    from performing the merge and checks that you wish to continue.</li>
+
+    <li>It adds details of the merge, using a standard template, into the
+    commit message file (<samp>#commit_message#</samp>). If you need to add any
+    extra comment, you should do so <strong>above</strong> the line that says
+    <samp>--Add your commit message ABOVE - do not alter this line or those
+    below--</samp>.
+
+      <ul>
+        <li>If you decide to revert the merge, you should remove the template
+        line manually from the commit message file, making sure that you do not
+        alter the standard template by accident.</li>
+      </ul>
+    </li>
+  </ul>
+  <pre>
+(SHELL PROMPT)$ fcm merge trunk # merge changes from the trunk into the branch
+Available Merges From /FCM/trunk: 1383 1375
+Please enter the revision you wish to merge from
+  (or just press &lt;return&gt; for "1383"):
+About to merge in changes from /FCM/trunk@1383 compared with /FCM/trunk@1371
+This merge will result in the following changes:
+--------------------------------------------------------------------------------
+A    doc/standards/fortran_standard.html
+U    src/lib/Fcm/ReposBranch.pm
+--------------------------------------------------------------------------------
+Would you like to go ahead with the merge?
+Enter "y" or "n" (or just press &lt;return&gt; for "n"): y
+Performing merge ...
+A    doc/standards/fortran_standard.html
+U    src/lib/Fcm/ReposBranch.pm
+</pre>
+
+  <h3 id="svn_gui">Using the GUI</h3>
+
+  <p>So far, all the tools described have been command line tools. Many people
+  will be happy with these but, for those who prefer it, there is also a simple
+  Graphical User Interface (GUI).</p>
+
+  <h4 id="svn_gui_start">Starting the GUI</h4>
+
+  <p>To run the GUI simply issue the command <code>fcm gui</code> from the
+  directory you want as your working directory. You can also start the GUI from
+  within Konqueror (see <a href="#svn_gui_konqueror">Accessing the GUI from
+  Konqueror</a>).</p>
+
+  <p>The GUI consists of several sections:</p>
+
+  <ul>
+    <li>The top section contains a row of buttons to allow you to select which
+    command you want to run.</li>
+
+    <li>Beneath this is shown the current working directory and the top level
+    directory of your working copy (these may be the same).</li>
+
+    <li>Beneath this come various buttons and entry boxes to allow you to
+    configure the command you have selected. These vary according to the
+    command.</li>
+
+    <li>Beneath this comes a further row of buttons
+
+      <ul>
+        <li><em>Quit</em> - this exits the GUI.</li>
+
+        <li><em>Help</em> - this displays the help message for the selected
+        command.</li>
+
+        <li><em>Clear</em> - this empties the text window.</li>
+
+        <li><em>Run</em> - this allows you to run your command.</li>
+      </ul>
+    </li>
+
+    <li>Beneath this comes a scrolling text window where the output from the
+    commands is displayed.</li>
+
+    <li>The bottom section displays help information when you position the
+    cursor over various parts of the GUI.</li>
+  </ul>
+
+  <p class="image"><img src="gui1.png" alt=
+  "Example GUI screen with the Status commands selected" /><br />
+  Example GUI screen with the <kbd>Status</kbd> commands selected</p>
+
+  <p>If you run a more complicated command, like <code>fcm branch</code>, which
+  prompts for input then extra entry windows will pop up.</p>
+
+  <p class="image"><img src="gui2.png" alt="Example GUI pop-up window" /><br />
+  Example GUI pop-up window</p>
+
+  <h4 id="svn_gui_commands">GUI Commands</h4>
+
+  <p>The commands available from the GUI should be self explanatory. A few
+  points to note:</p>
+
+  <ul>
+    <li>If the current directory is not a working copy, you will only be able
+    to Checkout a working copy or create a branch from the GUI.</li>
+
+    <li>The <kbd>Checkout</kbd> command is only available if you start the GUI
+    in a directory which is not already a working copy. After successfully
+    running a checkout the GUI automatically sets the working directory to the
+    top of this new working copy.</li>
+
+    <li>With some commands (Status, Diff, Add, Delete, Conflicts) you can
+    choose whether to run from the top level of your working copy or from your
+    working directory. With the remaining commands this would not make sense
+    and they can only be run from the top level.</li>
+
+    <li>You can only issue commands from the GUI if they do not need to prompt
+    you for authentication (i.e. the Subversion command can be run with the
+    <code>--non-interactive</code> option).
+
+      <ul>
+        <li>If authentication is required then the command issued by the GUI
+        will fail. For the <code>branch --create</code>, <code>branch
+        --delete</code> and <code>commit</code> commands, which support the
+        <code>--password</code> option, you should specify your password in
+        <kbd>Other options</kbd> and click <kbd>Run</kbd> again. For other
+        commands, you should run the command in interactive mode on the command
+        line. Use the command displayed in the GUI text window but remove the
+        <code>--non-interactive</code> option.</li>
+
+        <li>Most repositories will be configured so that you only need
+        authentication for writing (not reading). Therefore, the first command
+        requiring authentication will probably be creating a branch or
+        commiting to the trunk.</li>
+
+        <li>You should only need to do this the first time you ever issue such
+        a command on a each repository (unless the repository is moved to a new
+        location) since the Subversion client caches this information for
+        future comamnds .</li>
+      </ul>
+    </li>
+  </ul>
+
+  <h4 id="svn_gui_konqueror">Accessing the GUI from Konqueror</h4>
+
+  <p>To enable access from Konqueror run the script
+  <code>fcm_setup_konqueror</code>. You can then use the Konqueror file manager
+  to select the directory which you want as your working directory. To run the
+  GUI click the right mouse button and select <kbd>Open With =&gt; FCM
+  GUI</kbd>.</p>
+
+  <ul>
+    <li>Make sure that your current selection is the directory and not a file
+    within that directory.</li>
+  </ul>
+
+  <p class="image"><img src="konqueror.png" alt=
+  "Running the GUI from within Konqueror" /><br />
+  Running the GUI from within Konqueror</p>
+
+  <h3 id="svn_problems">Known Problems with Subversion</h3>
+
+  <p>There are some limitations with Subversion v1.3 which you should be aware
+  of:</p>
+
+  <ul>
+    <li>The <code>svn rename</code> command is not a true rename/move
+    operation, but is implemented as a copy and delete. As a result, if you
+    rename an item in a branch, and later attempt to merge it back to the
+    trunk, the operation may not be handled correctly by <code>svn merge</code>
+    (see <a href=
+    "http://subversion.tigris.org/issues/show_bug.cgi?id=898">subversion issue
+    898</a> for further details). Support for a <q title=
+    "http://subversion.tigris.org/issues/show_bug.cgi?id=898">true rename</q>
+    will hopefully be available in Subversion in the near future. Until this is
+    implemented, you should avoid renaming of files or directories unless you
+    can ensure that no-one is working in parallel on the affected areas of the
+    project.</li>
+  </ul>
+
+  <h2 id="trac">Using Trac</h2>
+
+  <p><cite>Trac</cite> has a simple and intuitive web interface which is
+  relatively easy to pick up. It also includes a <a href=
+  "http://trac.edgewall.org/wiki/TracGuide">User and Administration Guide</a>
+  which is full of helpful information (and is referred to extensively in this
+  section).</p>
+
+  <p>Trac contains a menu bar at the top of each page (which we will refer to
+  as the <cite>Trac menu</cite>). This provides access to all the main
+  features.</p>
+
+  <h3 id="trac_login">Logging In</h3>
+
+  <p>Although different projects may choose their own rules, we expect that
+  most systems will have Trac configured so that all the information is
+  viewable by anyone. However, in order to make any changes you will need to
+  login. This ensures that any changes are identified with the appropriate
+  userid.</p>
+
+  <p>In the rest of this section it is assumed that you have logged in to Trac
+  and are therefore able to make changes.</p>
+
+  <p>If you haven't yet got a Trac userid (which should be the same as the
+  userid you use for committing changes to Subversion) then please contact your
+  system manager.</p>
+
+  <h3 id="trac_wiki">Using the Wiki Pages</h3>
+
+  <p>A wiki enables documents to be written in a simple markup language using a
+  web browser. See the Trac Guide for information on the <a href=
+  "http://trac.edgewall.org/wiki/TracWiki">Trac Wiki Engine</a>. Make sure that
+  you read the information provided on:</p>
+
+  <ul>
+    <li><a href="http://trac.edgewall.org/wiki/WikiFormatting">Wiki
+    Formatting</a> which explains how to format your wiki pages.</li>
+
+    <li><a href="http://trac.edgewall.org/wiki/WikiPageNames">Wiki Page
+    Names</a> which explains how <em>CamelCase</em> is used to create <a href=
+    "http://trac.edgewall.org/wiki/WikiNewPage">New Wiki Pages</a>.</li>
+
+    <li><a href="http://trac.edgewall.org/wiki/TracLinks">Trac Links</a> which
+    allow hyperlinking between Trac entities (tickets, reports, changesets,
+    Wiki pages, milestones and source files). This is a fundamental feature of
+    Trac which makes it easy, for example, to link a bug report (ticket) to the
+    changeset which fixed the bug (and vice-versa).</li>
+  </ul>
+
+  <p>Whenever you are viewing a wiki page in Trac you should see several
+  buttons at the bottom of the page:</p>
+
+  <ul>
+    <li><kbd>Edit This Page</kbd> - Clicking this will bring up a page where
+    you can edit the page contents. Before saving your changes you can preview
+    how the modified page will appear. You can also leave a comment explaining
+    what changes you made.</li>
+
+    <li><kbd>Attach File</kbd> - Allows you to attach files to a page, e.g. an
+    image.</li>
+
+    <li>If you have admin rights then you will also see
+
+      <ul>
+        <li><kbd>Delete This Version</kbd> - Delete the particular version of
+        the page you are viewing.</li>
+
+        <li><kbd>Delete Page</kbd> - Delete the page and all its history.</li>
+      </ul>Use with care - these operations are irreversible!
+    </li>
+  </ul>
+
+  <p>At the top of each wiki page at the right hand side you can select
+  <kbd>Page History</kbd>. This shows you the full history of each page with
+  details of when each change was made, who made the change and what the
+  changes were.</p>
+
+  <h3 id="trac_browser">Using the Repository Browser</h3>
+
+  <p>The <a href="http://trac.edgewall.org/wiki/TracBrowser">Trac Browser</a>
+  is used to view the contents of your repository. To get to it just select
+  <kbd>Browse Source</kbd> from the Trac menu. You can view directories and
+  files at any version, see their revision histories and view <a href=
+  "http://trac.edgewall.org/wiki/TracChangeset">changesets</a>. Any wiki
+  formatting in log messages is recognised and interpreted so you can easily
+  link a changeset to a Trac ticket by using <a href=
+  "http://trac.edgewall.org/wiki/TracLinks">Trac Links</a>.</p>
+
+  <h3 id="trac_tickets">Using the Issue Tracker</h3>
+
+  <p>The Trac issue database provides a way of tracking issues within a project
+  (e.g. bug reports, feature requests, software support issues, project tasks).
+  Within Trac an issue is often referred to as a <em>Ticket</em>.</p>
+
+  <p>Please refer to the Trac Guide for the following information:</p>
+
+  <ul>
+    <li>
+      <a href="http://trac.edgewall.org/wiki/TracTickets">The Trac Ticket
+      System</a> - Creating and modifying tickets.
+
+      <ul>
+        <li>Only Trac accounts with admin rights can modify ticket
+        descriptions.</li>
+      </ul>
+    </li>
+
+    <li><a href="http://trac.edgewall.org/wiki/TracQuery">Trac Ticket
+    Queries</a> - List tickets matching your chosen criterion.</li>
+  </ul>
+
+  <h3 id="trac_roadmap">Using the Roadmap</h3>
+
+  <p>Each ticket can be assigned to a milestone. The Trac Roadmap can then be
+  used to provide a view on the ticket system. This can useful to see what
+  changes went into a particular system release or what changes are outstanding
+  before a milestone can be reached.</p>
+
+  <p>Please refer to the Trac Guide for further information on the <a href=
+  "http://trac.edgewall.org/wiki/TracRoadmap">Trac Roadmap</a>.</p>
+
+  <ul>
+    <li>Only Trac accounts with admin rights can add, modify and remove
+    milestones using the web interface.</li>
+  </ul>
+
+  <h3 id="trac_timeline">Using the Timeline</h3>
+
+  <p>The <a href="http://trac.edgewall.org/wiki/TracTimeline">Trac Timeline</a>
+  allows you to list all the acitivity on a project over any given period. It
+  can list:</p>
+
+  <ul>
+    <li>Creation and changes to wiki pages.</li>
+
+    <li>Creation, closure and changes to tickets.</li>
+
+    <li>Commits to the Subversion repository.</li>
+
+    <li>Milestones reached.</li>
+  </ul>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/command_ref.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/command_ref.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/command_ref.html	(revision 1981)
@@ -0,0 +1,1554 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: FCM Command Reference</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - FCM Command Reference" />
+  <meta name="keywords" content="FCM, user guide, command reference" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>FCM Command Reference</h1>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="fcm_config">fcm Configuration File</h2>
+
+  <p>The FCM system uses simple line based text files to store configuration
+  settings. All configuration files used by FCM are based on the same
+  principles:</p>
+
+  <ul>
+    <li>All configurations are stored in plain text files.</li>
+
+    <li>A line in a file may contain a configuration setting or a comment.</li>
+
+    <li>Blank lines are ignored.</li>
+
+    <li>A line that begins with a <code>#</code> is a <dfn>comment</dfn>
+    line.</li>
+
+    <li>Each configuration line has a <dfn>label</dfn>, a <dfn>value</dfn> and
+    optionally a trailing <dfn>comment</dfn>. For example:
+      <pre>
+my::label  this is the value  # some comment
+</pre>
+    </li>
+
+    <li>A <dfn>label</dfn> in a configuration line may contain any non-space
+    character. A space character marks the end of the <dfn>label</dfn>.</li>
+
+    <li>Words or fields in the <dfn>label</dfn> are delimited by a double colon
+    <code>::</code> or slash <code>/</code>. To improve readability, the
+    convention is to only use slash as the delimiter when referring to package
+    names.</li>
+
+    <li>The first non-space character after the <dfn>label</dfn> is the
+    beginning of the <dfn>value</dfn>. The <dfn>value</dfn> may contain space
+    characters. The newline character or the character sequence <code>" #
+    "</code> marks the end of the <dfn>value</dfn>.</li>
+
+    <li>The first non-space character after the character sequence <code>" #
+    "</code> at the end of the <dfn>value</dfn> is the <dfn>comment</dfn>. The
+    <dfn>comment</dfn> is normally ignored by the parser of the configuration
+    file.</li>
+
+    <li>If it is indicated in the documentation that a declaration is expecting
+    a <code>true</code> or <code>false</code> value, the following can all be
+    used to indicate a value of <code>false</code>: an empty string, the
+    numeric <code>0</code>, the string <code>off</code> or the string
+    <code>no</code>. All other values will be considered
+    <code>true</code>.</li>
+
+    <li>If the last character of the <dfn>value</dfn> is a backslash
+    <code>\</code>, the next non-comment line will be the continuation of the
+    current line. Please note that trailing spaces before the continuation mark
+    are preserved, and leading spaces are removed from the beginning of a
+    continuation. If you want to have leading spaces in a continued line, start
+    the line with a backslash <code>\</code> before the leading spaces. It is
+    also worth bearing in mind that the backslash <code>\</code> character is
+    only significant if it appears at the end of a value or the beginning of a
+    continuation line. It is not a special character if it appears elsewhere.
+    For example:
+      <pre>
+foo bar\
+    egg\
+    ham
+# will become:
+foo bareggham
+
+foo bar \
+    egg \
+    ham
+# will become:
+foo bar egg ham
+
+foo bar\
+    \ egg\
+    \ ham
+# will become:
+foo bar egg ham
+
+foo bar\
+    \ egg\ham
+# will become:
+foo bar egg\ham
+</pre>
+    </li>
+  </ul>
+
+  <p>The FCM central and user configuration files can be used to add or modify
+  some of the default settings of FCM. When the <code>fcm</code> command is
+  invoked, it normally attempts to search for a central configuration file at
+  <samp>$BINDIR/../etc/fcm.cfg</samp> and then <samp>$BINDIR/fcm.cfg</samp>,
+  where <samp>$BINDIR</samp> is the container directory of the <code>fcm</code>
+  command. If a central configuration file can be located, the settings in the
+  file will replace the pre-defined ones. After searching/reading the central
+  configuration file, the system will attempt to search for a user
+  configuration file located at <samp>$HOME/.fcm</samp> of the current user. If
+  such a file can be found, its settings will replace the pre-defined ones as
+  well as those defined in the central configuration file.</p>
+
+  <p>For information on the valid entries in the central and user configuration
+  files, please refer to the <a href="annex_fcm_cfg.html">Annex: Declarations
+  in FCM central/user configuration file</a>.</p>
+
+  <h2 id="fcm_bld">fcm build</h2>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm build [OPTIONS...] [CFGFILE]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm build</code> invokes the FCM build system.</p>
+
+      <p>The path to a valid build configuration file <var>CFGFILE</var> may be
+      provided as either a URL or a pathname. Otherwise, the build system
+      searches the default locations for a build configuration file.</p>
+
+      <p>If no option is specified, the system uses the <code>-s 5 -t all -j 1
+      -v 1</code> by default.</p>
+
+      <dl>
+        <dt><code>-a [--archive]</code></dt>
+
+        <dd>This option can be specified to switch on the archive mode. In
+        archive mode, sub-directories produced by the build will be archived in
+        <code>tar</code> format at the end of a successful build. This option
+        should not be used if the current build is intended to be re-used as a
+        pre-compiled build.</dd>
+
+        <dt><code>--clean</code></dt>
+
+        <dd>If this option is specified, the build system will parse the
+        configuration file, remove contents generated by the build system in
+        the destination and exit.</dd>
+
+        <dt><code>-f [--full]</code></dt>
+
+        <dd>If this option is specified, the build system will attempt to
+        perform a full/clean build by removing any previous build files.
+        Otherwise, the build system will attempt to perform an incremental
+        build where appropriate.</dd>
+
+        <dt><code>--ignore-lock</code></dt>
+
+        <dd>When the build system is invoked, it sets a lock file in the build
+        root directory to prevent other extracts/builds taking place in the
+        same location. The lock file is normally removed when the build system
+        exits. (However, a lock file may be left behind if the user interrupts
+        the command, e.g. by typing <kbd>Ctrl-C</kbd>.) You can bypass the
+        check for lock files by using this option.</dd>
+
+        <dt><code>-j [--jobs] arg</code></dt>
+
+        <dd>This option can be used to specify the number of parallel jobs that
+        can be handled by the <code>make</code> command. The argument
+        <var>arg</var> must be a natural integer to represent the number of
+        jobs. If not specified, the default is to perform serial
+        <code>make</code> (i.e. 1 job).</dd>
+
+        <dt><code>-s [--stage] arg</code></dt>
+
+        <dd>
+          This option can be used to limit the actions performed by the build
+          system, up to a named stage determined by the argument
+          <var>arg</var>. If not specified, the default is 5. The stages are:
+
+          <ul>
+            <li><dfn>1, s or setup</dfn>: Stage 1, read configuration and set
+            up the build</li>
+
+            <li><dfn>2, pp or pre_process</dfn>: Stage 2, perform
+            pre-processing for source files that require pre-processing</li>
+
+            <li><dfn>3, gd or generate_dependency</dfn>: Stage 3, scan source
+            files for dependency information and generate <code>make</code>
+            rules for them</li>
+
+            <li><dfn>4, gi or generate_interface</dfn>: Stage 4, generate
+            interface files for Fortran 9X source files</li>
+
+            <li><dfn>5, m or make</dfn>: Stage 5, invoke the <code>make</code>
+            command to build the project</li>
+          </ul>
+        </dd>
+
+        <dt><code>-t [--targets] arg</code></dt>
+
+        <dd>This option can be used to specify the targets to be built. The
+        argument <var>arg</var> must be a colon-separated list of valid
+        targets. If not specified, the default to be built is the
+        <samp>all</samp> target.</dd>
+
+        <dt><code>-v [--verbose] arg</code></dt>
+
+        <dd>This option can be specified to alter the level of diagnostic
+        output. The argument <var>arg</var> to this option must be an integer
+        greater than or equal to 0. The verbose level increases with this
+        number. If not specified, the default verbose level is 1.</dd>
+      </dl>
+
+      <p>For further details, please refer to the chapter on <a href=
+      "build.html">The Build System</a>.</p>
+    </dd>
+
+    <dt>Alternate Names</dt>
+
+    <dd>bld</dd>
+  </dl>
+
+  <h2 id="fcm_ext">fcm extract</h2>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm extract [OPTIONS...] [CFGFILE]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm extract</code> invokes the FCM extract system.</p>
+
+      <p>The path to a valid extract configuration file <var>CFGFILE</var> may
+      be provided as either a URL or a pathname. Otherwise, the extract system
+      searches the default locations for an extract configuration file.</p>
+
+      <dl>
+        <dt><code>--clean</code></dt>
+
+        <dd>If this option is specified, the extract system will parse the
+        configuration file, remove contents generated by previous extract in
+        the destination and exit.</dd>
+
+        <dt><code>-f [--full]</code></dt>
+
+        <dd>If this option is specified, the extract system will attempt to
+        perform a full extract by removing any previous extracted files.
+        Otherwise, the extract system will attempt to perform an incremental
+        extract where appropriate.</dd>
+
+        <dt><code>--ignore-lock</code></dt>
+
+        <dd>When the extract system is invoked, it sets a lock file in the
+        extract destination root directory to prevent other extracts/builds
+        taking place in the same location. The lock file is normally removed
+        when the extract system exits. (However, a lock file may be left behind
+        if the user interrupts the command, e.g. by typing <kbd>Ctrl-C</kbd>.)
+        You can bypass the check for lock files by using this option.</dd>
+
+        <dt><code>-v [--verbose] arg</code></dt>
+
+        <dd>This option can be specified to alter the level of diagnostic
+        output. The argument <var>arg</var> to this option must be an integer
+        greater than or equal to 0. The verbose level increases with this
+        number. If not specified, the default verbose level is 1.</dd>
+      </dl>
+
+      <p>For further details, please refer to the chapter on <a href=
+      "extract.html">The Extract System</a>.</p>
+    </dd>
+
+    <dt>Alternate Names</dt>
+
+    <dd>ext</dd>
+  </dl>
+
+  <h2 id="fcm_cmp-ext-cfg">fcm cmp-ext-cfg</h2>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm cmp-ext-cfg [--verbose (-v) arg] [--wiki (-w) arg] CFG1
+    CFG2</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm cmp-ext-cfg</code> compares the extract configurations of
+      two similar extract configuration files <var>CFG1</var> and
+      <var>CFG2</var>. It reports repository branches and source directories
+      that are declared in one file but not another. If a source directory is
+      declared in both files, it compares their versions. If they differ, it
+      uses <code>svn log</code> to obtain a list of revision numbers at which
+      changes are made to the source directory. It then reports, for each
+      declared repository branch, the revisions at which changes occur in their
+      declared source directories.</p>
+
+      <p>The list of revisions for each declared repository branch is normally
+      printed out as a simple list in plain text.</p>
+
+      <dl>
+        <dt><code>--verbose ARG</code></dt>
+
+        <dd>You can use this option to print the log of each revision, by
+        setting <var>ARG</var> to 2.</dd>
+
+        <dt><code>--wiki</code></dt>
+
+        <dd>Alternatively, you can use this option to change that into an
+        tabular output suitable for inserting into a Trac wiki page. This
+        option must be specified with an argument, which must be the Subversion
+        URL or FCM URL keyword of a FCM project associated with the intended
+        Trac system. The URL allows the command to work out the correct wiki
+        syntax to use.</dd>
+      </dl>
+    </dd>
+  </dl>
+
+  <h2 id="fcm_gui">fcm gui</h2>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm gui [DIR]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm gui</code> starts up the FCM GUI. If <var>DIR</var> is
+      specified then this is used as the working directory.</p>
+
+      <p>For further details, please refer to the section <a href=
+      "code_management.html#svn_gui">Using the GUI</a>.</p>
+    </dd>
+  </dl>
+
+  <h2 id="fcm_keyword-print">fcm keyword-print</h2>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm keyword-print [TARGET]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p>If no argument is specified, <code>fcm keyword-print</code> prints all
+      the registered FCM location keywords. Otherwise, it prints the location
+      and revision keywords according to the argument <var>TARGET</var>, which
+      must be a FCM URL keyword, a Subversion URL or a path to a Subversion
+      working copy.</p>
+    </dd>
+
+    <dt>Alternate Names</dt>
+
+    <dd>kp</dd>
+  </dl>
+
+  <h2 id="fcm_svn">FCM Code Management Commands</h2>
+
+  <p>This section describes all of the code management commands supported by
+  <code>fcm</code>.</p>
+
+  <ul>
+    <li>In some cases <code>fcm</code> simply passes the command directly on to
+    <code>svn</code> (after expanding any URL keywords). These commands are
+    listed in the <a href="#fcm_svn_other">Other Code Management Commands</a>
+    section.</li>
+
+    <li>Where <code>fcm</code> adds functionality these commands are discussed
+    individually.</li>
+
+    <li>In all cases, all the command abbreviations supported by
+    <code>svn</code> work with <code>fcm</code>.</li>
+  </ul>
+
+  <h3 id="fcm_svn_add">fcm add</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm add --check (-c)</code><br />
+    <code>fcm add &lt;any valid <em>svn add</em> options&gt;</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm add</code> supports all of the switches and arguments
+      supported by <code>svn add</code> (refer to the <a href=
+      "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.add.html">Subversion
+      book</a> for details).</p>
+
+      <p>In addition, <code>fcm add</code> supports a <code>--check</code>
+      switch (no other switches or arguments). When this is specified then
+      <code>fcm</code> checks for any files which are not currently under
+      version control (i.e. those marked with a <samp>?</samp> by <code>svn
+      status</code>) and prompts to see if you wish to schedule them for
+      addition at the next commit (using <code>svn add</code>).</p>
+
+      <p>For further details refer to the section <a href=
+      "code_management.html#svn_basic_check">Adding and Removing Files</a>.</p>
+    </dd>
+  </dl>
+
+  <h3 id="fcm_svn_br">fcm branch</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm branch [--info (-i)] [&lt;info-options&gt;]
+    [TARGET]</code><br />
+    <code>fcm branch --delete (-d) [&lt;info-options&gt;]
+    [&lt;commit-options&gt;] [TARGET]</code><br />
+    <code>fcm branch --create (-c) --name (-n) arg [--revision arg]
+    [&lt;create-options&gt;] [&lt;commit-options&gt;] [TARGET]</code><br />
+    <code>fcm branch --list (-l) [&lt;branch-list-options&gt;]
+    [TARGET]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p>If <var>TARGET</var> is specified, it must either be a URL or a path
+      to a local working copy. Otherwise, the current working directory must be
+      a working copy. For <code>--info</code> and <code>--delete</code>, the
+      specified URL or that of the working copy must be a valid branch
+      (including the trunk) in a standard FCM project. For
+      <code>--create</code> and <code>--list</code>, it must be a valid URL of
+      a standard FCM project.</p>
+
+      <dl>
+        <dt><code>fcm branch --info</code></dt>
+
+        <dd>
+          <p>Displays information about a branch. This is the default if no
+          options are specified. It performs the following actions:</p>
+
+          <ul>
+            <li>It reports the basic information of the branch URL, as returned
+            by <code>svn info</code>.</li>
+
+            <li>If <code>--verbose</code> is set, it also prints the log
+            message of the last change revision.</li>
+
+            <li>If the URL is not the trunk:
+
+              <ul>
+                <li>It reports the branch creation information, including the
+                revision, author and date. It also reports the parent URL@REV
+                of the branch. If <code>--verbose</code> is set, it prints the
+                log message of the branch creation revision.</li>
+
+                <li>If the branch does not exist at the HEAD, it reports the
+                revision at which it is deleted.</li>
+
+                <li>It reports the last merges into and from the parent branch.
+                If <code>--verbose</code> is set, it also prints the log
+                message of these merges.</li>
+
+                <li>It reports the revisions available for merging into and
+                from the parent branch. If <code>--verbose</code> is set, it
+                also prints the log message of these revisions.</li>
+              </ul>
+            </li>
+
+            <li>If <code>--show-children</code> is specified, it lists the
+            current children of the branch and their create revisions. Where
+            appropriate, it reports the revision of each child, which is last
+            merged from/into the current branch. It also reports the available
+            merges from/into each child into the current branch.</li>
+
+            <li>If <code>--show-siblings</code> is specified, it reports recent
+            merges from/into sibling branches. It also reports the available
+            merges from/into sibling branches where recent merges are detected.
+            If <code>--verbose</code> is set, it also prints the log message of
+            these merges.</li>
+
+            <li>If <code>--show-other</code> is specified, it reports all
+            custom and reverse merges into the current branch.</li>
+
+            <li>You can turn on <code>--show-children</code>,
+            <code>--show-siblings</code> and <code>--show-other</code>
+            simultaneously by specifying <code>--show-all</code>.</li>
+          </ul>
+
+          <p>For further details refer to the section <a href=
+          "code_management.html#svn_branching_info">Getting Information About
+          Branches</a>.</p>
+        </dd>
+
+        <dt><code>fcm branch --delete</code></dt>
+
+        <dd>
+          <p>Deletes a branch. This command performs the following actions:</p>
+
+          <ul>
+            <li>Firstly, it provides exactly the same output as <code>fcm
+            branch --info</code>.</li>
+
+            <li>If you do not specify the <code>--non-interactive</code>
+            option, it starts an editor (using a similar convention as <a href=
+            "#fcm_svn_ci">commit</a>) to allow you to add further comment to
+            the commit log message. A standard commit log template and change
+            summary is provided for you below the line that says <samp>--Add
+            your commit message ABOVE - do not alter this line or those
+            below--</samp>. If you need to add any extra message to the log,
+            please do so <strong>above</strong> this line. When you exit the
+            editor, the command will report the commit log before prompting for
+            confirmation that you wish to proceed with deleting the branch (it
+            aborts if not).</li>
+          </ul>
+
+          <p>If you specify the <code>--non-interactive</code> option, the
+          command will not prompt you for anything. (The
+          <code>--svn-non-interactive</code> option is set automatically when
+          you specify <code>--non-interactive</code>.)</p>
+
+          <p>Subversion may prompt you for authentication if it is the first
+          time you access the repository. The command fails if the
+          authentication fails. If you specify the
+          <code>--svn-non-interactive</code> option, Subversion will not prompt
+          you for authentication. In such case, the command will simply fail if
+          authentication is required. You can use the <code>--password</code>
+          option to specify a password. Please note that the
+          <code>--svn-non-interactive</code> option is always specified if you
+          are running <code>branch --delete</code> from the FCM GUI. If
+          authentication is required, you should specify your password using
+          the <code>--password</code> option in <kbd>Other options</kbd>.</p>
+
+          <p>For further details refer to the section <a href=
+          "code_management.html#svn_branching_delete">Deleting
+          Branches</a>.</p>
+        </dd>
+
+        <dt><code>fcm branch --create</code></dt>
+
+        <dd>
+          <p>Creates a new branch.</p>
+
+          <p>You have to choose a name for your branch. This must be specified
+          as the argument of the <code>--name (-n)</code> option. The name of
+          the branch must contain only characters in the set
+          [A-Za-z0-9_-.].</p>
+
+          <p>You can specify the type of branch you are creating using the
+          <code>--type (-t)</code> option. The argument to the option must be
+          one of the following:</p>
+
+          <dl>
+            <dt><code>DEV::USER</code></dt>
+
+            <dd>A development branch for the current user (e.g.
+            <samp>branches/dev/&lt;user_id&gt;/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>DEV::SHARE</code></dt>
+
+            <dd>A shared development branch (e.g.
+            <samp>branches/dev/Share/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>DEV</code></dt>
+
+            <dd>Same as <code>DEV::USER</code></dd>
+
+            <dt><code>TEST::USER</code></dt>
+
+            <dd>A test branch for the current user (e.g.
+            <samp>branches/test/&lt;user_id&gt;/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>TEST::SHARE</code></dt>
+
+            <dd>A shared test branch (e.g.
+            <samp>branches/test/Share/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>TEST</code></dt>
+
+            <dd>Same as <code>TEST::USER</code></dd>
+
+            <dt><code>PKG::USER</code></dt>
+
+            <dd>A package branch for the current user (e.g.
+            <samp>branches/pkg/&lt;user_id&gt;/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>PKG::SHARE</code></dt>
+
+            <dd>A shared package branch (e.g.
+            <samp>branches/pkg/Share/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>PKG::CONFIG</code></dt>
+
+            <dd>A configuration branch (e.g.
+            <samp>branches/pkg/Config/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>PKG::REL</code></dt>
+
+            <dd>A release branch (e.g.
+            <samp>branches/pkg/Rel/&lt;branch_name&gt;</samp>)</dd>
+
+            <dt><code>PKG</code></dt>
+
+            <dd>Same as <code>PKG::USER</code></dd>
+
+            <dt><code>CONFIG</code></dt>
+
+            <dd>Same as <code>PKG::CONFIG</code></dd>
+
+            <dt><code>REL</code></dt>
+
+            <dd>Same as <code>PKG::REL</code></dd>
+
+            <dt><code>SHARE</code></dt>
+
+            <dd>Same as <code>DEV::SHARE</code></dd>
+
+            <dt><code>USER</code></dt>
+
+            <dd>Same as <code>DEV::USER</code></dd>
+          </dl>
+
+          <p>If the <code>--type</code> option is not specified, it defaults to
+          DEV::USER.</p>
+
+          <p>Your branch name will normally be prefixed by the revision number
+          from which it is branched. (E.g. if the branch name is
+          <samp>my_branch</samp> and you are branching from revision 123 of the
+          trunk, the final name will be <samp>r123_my_branch</samp>.) If this
+          revision number is associated with a revision keyword, the keyword
+          will be used in place of the revision number. (E.g. if revision 123
+          is associated with the keyword vn6.1, <samp>r123_my_branch</samp>
+          will become <samp>vn6.1_my_branch</samp>.) You can alter this
+          behaviour using the argument to the <code>--rev-flag</code> option.
+          If <code>NORMAL</code> is specified, it uses the default behaviour.
+          If <code>NUMBER</code> is specified, it will always use the revision
+          number as the prefix, regardless of whether the revision number is
+          defined as a keyword or not. If <code>NONE</code> is specified, it
+          will not add a prefix to your branch name.</p>
+
+          <p>The command will normally create your branch from the last changed
+          revision of the trunk of the specified project.</p>
+
+          <ul>
+            <li>You can use the <code>--revision</code> option to specify an
+            earlier revision of the source.</li>
+
+            <li>If the source URL is a valid URL of a branch in a standard FCM
+            project, you can use the <code>--branch-of-branch</code> option to
+            create a branch of the source branch.</li>
+          </ul>
+
+          <p>The <code>--ticket</code> option can be used to specify one or
+          more Trac ticket numbers, which the branch relates to. Multiple
+          ticket numbers can be set by specifying this option multiple times,
+          or by using a comma-separated list of ticket numbers as the argument
+          to the option. If set, the line <samp>Relates to ticket
+          #&lt;number&gt;[, #&lt;number&gt;...]</samp> will be added to the
+          template commit log.</p>
+
+          <p>If you specify the <code>--non-interactive</code> option, the
+          command will not prompt you for anything. (The
+          <code>--svn-non-interactive</code> option is set automatically when
+          you specify <code>--non-interactive</code>.)</p>
+
+          <p>Subversion may prompt you for authentication if it is the first
+          time you access the repository. The command fails if the
+          authentication fails. If you specify the
+          <code>--svn-non-interactive</code> option, Subversion will not prompt
+          you for authentication. In such case, the command will simply fail if
+          authentication is required. You can use the <code>--password</code>
+          option to specify a password. Please note that the
+          <code>--svn-non-interactive</code> option is always specified if you
+          are running <code>branch --create</code> from the FCM GUI. If
+          authentication is required, you should specify your password using
+          the <code>--password</code> option in <kbd>Other options</kbd>.</p>
+
+          <p>This command performs the following actions:</p>
+
+          <ul>
+            <li>It determines the last changed revision of the trunk/source
+            branch at the HEAD (or the specified) revision.</li>
+
+            <li>It constructs the branch name from the option you have
+            specified and reports it.</li>
+
+            <li>It checks that the chosen branch name does not currently exist.
+            If so, the command aborts with an error.</li>
+
+            <li>If you do not specify the <code>--non-interactive</code>
+            option, it starts an editor (using a similar convention as <a href=
+            "#fcm_svn_ci">commit</a>) to allow you to add further comment to
+            the commit log message. A standard commit log template and change
+            summary is provided for you below the line that says <samp>--Add
+            your commit message ABOVE - do not alter this line or those
+            below--</samp>. If you need to add any extra message to the log,
+            please do so <strong>above</strong> this line. When you exit the
+            editor, the command will report the commit log before prompting for
+            confirmation that you wish to proceed (it aborts if not).</li>
+
+            <li>It uses <code>svn mkdir</code> to create any sub-directories
+            required for the copy to succeed.</li>
+
+            <li>It uses <code>svn copy</code> to create the branch.</li>
+          </ul>
+
+          <p>For further details refer to the section <a href=
+          "code_management.html#svn_branching_create">Creating
+          Branches</a>.</p>
+        </dd>
+
+        <dt><code>fcm branch --list</code></dt>
+
+        <dd>
+          <p>Lists the branches created by you (and/or other users) at the HEAD
+          revision of a standard FCM project.</p>
+
+          <p>By default, it lists the branches created by you at the HEAD
+          revision. To display the branches at a different revision, you can
+          use the <code>--revision arg</code> option.</p>
+
+          <p>You can specify a list of users with the <code>--user arg</code>
+          option, where <var>arg</var> is a colon separated list of users.
+          (Alternatively, you can specify this option multiple times.) When
+          this option is set, the command lists the branches created by the
+          specified list of users instead. Note that you can also list shared
+          branches by specifying the user as <code>Share</code>, configuration
+          branches by specifying the user as <code>Config</code> and release
+          branches by specifying the user as <code>Rel</code>.</p>
+
+          <p>You can list all branches in the project by specifying the
+          <code>--show-all</code> option. (This option overrides the
+          <code>--user arg</code> option.)</p>
+
+          <p>By default, it lists the branches in the FCM URL keyword format.
+          If you want to print the full Subversion URL of the branches, you can
+          use the <code>--verbose</code> option.</p>
+
+          <p>The command returns 0 (success) if one or more branches is found
+          for the specified users, or 1 (failure) if no branch is found.</p>
+        </dd>
+      </dl>
+    </dd>
+
+    <dt>Alternate Names</dt>
+
+    <dd>br</dd>
+  </dl>
+
+  <h3 id="fcm_svn_ci">fcm commit</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm commit [--dry-run] [--svn-non-interactive] [--password arg]
+    [PATH]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm commit</code> sends changes from your working copy in the
+      current working directory (or from <var>PATH</var> if it is specified) to
+      the repository.</p>
+
+      <p>This command performs the following actions:</p>
+
+      <ul>
+        <li>It checks that the current working directory (or <var>PATH</var> if
+        it is specified) is a working copy. (If not, it aborts with an
+        error).</li>
+
+        <li>It always commits from the top level of the working copy.</li>
+
+        <li>It checks that there are no files in conflict, missing or out of
+        date (it aborts if there are).</li>
+
+        <li>It checks that any files which have been added have the
+        <var>svn:executable</var> property set correctly (in case a script was
+        added before the execute bit was set correctly).</li>
+
+        <li>It reads in any existing commit message.
+
+          <ul>
+            <li>The commit message is stored in the file
+            <samp>#commit_message#</samp> in the top level of your working
+            copy.</li>
+          </ul>
+        </li>
+
+        <li>It adds the following line to the commit log message: <samp>--Add
+        your commit message ABOVE - do not alter this line or those
+        below--</samp>. This line, and anything below it, is automatically
+        ignored by <code>svn commit</code>. If you need to add any extra message
+        to the log, please do so <strong>above</strong> this line.</li>
+
+        <li>If you have run the <a href="fcm_svn_merge">merge</a> command
+        before the commit, you will get a standard commit log template below
+        a line that says <samp>--FCM message (will be inserted
+        automatically)--</samp>. Please do not try to alter this message (your
+        changes will be ignored if you do).</li>
+
+        <li>It adds current status information to the commit message showing
+        the list of modifications below a line that says <samp>--Change summary
+        (not part of commit message)--</samp>.</li>
+
+        <li>It starts an editor to allow you to edit the commit message.
+
+          <ul>
+            <li>If defined, the environment variable <var>SVN_EDITOR</var>
+            specifies the editor.</li>
+
+            <li>Otherwise the environment variable <var>VISUAL</var> specifies
+            the editor.</li>
+
+            <li>Otherwise the environment variable <var>EDITOR</var> specifies
+            the editor.</li>
+
+            <li>Otherwise the editor <code>nedit</code> is used.</li>
+          </ul>
+        </li>
+
+        <li>It reports the commit message that will be sent to Subversion and
+        then asks if you want to proceed (it aborts if not).</li>
+
+        <li>It calls <code>svn commit</code> to send the changes to the
+        repository.</li>
+
+        <li>It calls <code>svn update</code> to bring your working copy up to
+        the new revision.</li>
+      </ul>
+
+      <p>Subversion may prompt you for authentication if it is the first time
+      you access the repository. The command fails if the authentication fails.
+      If you specify the <code>--non-interactive</code> option, Subversion will
+      not prompt you for authentication. In such case, the command will simply
+      fail if authentication is required. You can use the
+      <code>--password</code> option to specify a password. Please note that
+      the <code>--svn-non-interactive</code> option is always specified if you
+      are running <code>commit</code> from the FCM GUI. If authentication is
+      required, you should specify your password using the
+      <code>--password</code> option in <kbd>Other options</kbd>.</p>
+
+      <p>The <code>--dry-run</code> switch prevents the command from committing
+      any changes. This can be used to allow you to add notes to your commit
+      message whilst you are still preparing your change.</p>
+
+      <p>For further details refer to the section <a href=
+      "code_management.html#svn_basic_commit">Committing Changes</a>.</p>
+    </dd>
+
+    <dt>Alternate Names</dt>
+
+    <dd>ci</dd>
+  </dl>
+
+  <h3 id="fcm_svn_cf">fcm conflicts</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm conflicts [PATH]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm conflicts</code> helps you to resolve any text files in your
+      working copy which have conflicts by using the graphical merge tool
+      <code>xxdiff</code>. If <var>PATH</var> is set, it must be a working
+      copy, and the command will operate in it. If <var>PATH</var> is not set,
+      the command will operate in your current working directory.</p>
+
+      <p>This command performs the following actions:</p>
+
+      <ul>
+        <li>For each text file reported as being in conflict (i.e. marked with
+        a <samp>C</samp> by <code>svn status</code>) it calls
+        <code>xxdiff</code>.</li>
+
+        <li>If <code>xxdiff</code> reports all conflicts resolved then if asks
+        if you wish to run <code>svn resolved</code> on that file.</li>
+      </ul>
+
+      <p>For further details refer to the section <a href=
+      "code_management.html#svn_basic_conflicts">Resolving Conflicts</a>.</p>
+    </dd>
+
+    <dt>Alternate Names</dt>
+
+    <dd>cf</dd>
+  </dl>
+
+  <h3 id="fcm_svn_rm">fcm delete</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm delete --check (-c)</code><br />
+    <code>fcm delete &lt;any valid <em>svn delete</em> options&gt;</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm delete</code> supports all of the switches, arguments and
+      alternate names supported by <code>svn delete</code> (refer to the
+      <a href=
+      "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.delete.html">Subversion
+      book</a> for details).</p>
+
+      <p>In addition, <code>fcm delete</code> supports a <code>--check</code>
+      switch (no other switches or arguments). When this is specified then
+      <code>fcm</code> checks for any files which are missing (i.e. marked with
+      a <samp>!</samp> by <code>svn status</code>) and prompts to see if you
+      wish to schedule them for deletion at the next commit (using <code>svn
+      delete</code>).</p>
+
+      <p>Subversion may prompt you for authentication if it is the first time
+      you access the repository. The command fails if the authentication fails.
+      If you specify the <code>--non-interactive</code> option, Subversion will
+      not prompt you for authentication. In such case, the command will simply
+      fail if authentication is required. Please note that the
+      <code>--non-interactive</code> option is automatically specified if you
+      are running <code>delete</code> from the FCM GUI and you have not checked
+      the box <kbd>Check for files and directories...</kbd>. If authentication
+      is required, you should run <code>delete</code> in interactive mode on a
+      command line.</p>
+
+      <p>For further details refer to the section <a href=
+      "code_management.html#svn_basic_check">Adding and Removing Files</a>.</p>
+    </dd>
+  </dl>
+
+  <h3 id="fcm_svn_diff">fcm diff</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm diff --branch (-b) [--graphical (-g) | --summarise | --wiki |
+    --trac (-t)] [TARGET]</code><br />
+    <code>fcm diff [--graphical (-g) | --summarise | &lt;any valid <em>svn
+    diff</em> options&gt;]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm diff</code> supports all of the switches, arguments and
+      alternate names supported by <code>svn diff</code> (refer to the <a href=
+      "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.diff.html">Subversion
+      book</a> for details). In addition, <code>fcm diff</code> supports the
+      following switches:</p>
+
+      <dl>
+        <dt><code>--graphical (-g)</code></dt>
+
+        <dd>If this option is specified, the command uses a graphical tool to
+        display the differences. (The default graphical diff tool is
+        <code>xxdiff</code>, but you can alter the behaviour by following the
+        instruction discussed in the sub-section on <a href=
+        "code_management.html#svn_basic_diff">Examining Changes</a>.) This
+        switch can be used in combination with all other valid switch
+        (including <code>--branch</code>) except <code>--diff-cmd</code>,
+        <code>--extensions</code>, <code>--trac</code> and
+        <code>--wiki</code>.</dd>
+
+        <dt><code>--summarise</code></dt>
+
+        <dd>This option is implemented in FCM as a wrapper to the Subversion
+        <code>--summarize</code> option. It prints only a summary of the
+        results.</dd>
+
+        <dt><code>--branch</code></dt>
+
+        <dd>
+          If this option is specified, the command displays the differences
+          between the target branch and its parent. This should show you the
+          differences which you would get if you tried to merge the changes in
+          the branch into its parent. It performs the following actions:
+
+          <ul>
+            <li>If <var>TARGET</var> is specified, it must either be a URL or a
+            path to a local working copy. Otherwise, the current working
+            directory must be a working copy. The specified URL or that of the
+            working copy must be a valid branch in a standard FCM project.</li>
+
+            <li>It determines the base of the branch relative to its parent.
+            This is adjusted to account for any merges from the branch to its
+            parent or vice-versa.</li>
+
+            <li>It reports what path and revision it is comparing against.</li>
+
+            <li>If <code>--trac</code> is specified, it launches Trac with your
+            default web browser to display it. Note: if <var>TARGET</var> is a
+            working copy, local changes in it will not be displayed by
+            Trac.</li>
+
+            <li>If <code>--wiki</code> is specified, it prints a Trac wiki
+            syntax for the differences between the base and the specified
+            branch.</li>
+
+            <li>Otherwise, it calls <code>svn diff</code> to report the
+            differences between the base and the specified branch (or working
+            copy).</li>
+          </ul>
+        </dd>
+      </dl>
+
+      <p>For further details refer to the section <a href=
+      "code_management.html#svn_basic_diff">Examining Changes</a>.</p>
+    </dd>
+  </dl>
+
+  <h3 id="fcm_svn_merge">fcm merge</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm merge [--dry-run] [--non-interactive] [--verbose (-v)]
+    SOURCE</code><br />
+    <code>fcm merge --custom --revision N[:M] [--dry-run] [--non-interactive]
+    [--verbose (-v)] SOURCE</code><br />
+    <code>fcm merge --custom [--dry-run] [--non-interactive] [--verbose (-v)]
+    URL1[@REV1] URL2[@REV2]</code><br />
+    <code>fcm merge --reverse --revision [M:]N [--dry-run] [--non-interactive]
+    [--verbose (-v)]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm merge</code> allows you to merge changes from a source into
+      your working copy.</p>
+
+      <p>Before it begins, the command does the following:</p>
+
+      <ul>
+        <li>If a <var>SOURCE</var> or <var>URL</var> is specified, it can be a
+        full URL or a partial URL starting at the branches, trunk or tags
+        level.
+
+          <ul>
+            <li>If a partial URL is given, and the path name does not begin
+            with <samp>trunk</samp>, <samp>tags</samp> or <samp>branches</samp>
+            then <samp>branches/</samp> is automatically added to the beginning
+            of your path.</li>
+          </ul>
+        </li>
+
+        <li>It determines the <var>TARGET</var> URL by examining your working
+        copy.</li>
+
+        <li>If the current directory is not the top of your working copy, it
+        changes the current directory to the top of your working copy.</li>
+
+        <li>If your working copy is not pointing to a branch of a project
+        managed by FCM, the command aborts with an error.</li>
+
+        <li>If you do not specify the <code>--non-interactive</code> option, it
+        checks for any local modifications in your working copy. If it finds
+        any it reports them and asks you to confirm that you wish to continue
+        (it aborts if not).</li>
+      </ul>
+
+      <dl>
+        <dt>Automatic mode (i.e. neither <code>--custom</code> nor
+        <code>--reverse</code> is specified)</dt>
+
+        <dd>
+          <p>Automatic merges are used to merge changes between two directly
+          related branches, (i.e. the branches must either be created from the
+          same parent or have a parent/child relationship). These merges are
+          tracked by FCM and can be used by subsequent FCM commands. The merge
+          delta is calculated by doing the following:</p>
+
+          <ul>
+            <li>It checks that the <var>SOURCE</var> and <var>TARGET</var> are
+            directly related.</li>
+
+            <li>It determines the base revision and path of the <em>common
+            ancestor</em> of the <var>SOURCE</var> and <var>TARGET</var>.</li>
+
+            <li>The base revision and path are adjusted to account for any
+            merges from the <var>SOURCE</var> to the <var>TARGET</var> or
+            vice-versa.</li>
+
+            <li>It reports the revisions from <var>SOURCE</var> available for
+            merging into <var>TARGET</var>. If the <code>--verbose</code>
+            option is set, it prints the log for these revisions. It aborts if
+            no revision is available for merging.</li>
+
+            <li>If there are 2 or more revisions available for merging and you
+            do not specify the <code>--non-interactive</code> target, it asks
+            you which revision of the <var>SOURCE</var> you wish to merge from.
+            The default is the last changed revision of the <var>SOURCE</var>.
+            The merge delta is between the base and the specified revision of
+            the <var>SOURCE</var>.</li>
+
+            <li>If your working copy is a sub-tree of the <var>TARGET</var>, it
+            ensures that the <var>SOURCE</var> contains only changes in the
+            same sub-tree. Otherwise, the merge is unsafe, and the command will
+            abort with an error.
+
+              <p>N.B.: The command looks for changes in the <var>SOURCE</var>
+              by going through the list of changed files since the the
+              <var>SOURCE</var> was last merged into the <var>TARGET</var>. (If
+              there is no previous merge from SOURCE to <var>TARGET</var>, the
+              common ancestor is used.) It is worth noting that there are
+              situations when the command will regard your merge as
+              <em>unsafe</em> (and so will fail incorrectly) even if the
+              changes in the <var>SOURCE</var> outside of the current sub-tree
+              will result in a null merge. This can happen if the changes are
+              the results of a previous merge from the <var>TARGET</var> to the
+              <var>SOURCE</var> or if these changes have been reversed. In such
+              case, you will have to perform your merge in a working copy of a
+              full tree.</p>
+            </li>
+          </ul>
+        </dd>
+
+        <dt>Custom mode (i.e. <code>--custom</code> is specified)</dt>
+
+        <dd>
+          <p>The custom mode is useful if you need to merge changes selectively
+          from another branch. The custom mode can be used in two forms:</p>
+
+          <ul>
+            <li>In the first form, you must specify a <var>SOURCE</var> as well
+            as a revision (range) using the <code>--revision</code> option. If
+            you specify a single revision <var>N</var>, the merge delta is
+            between revision <var>N - 1</var> and revision <var>N</var> of the
+            SOURCE. Otherwise, the merge delta is between revision <var>N</var>
+            and revision <var>M</var>, where <var>N</var> &lt;
+            <var>M</var>.</li>
+
+            <li>In the second form, you must specify two URLs. The merge delta
+            is simply between the two URLs. (For each URL, if you do not
+            specify a peg revision, the command will peg the URL with its last
+            changed revision.)</li>
+          </ul>
+
+          <p>N.B. Unlike automatic merges, custom merges are not tracked or
+          used by subsequent FCM <code>diff</code> or <code>merge</code>
+          commands, (although <code>branch --info</code> can be set to report
+          them). Custom merges are always allowed, even if your working copy is
+          pointing to a sub-tree of a branch. However, there is no checking
+          mechanism to ensure the safety of your sub-tree custom merge so you
+          should only do this if you are confident it is what you want.
+          Therefore, it is recommended that you use automatic merges where
+          possible, and use custom merges only if you know what you are
+          doing.</p>
+        </dd>
+
+        <dt>Reverse mode (i.e. <code>--reverse</code> is specified)</dt>
+
+        <dd>
+          <p>The reverse mode is useful if you need to reverse a changeset (or
+          a range of changesets) in the current branch of the working copy. In
+          this mode, you must specify a revision (range) using the
+          <code>--revision</code> option. If you specify a single revision
+          <var>N</var>, the merge delta is between revision <var>N</var> and
+          revision <var>N - 1</var> of the current branch. Otherwise, the merge
+          delta is between revision <var>M</var> and revision <var>N</var>,
+          where <var>M</var> &gt; <var>N</var>.</p>
+
+          <p>N.B. Like custom merges, reverse merges are not tracked or used by
+          subsequent FCM <code>diff</code> or <code>merge</code> commands,
+          (although <code>branch --info</code> can be set to report them).
+          Likewise, reverse merges in sub-trees are always allowed, although
+          there is no checking mechanism to ensure the safety of your sub-tree
+          reverse merge.</p>
+        </dd>
+      </dl>
+
+      <p>Once the merge delta is determined, the command performs the
+      following:</p>
+
+      <ul>
+        <li>If you set the <code>--dry-run</code> option or if you are running
+        in the interactive mode, it reports what changes will result from
+        performing this merge by calling <code>svn merge --dry-run</code>.
+
+          <ul>
+            <li>It prints the actual <code>svn merge --dry-run</code> command
+            if the <code>--verbose</code> option is specified.</li>
+
+            <li>If you specify the <code>--dry-run</code> option, it exits
+            after reporting what changes will result from performing the
+            merge.</li>
+          </ul>
+        </li>
+
+        <li>If you are running in the interactive mode, it asks if you want to
+        go ahead with the merge (it aborts if not).</li>
+
+        <li>It performs the merge by calling <code>svn merge</code> to apply
+        the delta between the base and the <var>SOURCE</var> on your working
+        copy.
+
+          <ul>
+            <li>It prints the actual <code>svn merge</code> command if the
+            <code>--verbose</code> option is specified.</li>
+          </ul>
+        </li>
+
+        <li>It adds a standard template into the commit message to provide
+        details of the merge. The template is written below the line that says
+        <samp>--FCM message (will be inserted automatically)--</samp>. The
+        <a href="fcm_svn_commit">fcm commit</a> command will detect the
+        existence of the template, so that you will not be able to alter it
+        by accident.
+
+          <ul>
+            <li>The commit message is stored in the file
+            <samp>#commit_message#</samp> in the top level of your working
+            copy. It is created by the merge command if it does not already
+            exist.</li>
+          </ul>
+        </li>
+      </ul>
+
+      <p>For further details refer to the section <a href=
+      "code_management.html#svn_branching_merge">Merging</a>.</p>
+    </dd>
+  </dl>
+
+  <h3 id="fcm_svn_mkpatch">fcm mkpatch</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm mkpatch [OPTIONS] URL [OUTDIR]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm mkpatch</code> creates patches from the specified revisions
+      of the specified <var>URL</var>, which must be a branch URL of a valid
+      FCM project. If the <var>URL</var> is a sub-directory of a branch, it
+      will use the root of the branch.</p>
+
+      <p>If <var>OUTDIR</var> is specified, the output is sent to
+      <var>OUTDIR</var>. Otherwise, the output will be sent to a default
+      location in the current directory (<samp>$PWD/fcm-mkpatch-out/</samp>).
+      The output directory will contain the patch for each revision as well as
+      a script for importing the patch.</p>
+
+      <p>If a revision is specified using the <code>--revision</code> 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).</p>
+
+      <p>The <code>--exclude</code> option can be used to exclude a path in the
+      URL. The specified path must be a relative path of the URL. Glob patterns
+      such as <code>*</code> and <code>?</code> 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. Multiple
+      paths can be specified by using a colon-separated list of paths, or by
+      specifying this option multiple times.</p>
+
+      <p>The <code>--organisation</code> 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 <samp>ticket:123</samp>
+      with <samp>$organisation_ticket:123</samp>, and report the orginal
+      changeset with a message such as
+      <samp>$organisation_changeset:1000</samp>. If the organisation name is
+      not specified then it defaults to <samp>original</samp>.</p>
+
+      <p>Within the output directory are the <em>patches</em> and the log
+      message file for each revision. It also contains a generated script
+      <code>fcm-import-patch</code> for importing the patches. The user of the
+      script can invoke the script with either a URL or a working copy
+      argument, and the script will attempt to import the patches into the
+      given URL or working copy.</p>
+
+      <p>It is worth noting that changes in Subversion properties, including
+      changes in executable permissions, are not handled by the import
+      script.</p>
+    </dd>
+  </dl>
+
+  <h3 id="fcm_svn_switch">fcm switch</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm switch [OPTIONS] URL [PATH]</code><br />
+    <code>fcm switch --relocate [OPTIONS] FROM TO [PATH]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm switch</code> supports the arguments and alternate names
+      supported by <code>svn switch</code>. If <code>--relocate</code> is
+      specified, it supports all options supported by <code>svn switch</code>.
+      Otherwise, it supports <code>--non-interactive</code>, <code>-r
+      [--revision]</code> and <code>-q [--quiet]</code> only. (Please refer to
+      the <a href=
+      "http://svnbook.red-bean.com/en/1.4/svn.branchmerge.switchwc.html">Subversion
+      book</a> for details).</p>
+
+      <p>If <code>--relocate</code> is specified, FCM will pass the options and
+      arguments directly to the corresponding Subversion command. Otherwise,
+      FCM will ensure that your working copy switches safely through the
+      following actions:</p>
+
+      <ul>
+        <li>If <var>PATH</var> (or the current working directory if
+        <var>PATH</var> is not specified) is not at the top of a working copy,
+        the command will automatically search for the top of the working copy,
+        and the switch command will always apply recursively from that
+        level.</li>
+
+        <li>You can specify only the <em>branch</em> part of the URL, such as
+        <samp>trunk</samp>, <samp>branches/dev/fred/r1234_bob</samp> or even
+        <samp>dev/fred/r1234_bob</samp> and the command will work out the full
+        URL for you.</li>
+
+        <li>If you do not specify the <code>--non-interactive</code> option, it
+        checks for any local modifications in your working copy. If it finds
+        any it reports them and asks you to confirm that you wish to continue
+        (it aborts if not).</li>
+
+        <li>If you have some template messages in the
+        <samp>#commit_message#</samp> file in the top level of your working
+        copy, (e.g. after you have performed a merge), the command will report
+        an error. You should remove the template message manually from the
+        <samp>#commit_message#</samp> file before re-running
+        <code>switch</code>.</li>
+
+        <li>The command will analyse the current working copy URL and the
+        specified URL to ensure that they are in the same project. If your
+        working copy is a sub-tree of a project, the command will assume that
+        you want the same sub-tree in the new URL.</li>
+      </ul>
+
+      <p>For further details refer to the section <a href=
+      "code_management.html#svn_branching_switch">Switching your working copy
+      to point to another branch</a>.</p>
+    </dd>
+  </dl>
+
+  <h3 id="fcm_svn_trac">fcm trac</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm trac [--browser (-b) ARG] [PATH]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm trac</code> invokes the web-browser to launch the
+      corresponding URL of the web-based repository browser (currently Trac
+      browser) to view the Subversion repository specified by
+      <var>PATH</var>.</p>
+
+      <p>If the <code>--browser</code> option is specified, <var>ARG</var> must
+      be a valid command to a web browser. If this option is not specified, the
+      default is to use <code>firefox</code>, or whatever setting you have
+      declared in the user configuration file (<samp>$HOME/.fcm</samp>) using
+      the label <code>SET::MISC::WEB_BROWSER</code>. For example:</p>
+      <pre>
+set::misc::web_browser  mozilla
+</pre>
+
+      <p>If <var>PATH</var> is specified, it must be a path to a local working
+      copying, a Subversion URL or a FCM URL keyword. If <var>PATH</var> is not
+      specified, it is set to <samp>.</samp>, the current working directory. If
+      <var>PATH</var> is a directory in the local file system, the command will
+      determine whether it is a working copy. If so, its associated Subversion
+      URL will be used. The command fails if the directory is not a working
+      copy. If <var>PATH</var> is a Subversion URL or a FCM URL keyword, the
+      URL can be <em>pegged</em> with a revision number using the
+      <code>@</code> symbol. For example, to view the trunk of the FCM
+      repository at revision 400, you can use <samp>fcm:fcm_tr@400</samp>. The
+      URL declared by or associated with <var>PATH</var> must also be
+      associated with a Trac browser URL, which is declared using the
+      <code>SET::TRAC::&lt;pck&gt;</code> label in the FCM central/user
+      configuration file. The command fails if an associated Trac browser URL
+      is not found.</p>
+    </dd>
+
+    <dt>Alternate Names</dt>
+
+    <dd>www</dd>
+  </dl>
+
+  <h3 id="fcm_svn_update">fcm update</h3>
+
+  <dl>
+    <dt>Usage</dt>
+
+    <dd><code>fcm update [OPTIONS] [PATH ...]</code></dd>
+
+    <dt>Description</dt>
+
+    <dd>
+      <p><code>fcm update</code> supports the arguments and alternate names
+      supported by <code>svn update</code>. It supports the options
+      <code>--non-interactive</code>, <code>-r [--revision]</code> and
+      <code>-q [--quiet]</code> only. (Please refer to the <a href=
+      "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.update.html">Subversion
+      book</a> for details).</p>
+
+      <p>FCM will ensure that your working copies updates safely through the
+      following actions:</p>
+
+      <ul>
+        <li>If <var>PATH</var> (or the current working directory if
+        <var>PATH</var> is not specified) is not at the top of a working copy,
+        the command will automatically search for the top of the working copy,
+        and the update command will always apply recursively from that
+        level.</li>
+
+        <li>If you do not specify the <code>--non-interactive</code> option, it
+        uses <code>svn status --show-updates</code> to display what will be
+        updated in your working copies and to check for local modifications (if
+        you specify <code>-r [--revision]</code> then it just uses <code>svn
+        status</code>). If it finds any it reports them and asks you to confirm
+        that you wish to continue (it aborts if not).</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h3 id="fcm_svn_other">Other Code Management Commands</h3>
+
+  <p>Other <code>svn</code> commands are supported by <code>fcm</code> without
+  any change in functionality, with the following minor enhancements:</p>
+
+  <ul>
+    <li>Where appropriate, FCM performs repository and revision keywords
+    expansion.</li>
+
+    <li>The <code>fcm checkout</code> command fails if you attempt to checkout
+    into an existing working copy.</li>
+
+    <li>FCM prints the corresponding <code>svn</code> command, except for
+    <code>cat</code> and any commands with the <code>--xml</code> option
+    specified.</li>
+  </ul>
+
+  <p>The following is a list of the commands:</p>
+
+  <ul>
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.blame.html">svn
+    blame</a></li>
+
+    <li><a href="http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.cat.html">svn
+    cat</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.checkout.html">svn
+    checkout</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.cleanup.html">svn
+    cleanup</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.copy.html">svn
+    copy</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.export.html">svn
+    export</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.import.html">svn
+    import</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.info.html">svn
+    info</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.list.html">svn
+    list</a></li>
+
+    <li><a href="http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.log.html">svn
+    log</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.lock.html">svn
+    lock</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.mkdir.html">svn
+    mkdir</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.move.html">svn
+    move</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.propdel.html">svn
+    propdel</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.propedit.html">svn
+    propedit</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.propget.html">svn
+    propget</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.proplist.html">svn
+    proplist</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.propset.html">svn
+    propset</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.resolved.html">svn
+    resolved</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.revert.html">svn
+    revert</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.status.html">svn
+    status</a></li>
+
+    <li><a href=
+    "http://svnbook.red-bean.com/en/1.4/svn.ref.svn.c.unlock.html">svn
+    unlock</a></li>
+  </ul>
+
+  <p>Please refer to the <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.ref.html">Subversion Complete
+  Reference</a> in the Subversion book for details of these commands.</p>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/extract.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/extract.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/extract.html	(revision 1981)
@@ -0,0 +1,1151 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: The Extract System</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - The Extract System" />
+  <meta name="keywords" content="FCM, user guide, extract, build" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>The Extract System</h1>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="introduction">Introduction</h2>
+
+  <p>The extract system provides an interface between the revision control
+  system (currently Subversion) and the build system. Where appropriate, it
+  extracts code from the repository and other user-defined locations to a
+  directory tree suitable for feeding into the build system. In this chapter,
+  we shall use many examples to explain how to use the extract system. At the
+  end of this chapter, you will be able to extract code from the local file
+  system as well as from different branches of different repository URLs. You
+  will also learn how to mirror code to an alternate destination. Finally, you
+  will be given an introduction on how to specify configurations for the build
+  system via the extract configuration file. (For further information on the
+  build system, please see the next chapter <a href="build.html">The Build
+  System</a>.) The last section of the chapter tells you what you can do in the
+  case when Subversion is not available.</p>
+
+  <h2 id="command">The Extract Command</h2>
+
+  <p>To invoke the extract system, simply issue the command:</p>
+  <pre>
+fcm extract
+</pre>
+
+  <p>By default, the extract system searches for an extract configuration file
+  <samp>ext.cfg</samp> in <samp>$PWD</samp> and then <samp>$PWD/cfg</samp>. If
+  an extract configuration file is not found in these directories, the command
+  fails with an error. If an extract configuration file is found, the system
+  will use the configuration specified in the file to perform the current
+  extract.</p>
+
+  <p>If the destination of the extract does not exist, the system performs a
+  new full extract to the destination. If a previous extract already exists at
+  the destination, the system performs an incremental extract, updating any
+  modifications if necessary. If a full (fresh) extract is required for
+  whatever reason, you can invoke the extract system using the <code>-f</code>
+  option, (i.e. the command becomes <code>fcm extract -f</code>). If you simply
+  want to remove all the items generated by a previous extract in the
+  destination, you can invoke the extract system using the <code>--clean</code>
+  option.</p>
+
+  <p>For further information on the extract command, please see <a href=
+  "command_ref.html#fcm_ext">FCM Command Reference &gt; fcm extract</a>.</p>
+
+  <h2 id="simple">Simple Usage</h2>
+
+  <p>The extract configuration file is the main user interface of the extract
+  system. It is a line based text file. For a complete set of extract
+  configuration file declarations, please refer to the <a href=
+  "annex_ext_cfg.html">Annex: Declarations in FCM extract configuration
+  file</a>.</p>
+
+  <h3 id="simple_local">Extract from a local path</h3>
+
+  <p>A simple example of a basic extract configuration file is given below:</p>
+  <pre id="example_1">
+# Example 1
+# ----------------------------------------------------------------------
+cfg::type         ext       # line 1
+cfg::version      1.0       # line 2
+                            # line 3
+dest              $PWD      # line 4
+                            # line 5
+repos::var::user  $HOME/var # line 6
+                            # line 7
+expsrc::var::user code      # line 8
+</pre>
+
+  <p>The above demonstrates how to use the extract system to extract code from
+  a local user directory. Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 1</dfn>: the label <code>CFG::TYPE</code> declares the type
+    of the configuration file. The value <samp>ext</samp> tells the system that
+    it is an extract configuration file.</li>
+
+    <li><dfn>line 2</dfn>: the label <code>CFG::VERSION</code> declares the
+    version of the extract configuration file. The current default is
+    <samp>1.0</samp>. Although it is not currently used, if we have to change
+    the format of the configuration file at a later stage, we shall be able to
+    use this number to determine whether we are reading a file with an older
+    format or one with a newer format.</li>
+
+    <li><dfn>line 3</dfn>: a blank line or a line beginning with a
+    <code>#</code> is a comment, and is ignored by the interpreter.</li>
+
+    <li><dfn>line 4</dfn>: the label <code>DEST</code> declares the destination
+    root directory of this extract. The value <samp>$PWD</samp> expands to the
+    current working directory.</li>
+
+    <li><dfn>line 5</dfn>: comment line, ignored.</li>
+
+    <li><dfn>line 6</dfn>: the label
+    <code>REPOS::&lt;pck&gt;::&lt;branch&gt;</code> declares the top level URL
+    or path of a repository. The package name of the repository is given by
+    &lt;pck&gt;. In our example, we choose <samp>var</samp> as the name of the
+    package. (You can choose any name you like, however, it is usually sensible
+    to use a package name that matches the name of the project or system you
+    are working with.) The branch name in the repository is given by
+    &lt;branch&gt;. (Again, you can choose any name you like, however, it is
+    usually sensible to use a name such as <samp>base</samp>, <samp>user</samp>
+    or something that matches your branch name.) In our example, the word
+    <samp>user</samp> is normally used to denote a local user directory. Hence
+    the statement declares that the repository path for the <samp>var</samp>
+    package in the <samp>user</samp> branch can be found at
+    <samp>$HOME/var</samp>.</li>
+
+    <li><dfn>line 7</dfn>: comment line, ignored.</li>
+
+    <li><dfn>line 8</dfn>: the label
+    <code>EXPSRC::&lt;pck&gt;::&lt;branch&gt;</code> declares an
+    <em>expandable</em> source directory for the package &lt;pck&gt; in the
+    branch &lt;branch&gt;. In our example, the package name is
+    <samp>var</samp>, and the branch name is <samp>user</samp>. These match the
+    package and the branch names of the repository declaration in line 6. It
+    means that the source directory declaration is associated with the path
+    <samp>$HOME/var</samp>. The value of the declaration <samp>code</samp> is
+    therefore a sub-directory under <samp>$HOME/var</samp>. By declaring a
+    source directory using an <code>EXPSRC</code> label, the system
+    automatically searches for all sub-directories (recursively) under the
+    declared source directory.</li>
+  </ul>
+
+  <p>Invoking the extract system using the above configuration file will
+  extract all sub-directories under <samp>$HOME/var/code</samp> to
+  <samp>$PWD/src/var/code</samp>. Note: the extract system ignores symbolic
+  links and hidden files, (i.e. file names beginning with a <samp>.</samp>). It
+  will write a build configuration file to <samp>$PWD/cfg/bld.cfg</samp>. The
+  configuration used for this extract will be written to the configuration file
+  at <samp>$PWD/cfg/ext.cfg</samp>.</p>
+
+  <dl>
+    <dt>Note - incremental extract</dt>
+
+    <dd>Suppose you have already performed an extract using the above
+    configuration file. At a later time, you have made some changes to some of
+    the files in the source directory. Re-running the extract system on the
+    same configuration will trigger an incremental extract. In an incremental
+    extract, the 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.</dd>
+  </dl>
+
+  <h3 id="simple_url">Extract from a Subversion URL</h3>
+
+  <p>The next example demonstrates how to extract from a Subversion repository
+  URL:</p>
+  <pre id="example_2">
+# Example 2
+# ----------------------------------------------------------------------
+cfg::type           ext                    # line 1
+cfg::version        1.0                    # line 2
+                                           # line 3
+dest                $PWD                   # line 4
+                                           # line 5
+repos::var::base    svn://server/var/trunk # line 6
+revision::var::base 1234                   # line 7
+                                           # line 8
+expsrc::var::base   code                   # line 9
+</pre>
+
+  <ul>
+    <li><dfn>line 1-5</dfn>: same as <a href="#example_1">example 1</a>.</li>
+
+    <li><dfn>line 6</dfn>: the line declares the repository location of the
+    <samp>base</samp> branch of the <samp>var</samp> package to be the
+    Subversion URL <samp>svn://server/var/trunk</samp>.</li>
+
+    <li><dfn>line 7</dfn>: the label
+    <code>REVISION::&lt;pck&gt;::&lt;branch&gt;</code> declares the revision of
+    the repository associated with the package &lt;pck&gt; in the branch
+    &lt;branch&gt;. The current line tells the extract system to use revision
+    1234 of <samp>svn://server/var/trunk</samp>. It is worth noting that the
+    declared revision must be a revision when the declared branch exists. The
+    actual revision used is the last changed revision of the declared one. If
+    the revision is not declared, the default is to use the last changed
+    revision at the HEAD of the branch.</li>
+
+    <li><dfn>line 8</dfn>: comment line, ignored.</li>
+
+    <li><dfn>line 9</dfn>: the line declares an expandable source directory in
+    the repository <samp>svn://server/var/trunk</samp>.</li>
+  </ul>
+
+  <p>Invoking the extract system using the above configuration file will
+  extract all sub-directories under <samp>svn://server/var/trunk/code</samp> to
+  <samp>$PWD/src/var/code</samp>. It will write a build configuration file to
+  <samp>$PWD/cfg/bld.cfg</samp>. The configuration used for this extract will
+  be written to the configuration file at <samp>$PWD/cfg/ext.cfg</samp>.</p>
+
+  <dl>
+    <dt>EXPSRC or SRC?</dt>
+
+    <dd>
+      <p>So far, we have only declared source directories using the
+      <code>EXPSRC</code> statement, which stands for <em>expandable source
+      directory</em>. A source directory declared using this statement will
+      trigger the system to search recursively for any sub-directories under
+      the declared one. Any sub-directories containing regular source files
+      will be included in the extract. Symbolic links, hidden files and empty
+      directories (or those containing only symbolic links and/or hidden files)
+      are ignored.</p>
+
+      <p>If you do not want the system to search for sub-directories underneath
+      your declared source directory, you can declare your source directory
+      using the <code>SRC</code> statement. The <code>SRC</code> statement is
+      essentially the same as <code>EXPSRC</code> except that it does not
+      trigger the automatic recursive search for source directories. In fact,
+      the system implements the <code>EXPSRC</code> statement by expanding it
+      into a list of <code>SRC</code> statements.</p>
+    </dd>
+
+    <dt>Package and sub-package</dt>
+
+    <dd>
+      <p>The second field of a repository, revision or source directory
+      declaration label is the name of the container package. It is a name
+      selected by the user to identify the system or project he/she is working
+      on. (Therefore, it is often sensible to choose an identifier that matches
+      the name of the project or system.) The package name provides a unique
+      namespace for a file container. Source directories are automatically
+      arranged into sub-packages, using the names of the sub-directories as the
+      names of the sub-packages. For example, the declaration at line 9 in
+      <a href="#example_2">example 2</a> will put the source directory in the
+      <samp>var/code</samp> sub-package automatically.</p>
+
+      <p>Note that, in additional to slash <code>/</code>, double colon
+      <code>::</code> and double underscore <code>__</code> (internal only)
+      also act as delimiters for package names. Please avoid using them for
+      naming your files and directories.</p>
+
+      <p>You can declare a sub-package name explicitly in your source directory
+      statement. For example, the following two lines are equivalent:</p>
+      <pre>
+src::var::base                      code/VarMod_Surface
+src::var/code/VarMod_Surface::base  code/VarMod_Surface
+</pre>
+
+      <p>Explicit sub-package declaration should not be used normally, as it
+      requires a lot more typing (although there are some situations where it
+      can be useful, e.g. if you need to re-define the package name).</p>
+
+      <p>Currently, the extract system only supports non-space characters in
+      the package name, as the space character is used as a delimiter between
+      the declaration label and its value. If there are spaces in the path name
+      to a file or directory, you should explicity re-define the package name
+      of that path to a package name with no space using the above method.
+      However, we recommend that only non-space characters are used for naming
+      directories and files to make life simpler.</p>
+    </dd>
+  </dl>
+
+  <dl>
+    <dt>The expanded extract configuration file</dt>
+
+    <dd>
+      <p>At the end of a successful extract, the configuration used by the
+      current extract is written in <samp>cfg/ext.cfg</samp> under the extract
+      destination root. This file is an <em>expanded</em> version of the
+      original, with changes in the following declarations:</p>
+
+      <ul>
+        <li>All revision keywords are converted into revision numbers.</li>
+
+        <li>If a revision is not defined for a repository, it is set to the
+        corresponding revision number of the HEAD revision.</li>
+
+        <li>All URL keywords are converted into the full URLs.</li>
+
+        <li>All <code>EXPSRC</code> declarations are expanded into
+        <code>SRC</code> declarations.</li>
+
+        <li>All other variables are expanded.</li>
+      </ul>
+
+      <p>With this file, it should be possible for a later extract to re-create
+      the current configuration even if the contents of the repository have
+      changed. (This applies only to code stored in the repository.)</p>
+    </dd>
+  </dl>
+
+  <h3 id="simple_mirror">Mirror code to an alternate location</h3>
+
+  <p>The next example demonstrates how to extract from a repository and mirror
+  the code to an alternate location. It is essentially the same as <a href=
+  "#example_2">example 2</a>, except that it has three new lines to describe
+  how the system can mirror the extracted code to an alternate location.</p>
+  <pre id="example_3">
+# Example 3
+# ----------------------------------------------------------------------
+cfg::type           ext
+cfg::version        1.0
+
+dest                $PWD
+
+rdest::machine      tx01                           # line 6
+rdest::logname      frva                           # line 7
+rdest               /scratch/frva/extract/example3 # line 8
+
+repos::var::base    svn://server/var/trunk
+revision::var::base 1234
+
+expsrc::var::base   code
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 6</dfn>: <code>RDEST::MACHINE</code> declares the target
+    machine to which the code will be mirrored. The example mirrors the code to
+    the machine named <samp>tx01</samp>.</li>
+
+    <li><dfn>line 7</dfn>: <code>RDEST::LOGNAME</code> declares the user name
+    of the target machine, to which the user has login access. If this is not
+    declared, the system uses the login name of the current user on the local
+    machine.</li>
+
+    <li><dfn>line 8</dfn>: <code>RDEST</code> declares the root directory of
+    the alternate destination, where the mirror version of the extract will be
+    sent.</li>
+  </ul>
+
+  <p>Invoking the extract system on the above configuration will trigger an
+  extract similar to that given in <a href="#example_2">example 2</a>, but it
+  will also attempt to mirror the contents at <samp>$PWD/src/var/code</samp> to
+  <samp>/scratch/frva/extract/example3/src</samp> on the alternate destination.
+  It will also mirror the expanded extract configuration file
+  <samp>$PWD/cfg/ext.cfg</samp> to
+  <samp>/scratch/frva/extract/example3/cfg/ext.cfg</samp> and
+  <samp>$PWD/cfg/bld.cfg</samp> to
+  <samp>/scratch/frva/extract/example3/cfg/bld.cfg</samp>. It is also worth
+  noting that the content of the build configuration file will be slightly
+  different, since it will include directory names appropriate for the
+  alternate destination.</p>
+
+  <dl>
+    <dt>Note - mirroring command</dt>
+
+    <dd>
+      <p>The extract system currently supports <code>rdist</code> and
+      <code>rsync</code> as its mirroring tool. The default is
+      <code>rsync</code>. To use <code>rdist</code> instead of
+      <code>rsync</code>, add the following line to your extract configuration
+      file:</p>
+      <pre>
+rdest::mirror_cmd  rdist
+</pre>
+
+      <p>If <code>rsync</code> is used to mirror an extract, the system needs to
+      issue a separate remote shell command to create the container directory of
+      the mirror destination. The default is to issue a shell command in the
+      form <samp>ssh -n -oBatchMode=yes LOGNAME@MACHINE mkdir -p DEST</samp>.
+      The following declarations can be used to modify the command:</p>
+      <pre>
+# Examples using the default settings:
+rdest::rsh_mkdir_rsh         ssh
+rdest::rsh_mkdir_rshflags    -n -oBatchMode=yes
+rdest::rsh_mkdir_mkdir       mkdir
+rdest::rsh_mkdir_mkdirflags  -p
+</pre>
+
+      <p>In addition, the default <code>rsync</code> shell command is
+      <samp>rsync -a --exclude='.*' --delete-excluded --timeout=900 --rsh='ssh
+      -oBatchMode=yes' SOURCE DEST</samp>. The following declarations can be
+      used to modify the command:</p>
+      <pre>
+# Examples using the default settings:
+rdest::rsync       rsync
+rdest::rsyncflags  -a --exclude='.*' --delete-excluded --timeout=900 \
+                   --rsh='ssh -oBatchMode=yes'
+</pre>
+    </dd>
+  </dl>
+
+  <h2 id="advanced">Advanced Usage</h2>
+
+  <h3 id="advanced_multi">Extract from multiple repositories</h3>
+
+  <p>So far, we have only extracted from a single location. The extract system
+  is not much use if that is the only thing it can do. In fact, the extract
+  system supports extract of multiple source directories from multiple branches
+  in multiple repositories. The following configuration file is an example of
+  how to extract from multiple repositories:</p>
+  <pre id="example_4">
+# Example 4
+# ----------------------------------------------------------------------
+cfg::type           ext
+cfg::version        1.0
+
+dest                $PWD
+
+repos::var::base    fcm:var_tr              # line 6
+repos::ops::base    fcm:ops_tr              # line 7
+repos::gen::base    fcm:gen_tr              # line 8
+
+revision::gen::base 2468                    # line 10
+
+expsrc::var::base   src/code                    # line 12
+expsrc::var::base   src/scripts                 # line 13
+expsrc::ops::base   src/code                    # line 14
+src::gen::base      src/code/GenMod_Constants   # line 15
+src::gen::base      src/code/GenMod_Control     # line 16
+src::gen::base      src/code/GenMod_FortranIO   # line 17
+src::gen::base      src/code/GenMod_GetEnv      # line 18
+src::gen::base      src/code/GenMod_ModelIO     # line 19
+src::gen::base      src/code/GenMod_ObsInfo     # line 20
+src::gen::base      src/code/GenMod_Platform    # line 21
+src::gen::base      src/code/GenMod_Reporting   # line 22
+src::gen::base      src/code/GenMod_Trace       # line 23
+src::gen::base      src/code/GenMod_UMConstants # line 24
+src::gen::base      src/code/GenMod_Utilities   # line 25
+</pre>
+
+  <p>Here is an explanation of what each line does:</p>
+
+  <ul>
+    <li><dfn>line 6-8</dfn>: these lines declare the repositories for the
+    <samp>base</samp> branches of the <samp>var</samp>, <samp>ops</samp> and
+    <samp>gen</samp> packages respectively. It is worth noting that the values
+    of the declarations are no longer Subversion URLs but are FCM URL keywords.
+    These keywords are normally declared in the central configuration file of
+    the FCM system, and will be expanded into the corresponding Subversion URLs
+    by the FCM system. For further information on URL keywords, please see
+    <a href="code_management.html#svn_basic_keywords">Code Management System
+    &gt; Using Subversion &gt; Basic Command Line Usage &gt; Repository &amp;
+    Revision Keywords</a>.</li>
+
+    <li><dfn>line 10</dfn>: this line declares the revision number for the
+    <samp>base</samp> branch of the <samp>gen</samp> package, i.e. for the
+    <samp>fcm:gen_tr</samp> repository. It is worth noting that the revision
+    numbers for the <samp>var</samp> and <samp>ops</samp> packages have not
+    been declared. By default, their revision numbers will be set to the last
+    changed revision at the HEAD.</li>
+
+    <li><dfn>line 12-14</dfn>: these line declares the source directories for
+    the <samp>base</samp> branches of the <samp>var</samp> and <samp>ops</samp>
+    packages. For the <samp>var</samp> package, we are extracting everything
+    from the <samp>code</samp> and the <samp>scripts</samp> sub-directory. For
+    the <samp>ops</samp> package, we are extracting everything from the
+    <samp>code</samp> directory.</li>
+
+    <li><dfn>line 15-25</dfn>: these line declares the source directories for
+    the <samp>base</samp> branch of the <samp>gen</samp> package. The source
+    directories declared will not be searched for sub-directories underneath
+    the declared directories.</li>
+  </ul>
+
+  <p>We shall end up with a directory tree such as:</p>
+  <pre>
+$PWD
+   |
+   |--- cfg
+   |      |
+   |      |--- bld.cfg
+   |      |--- ext.cfg
+   |
+   |--- src
+          |
+          |--- gen
+          |      |
+          |      |--- code
+          |              |
+          |              |--- GenMod_Constants
+          |              |--- GenMod_Control
+          |              |--- GenMod_FortranIO
+          |              |--- GenMod_GetEnv
+          |              |--- GenMod_ModelIO
+          |              |--- GenMod_ObsInfo
+          |              |--- GenMod_Platform
+          |              |--- GenMod_Reporting
+          |              |--- GenMod_Trace
+          |              |--- GenMod_UMConstants
+          |              |--- GenMod_Utilities
+          |
+          |--- ops
+          |      |
+          |      |--- code
+          |              |
+          |              |--- ...
+          |
+          |--- var
+                 |
+                 |--- code
+                 |       |
+                 |       |--- ...
+                 |
+                 |--- scripts
+                         |
+                         |--- ...
+</pre>
+
+  <dl>
+    <dt>Note - revision number</dt>
+
+    <dd>
+      <p>As seen in the above example, if a revision number is not specified
+      for a repository URL, it defaults to the last changed revision at the
+      HEAD of the branch. The revision number can also be declared in other
+      ways:</p>
+
+      <ul>
+        <li>Any revision arguments acceptable by Subversion are allowed. You
+        can use a valid revision number, a date between a pair of curly
+        brackets (e.g. <samp>{2005-05-01T12:00}</samp>) or the keyword HEAD.
+        However, please do not use the keywords BASE, COMMITTED or PREV as
+        these are reserved for working copy only.</li>
+
+        <li>FCM revision keywords are allowed. These must be defined for the
+        corresponding repository URLs in either the central or the user FCM
+        configuration file. For further information on revision keywords,
+        please see <a href="code_management.html#svn_basic_keywords">Code
+        Management &gt; Using Subversion &gt; Basic Command Line Usage &gt;
+        Repository &amp; Revision Keywords</a>.</li>
+
+        <li>Do not use the keyword USER, as it is used internally by the
+        extract system.</li>
+      </ul>
+
+      <p>If a revision number is specified for a branch, the actual revision
+      used by the extract system is the last changed revision of the branch,
+      which may differ from the declared revision. While this behaviour is
+      useful in most situations, some users may find it confusing to work with.
+      It is possible to alter this behaviour so that extract will fail if the
+      declared revision does not correspond to a changeset of the declared
+      branch. Make the following declaration to switch on this checking:</p>
+      <pre>
+revmatch  true
+</pre>
+    </dd>
+  </dl>
+
+  <h3 id="advanced_branches">Extract from multiple branches</h3>
+
+  <p>We have so far dealt with a single branch in any package. The extract
+  system can be used to <em>combine</em> changes from different branches of a
+  package. An example is given below:</p>
+  <pre id="example_5">
+# Example 5
+# ----------------------------------------------------------------------
+cfg::type               ext
+cfg::version            1.0
+ 
+dest                    $PWD
+ 
+repos::var::base        fcm:var_tr
+repos::ops::base        fcm:ops_tr
+repos::gen::base        fcm:gen_tr
+ 
+revision::gen::base     2468
+ 
+expsrc::var::base       src/code
+expsrc::var::base       src/scripts
+expsrc::ops::base       src/code
+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_ObsInfo
+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
+
+repos::var::branch1     fcm:var_br/frva/r1234_new_stuff   # line 27
+repos::var::branch2     fcm:var_br/frva/r1516_bug_fix     # line 28
+repos::ops::branch1     fcm:ops_br/opsrc/r3188_good_stuff # line 29
+</pre>
+
+  <p>The configuration file in <a href="#example_5">example 5</a> is similar to
+  that of <a href="#example_4">example 4</a> except for the last three lines.
+  Here is an explanation of what they do:</p>
+
+  <ul>
+    <li>
+      <dfn>line 27</dfn>: this line declares a repository URL for the
+      <samp>branch1</samp> branch of the <samp>var</samp> package. From the URL
+      of the branch, we know that the branch was created by the user
+      <samp>frva</samp> based on the trunk at revision at 1234. The description
+      of the branch is <samp>branch1</samp>. The following points are worth
+      noting:
+
+      <ul>
+        <li>By declaring a new branch with the same package name to a
+        previously declared branch, it is assumed that both branches reside in
+        the same Subversion repository.</li>
+
+        <li>No revision is declared for this URL, so the default is used which
+        is the last changed revision at the HEAD of the branch.</li>
+
+        <li>No source directory is declared for this URL. By default, if no
+        source directory is declared for a branch repository, it will attempt
+        to use the same set of source directories as the first declared branch
+        of the package. In this case, the source directories declared for the
+        <samp>base</samp> branch of the <samp>var</samp> package will be
+        used.</li>
+      </ul>
+    </li>
+
+    <li><dfn>line 28</dfn>: this line declares another branch called
+    <samp>branch2</samp> for the <samp>var</samp> package. No source directory
+    is declared for this URL either, so it will use the same set of source
+    directories declared for the <samp>base</samp> branch.</li>
+
+    <li><dfn>line 29</dfn>: this line declares a branch called
+    <samp>branch1</samp> for the <samp>ops</samp> package. It will use the same
+    set of source directories declared for the <samp>ops</samp> package
+    <samp>base</samp> branch.</li>
+  </ul>
+
+  <p>When we invoke the extract system, it will attempt to extract from the
+  first declared branch of a package, if the last changed revision of the
+  source directory is the same in all the branches. However, if the last
+  changed revision of the source directory differs for different branches, the
+  system will attempt to obtain an extract priority list for each source
+  directory, using the following logic:</p>
+
+  <ol>
+    <li>The system looks for source directory packages from the first declared
+    branch to the last declared branch.</li>
+
+    <li>The branch in which a source directory package is first declared is the
+    <samp>base</samp> branch of the source directory package.</li>
+
+    <li>The last changed revision of a source directory package in a
+    subsequently declared repository branch is compared with that of the base
+    branch. If the last changed revision is the same as that of the base
+    branch, the source directory of this branch is discarded. Otherwise, it is
+    placed at the end of the extract priority list.</li>
+  </ol>
+
+  <p>For the <samp>var</samp> package in the above example, let us assume that
+  we have three source directory packages X, Y and Z under <samp>code</samp>,
+  and their last changed revisions under <samp>base</samp> are 100. Let's say
+  we have committed some changes to X and Z in the <samp>branch1</samp> branch
+  at revision 102, and other changes to Y and Z in the <samp>branch2</samp>
+  branch at revision 104, the extract priority lists for X, Y and Z will look
+  like:</p>
+
+  <ul>
+    <li>X: base (100, base), branch1 (102), branch2 (100, discarded)</li>
+
+    <li>Y: base (100, base), branch1 (100, discarded), branch2 (104)</li>
+
+    <li>Z: base (100, base), branch1 (102), branch2 (104)</li>
+  </ul>
+
+  <p>Once we have an extract priority list for a source directory, we can begin
+  extracting source files in the source directory. The source directory of the
+  base branch is extracted first, followed by that in the subsequent branches.
+  If a source file in a subsequent branch has the same content as the that in
+  the base branch, it is discarded. Otherwise, the following logic determines
+  the branch to use:</p>
+
+  <ol>
+    <li>If a source file is modified in only one subsequent branch, the source
+    file in that branch is extracted.</li>
+
+    <li>If a source file is modified in two or more subsequent branches, but
+    their modifications are the same, then the source file in the first
+    modification is used.</li>
+
+    <li>If a source file is modified in two or more subsequent branches and
+    their modifications differ, then the behaviour depends on the "conflict
+    mode" setting, which can be <code>fail</code>, <code>merge</code> (default)
+    and <code>override</code>. If the conflict mode is <code>fail</code>, the
+    extract fails. If the conflict mode is <code>merge</code>, the system will
+    attempt to merge the changes using a tool such as <code>diff3</code>. The
+    result of the merge will be used to update the destination. The extract
+    fails only if there are unresolved conflicts in the merge. (In which case,
+    the conflict should be resolved using the version control system before
+    re-running the extract system.) If the conflict mode is
+    <code>override</code>, the change in the latest declared branch takes
+    precedence, and the changes in all other branches will be ignored. The
+    conflict mode can be changed using the <code>CONFLICT</code> declaration in
+    the extract configuration file. E.g:
+      <pre>
+conflict  fail
+</pre>
+    </li>
+  </ol>
+
+  <p>Once the system has established which source files to use, it determines
+  whether the destination file is out of date or not. The destination file is
+  out of date if it does not exist or if its content differs from the version
+  of the source file we are using. The system only updates the destination if
+  it is considered to be out of date.</p>
+
+  <p>The extract system can also combine changes from branches in the
+  Subversion repository and the local file system. The limitation is that there
+  can only be one branch from the local file system. (By convention, the branch
+  is named <samp>user</samp>.)</p>
+
+  <p>It is also worth bearing in mind that the <samp>user</samp> branch always
+  takes precedence over branches residing in Subversion repositories. Hence,
+  source directories from a <samp>user</samp> branch are always placed at the
+  end of the extract priority list.</p>
+
+  <p>Extracting from a mixture of Subversion repository and local file system
+  is demonstrated in the next example.</p>
+  <pre id="example_6">
+# Example 6
+# ----------------------------------------------------------------------
+cfg::type               ext
+cfg::version            1.0
+ 
+dest                    $PWD
+  
+repos::var::base        fcm:var_tr
+repos::ops::base        fcm:ops_tr
+repos::gen::base        fcm:gen_tr
+ 
+revision::gen::base     2468
+ 
+expsrc::var::base       src/code
+expsrc::var::base       src/scripts
+expsrc::ops::base       src/code
+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_ObsInfo
+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
+
+repos::var::branch1     fcm:var_br/frva/r1234_new_stuff
+repos::var::branch2     fcm:var_br/frva/r1516_bug_fix
+repos::ops::branch1     fcm:ops_br/opsrc/r3188_good_stuff
+
+repos::var::user        $HOME/var                         # line 31
+repos::gen::user        $HOME/gen                         # line 32
+</pre>
+
+  <p><a href="#example_6">Example 6</a> is similar to <a href=
+  "#example_5">example 5</a> except that it is also extracting from local
+  directories. Here is an explanation of the lines:</p>
+
+  <ul>
+    <li><dfn>line 31-32</dfn>: these line declare the repositories for the
+    <samp>user</samp> branches of the <samp>var</samp> and <samp>gen</samp>
+    packages respectively. Both are local paths at the local file system. There
+    are no declarations for source directories for the <samp>user</samp>
+    branches, so they use the same set of source directories of the first
+    declared branches, the <samp>base</samp> branches in both cases.</li>
+  </ul>
+
+  <dl>
+    <dt>Note - the INC declaration</dt>
+
+    <dd>
+      You have probably realised that the above examples have many repeated
+      lines. To avoid having repeated lines in multiple extract configuration
+      files, you can use <code>INC</code> declarations to include other extract
+      configuration files. For example, if the configuration file of example 5
+      is stored in the file <samp>$HOME/example5/ext.cfg</samp>, line 1 to 29
+      of <a href="#example_6">example 6</a> can be replaced with an
+      <code>INC</code> declaration. <a href="#example_6">Example 6</a> can then
+      be written as:
+      <pre>
+inc                     $HOME/example5/ext.cfg
+
+repos::var::user        $HOME/var
+repos::gen::user        $HOME/gen
+</pre>
+
+      <p>Note: the <code>INC</code> declaration supports the special
+      environment variable <var>$HERE</var>. If this variable is already set in
+      the environment, it acts as a normal environment variable. However, if it
+      is not set, it will be expanded into the container directory of the
+      current extract configuration file. This feature is particularly useful
+      if you are including a hierarchy of extract configurations from files in
+      the same container directory in a repository.</p>
+    </dd>
+  </dl>
+
+  <h3 id="advanced_inherit">Inherit from a previous extract</h3>
+
+  <p>All the examples above dealt with standalone extract, that is, the current
+  extract is independent of any other extract. If a previous extract exists in
+  another location, the extract system can 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 source directory tree. This
+  type of incremental extract is useful in several ways. For instance:</p>
+
+  <ul>
+    <li>It is fast, because you only have to extract and mirror files that you
+    have changed.</li>
+
+    <li>The subsequent build will also be fast, since it will use 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>The following example is based on <a href="#example_4">example 4</a> and
+  <a href="#example_6">example 6</a>. The assumption is that an extract has
+  already been performed at the directory <samp>~frva/var/vn22.0</samp> based
+  on the configuration file in <a href="#example_4">example 4</a>.</p>
+  <pre id="example_7">
+# Example 7
+# ----------------------------------------------------------------------
+cfg::type               ext
+cfg::version            1.0
+ 
+dest                    $PWD
+
+use                     ~frva/var/vn22.0                  # line 6
+
+repos::var::branch1     fcm:var_br/frva/r1234_new_stuff   # line 8
+repos::var::branch2     fcm:var_br/frva/r1516_bug_fix     # line 9
+repos::ops::branch1     fcm:ops_br/opsrc/r3188_good_stuff # line 10
+
+repos::var::user        $HOME/var                         # line 12
+repos::gen::user        $HOME/gen                         # line 13
+</pre>
+
+  <ul>
+    <li><dfn>line 6</dfn>: this line replaces line 1 to 25 of <a href=
+    "#example_6">example 6</a>. It declares that the current extract should
+    inherit from the previous extract located at
+    <samp>~frva/var/vn22.0</samp>.</li>
+  </ul>
+
+  <p>Running the extract system using the above configuration will trigger an
+  incremental extract, as if you are running an incremental extract having
+  modified the configuration file in <a href="#example_4">example 4</a> to that
+  of <a href="#example_6">example 6</a>. The only difference is that the
+  original extract using the <a href="#example_4">example 4</a> configuration
+  will be left untouched at <samp>~frva/var/vn22.0</samp>, and the new extract
+  will contain only the changes in the branches declared from line 8 to 13.</p>
+
+  <p>Note: extract inheritance allows you to add more branches to a package,
+  but you should not redefine the <code>REPOS</code>, <code>REVISION</code>,
+  <code>EXPSRC</code> or <code>SRC</code> declarations of a branch that is
+  already declared (and already extracted) in the inherited extract. Although
+  the system will not stop you from doing so, you may end up with an extract
+  that does not quite do what it is supposed to do. For example, if the
+  <samp>base</samp> branch in the <samp>foo</samp> package
+  (<tt>repos::foo::base</tt>) is already defined and extracted in an extract
+  you are inheriting from, you should not redefine any of the
+  <tt>*::foo::base</tt> declarations in your current extract. However, you are
+  free to add more branches for the same package with new labels (e.g.
+  <tt>repos::foo::b1</tt>), and indeed new packages that are not already
+  defined in the inherited extract (e.g. <tt>repos::bar::base</tt>).</p>
+
+  <p>If you are setting up an extract to be inherited, you do not have to
+  perform a build. If you don't you will still gain the benefit of incremental
+  file extract, but you will be performing a full build of the code.</p>
+
+  <dl>
+    <dt>Note - inherit and mirror</dt>
+
+    <dd>
+      <p>It is worth bearing in mind that <tt>rdest::*</tt> settings are not
+      inherited. If mirroring is required in the inheriting extract, it will
+      require its own set of <tt>rdest::*</tt> declarations.</p>
+
+      <p>The system will, however, assume that a mirrored version of the
+      inherited extract is available for inheritance from the mirrored
+      destination of the current extract.</p>
+      
+      <p>E.g.: Consider an extract at <samp>/path/to/inherited/</samp> and an
+      inheriting extract at <samp>/path/to/current/</samp>. If the former does
+      not have a mirror, the latter should not have one either. If the former
+      mirrors to <samp>machine@/path/to/inherited/mirror/</samp> and the latter
+      mirrors to <samp>machine@/path/to/current/mirror/</samp>, the system will
+      assume that the subsequent build at
+      <samp>machine@/path/to/current/mirror/</samp> can inherit from the build
+      at <samp>machine@/path/to/inherited/mirror/</samp>. This is illustrated
+      below:</p>
+
+      <pre>
+/path/to/current/       =&gt; at machine: /path/to/current/mirror/
+use /path/to/inherited/ =&gt; at machine: use /path/to/inherited/mirror/
+</pre>
+    </dd>
+  </dl>
+
+  <h3 id="advanced_build">Extract - Build Configuration</h3>
+
+  <p>Configuration settings for feeding into the build system can be declared
+  through the extract configuration file using the <code>BLD::</code> prefix.
+  Any line in an extract configuration containing a label with such a prefix
+  will be considered a build system variable. At the end of a successful
+  extract, the system strips out the <code>BLD::</code> prefix before writing
+  these variables to the build configuration file. Some example entries are
+  given between line 17 and 22 in the following configuration file:</p>
+  <pre id="example_8">
+# Example 8
+# ----------------------------------------------------------------------
+cfg::type           ext
+cfg::version        1.0
+
+dest                $PWD
+
+repos::var::base    fcm:var_tr
+repos::ops::base    fcm:ops_tr
+repos::gen::base    fcm:gen_tr
+
+revision::gen::base 2468
+
+expsrc::var::base   src/code
+expsrc::var::base   src/scripts
+expsrc::ops::base   src/code
+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_ObsInfo
+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
+
+bld::target         VarProg_AnalysePF.exe   # line 27
+
+bld::tool::fc       sxmpif90                # line 29
+bld::tool::cc       sxmpic++                # line 30
+bld::tool::ld       sxmpif90                # line 31
+</pre>
+
+  <p>The above example is essentially the same as <a href="#example_4">example
+  4</a>, apart from the additional build configuration. The following is a
+  simple explanation of what the lines represent: (For detail of the build
+  system, please see the next chapter on <a href="build.html">The Build
+  System</a>.)</p>
+
+  <ul>
+    <li><dfn>line 27</dfn>: the line declares a default target of the
+    build.</li>
+
+    <li><dfn>line 29-31</dfn>: the lines declare the Fortran compiler, the C
+    compiler and the linker respectively.</li>
+  </ul>
+
+  <dl>
+    <dt>Note - use of variables</dt>
+
+    <dd>
+      <p>When you start using the extract system to define compiler flags for
+      the build system, you may end up having to make a lot of long and
+      repetitive declarations. In this case, you may want to define variables
+      to replace the repetitive parts of the declarations.</p>
+
+      <p>Environment variables whose names contain only upper case latin
+      alphabets, numbers and underscores can be referenced in a declaration
+      value via the syntax <code>$NAME</code> or <code>${NAME}</code>. For
+      example:</p>
+      <pre>
+repos::um::base    ${HOME}/svn-wc/um
+bld::tool::fflags  $MY_FFLAGS
+</pre>
+      
+      <p>You can define a user variable by making a declaration with a label
+      that begins with a percent sign <code>%</code>. The value of a user
+      variable remains in memory until the end of the current file is reached.
+      You can reference a user variable in a declaration value via the syntax
+      <code>%NAME</code> or <code>%{NAME}</code>. For example:</p>
+      <pre>
+# Declare a variable %fred
+%fred                     -Cdebug -eC -Wf,-init heap=nan stack=nan
+
+bld::tool::fflags         %fred
+# bld::tool::fflags       -Cdebug -eC -Wf,-init heap=nan stack=nan
+
+bld::tool::fflags::foo    %fred -f0
+# bld::tool::fflags::foo  -Cdebug -eC -Wf,-init heap=nan stack=nan -f0
+
+bld::tool::fflags::bar    -w %fred
+# bld::tool::fflags::bar  -w -Cdebug -eC -Wf,-init heap=nan stack=nan
+</pre>
+
+      <p>Further to this, each declaration results in an internal variable of
+      the same name and you can also refer to any of these internal variables in
+      the same way. So, the example given above could also be written as
+      follows:</p>
+      <pre>
+bld::tool::fflags         -Cdebug -eC -Wf,-init heap=nan stack=nan
+bld::tool::fflags::foo    %bld::tool::fflags -f0
+bld::tool::fflags::bar    -w %bld::tool::fflags
+</pre>
+    </dd>
+
+    <dt>Note - as-parsed configuration</dt>
+
+    <dd>
+      <p>If you use a hierarchy of <code>INC</code> declarations or variables,
+      you may end up with a configuration file that is difficult to understand.
+      To help you with this, the extract system generates an as-parsed
+      configuration file at <samp>cfg/parsed_ext.cfg</samp> of the destination.
+      The content of the as-parsed configuration file is what the extract
+      system actually reads. It should contain everything in your original
+      extract configuration file, except that all <code>INC</code>
+      declarations, environment variables and user/internal variables are
+      expanded.</p>
+    </dd>
+  </dl>
+
+  <h2 id="verbose">Diagnostic verbose level</h2>
+
+  <p>The amount of diagnostic messages generated by the extract system is
+  normally set to a level suitable for normal everyday operation. This is the
+  default diagnostic verbose level 1. If you want a minimum amount of
+  diagnostic messages, you should set the verbose level to 0. If you want more
+  diagnostic messages, you can set the verbose level to 2 or 3. You can modify
+  the verbose level in two ways. The first way is to set the environment
+  variable <var>FCM_VERBOSE</var> to the desired verbose level. The second way
+  is to invoke the extract system with the <code>-v &lt;level&gt;</code>
+  option. (If set, the command line option overrides the environment
+  variable.)</p>
+
+  <p>The following is a list of diagnostic output at each verbose level:</p>
+
+  <dl>
+    <dt>Level 0</dt>
+
+    <dd>
+      <ul>
+        <li>Report the time taken to extract the code.</li>
+
+        <li>Report the time taken to mirror the code.</li>
+
+        <li>If <code>rdist</code> is used to mirror the code, run the command
+        with the <code>-q</code> option.</li>
+      </ul>
+    </dd>
+
+    <dt>Level 1</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at verbose level 0.</li>
+
+        <li>Report the name of the extract configuration file.</li>
+
+        <li>Report the location of the extract destination.</li>
+
+        <li>Report date/time at the beginning of the extract step.</li>
+
+        <li>If the revision specified for a repository branch is not its last
+        changed revision, print an information statement to inform the user of
+        the last changed revision of the branch.</li>
+
+        <li>Summarises the destination status and the source status.</li>
+
+        <li>Report date/time at the beginning of the mirror step.</li>
+
+        <li>Report the location of the alternate destination.</li>
+
+        <li>Report total time.</li>
+      </ul>
+    </dd>
+
+    <dt>Level 2</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at verbose level 1.</li>
+
+        <li>If the revision specified for a repository branch is not current
+        (i.e. the specified revision number is less than the revision number of
+        the last commit revision), print an information statement to inform the
+        user of the last commit revision of the branch.</li>
+
+        <li>Report the detail of each change in the destination.</li>
+
+        <li>If <code>rdist</code> is used to mirror the code, run the command
+        without the <code>-q</code> option.</li>
+      </ul>
+    </dd>
+
+    <dt>Level 3</dt>
+
+    <dd>
+      <ul>
+        <li>Everything at verbose level 2.</li>
+
+        <li>Report all shell commands invoked by the extract system with
+        timestamp.</li>
+
+        <li>If <code>rdist</code> is used to mirror the code, print the
+        <samp>distfile</samp> supplied to the command.</li>
+
+        <li>If <code>rsync</code> is used to mirror the code, invoke the
+        command with the <code>-v</code> option.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h2 id="nosvn">When Subversion Is Not Available</h2>
+
+  <p>The extract system can still be used if Subversion is not available.
+  Clearly, you can only use local repositories. However, you can still do
+  incremental extract, mirror an extract to an alternate location, or combine
+  code from multiple local repositories.</p>
+
+  <p>If you are using Subversion but your server is down then clearly there is
+  little you can do. However, if you already have an extract then you can
+  re-run <code>fcm extract</code> as long as the extract configuration file
+  only refers to fixed revisions. If this is not the case then you can always
+  use the expanded extract configuration file which can be found in
+  <samp>cfg/ext.cfg</samp> under the extract destination root. This means that
+  you can continue to makes changes to local code and do incremental extracts
+  even whilst your Subversion server is down.</p>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/fcm.js
===================================================================
--- vendors/fcm/1-5/doc/user_guide/fcm.js	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/fcm.js	(revision 1981)
@@ -0,0 +1,212 @@
+//------------------------------------------------------------------------------
+// JS for displaying information of FCM documents.
+//------------------------------------------------------------------------------
+FCM = {
+    // List of heading tags for the contents
+    CONTENT_INDEX_OF: {'H2': 0, 'H3': 1, 'H4': 2, 'H5': 3, 'H6': 4},
+    // ID in a document, for the content
+    ID_OF_CONTENT: 'fcm-content',
+    // ID in a document, for the maintenance information
+    ID_OF_MAINTENANCE: 'fcm-js-maintenance',
+    // ID in a document, for the link to its PDF version
+    ID_OF_PDF: 'fcm-js-pdf',
+    // ID in a document, for the breadcrumb trail
+    ID_OF_TRAIL: 'fcm-js-trail',
+    // URL to the log browser of the FCM trunk
+    URL_OF_LOG_BROWSER: 'http://fcm1/projects/FCM/intertrac/log:/FCM/trunk/',
+    // URL to the FCM team page
+    URL_OF_TEAM: null,
+
+    // Making DOM manipulation less painful
+    DOM: {
+        // The current document
+        DOC: document,
+
+        // Adds an array of "nodes" (Node or String) to "node"
+        append: function(node, items) {
+            if (node == null || items == null) {
+                return;
+            }
+            for (var i = 0; i < items.length; i++) {
+                if (items[i] == null) {
+                    continue;
+                }
+                if (items[i].nodeType == null) {
+                    node.appendChild(this.DOC.createTextNode(items[i]));
+                }
+                else {
+                    node.appendChild(items[i]);
+                }
+            }
+        },
+
+        // Returns the base name of a URL, relative to current document
+        basename: function(url) {
+            if (url == null) {
+                url = this.DOC.URL;
+            }
+            return url.substr(this.DOC.URL.lastIndexOf('/') + 1);
+        },
+
+        // Creates and returns an element
+        create: function(tag, items, attribs) {
+            var element = this.DOC.createElement(tag);
+            this.append(element, items);
+            if (attribs == null) {
+                return element;
+            }
+            for (var i = 0; i < attribs.length; i++) {
+                element[attribs[i][0]] = attribs[i][1];
+            }
+            return element;
+        },
+
+        // Shorthand for root[attrib]
+        get_attrib: function(root, attrib) {
+            return root[attrib];
+        },
+
+        // Shorthand for this.DOC.getElementById(id)
+        get_by_id: function(id) {
+            return this.DOC.getElementById(id);
+        },
+
+        // Shorthand for root.getElementsByTagName(name)[i]
+        get_by_name: function(root, name, i) {
+            var elements = root.getElementsByTagName(name);
+            if (i == null) {
+                return elements;
+            }
+            if (elements == null || i >= elements.length) {
+                return null;
+            }
+            return elements[i];
+        },
+
+        map: function(action_on, items) {
+            var ret = [];
+            for (var i = 0; i < items.length; i++) {
+                ret = ret.concat(action_on(items[i], i));
+            }
+            return ret;
+        }
+    },
+
+    // Creates and returns the content
+    create_content: function() {
+        var root = $D.get_attrib($D.DOC, 'body');
+        var stack = [{content: null, item: null}];
+        for (var node = root.firstChild; node != null; node = node.nextSibling) {
+            if (
+                node.nodeType != 1
+                || node.id == null
+                || node.id == ''
+                || this.CONTENT_INDEX_OF[node.tagName] == null
+                || this.CONTENT_INDEX_OF[node.tagName] + 1 > stack.length
+            ) {
+                continue;
+            }
+            while (this.CONTENT_INDEX_OF[node.tagName] + 1 < stack.length) {
+                stack.shift();
+            }
+            if (stack[0].content == null) {
+                stack[0].content = $D.create('ul');
+                if (stack[0].item != null) {
+                    $D.append(stack[0].item, [stack[0].content]);
+                }
+            }
+            var item = $D.create(
+                'li',
+                [$D.create('a', [node.innerHTML], [['href', '#' + node.id]])]
+            );
+            $D.append(stack[0].content, [item]);
+            stack.unshift({content: null, item: item});
+        }
+        return [stack[stack.length - 1].content];
+    },
+
+    // Creates and returns the maintenance information for "path"
+    create_info_for_maintenance: function(path) {
+        if (this.URL_OF_TEAM == null) {
+            return [];
+        }
+        return [
+            'Maintained by: ',
+            $D.create('a', ['FCM team'], [['href', FCM.URL_OF_TEAM]]),
+            '. History: ',
+            $D.create(
+                'a',
+                ['log'],
+                [['href', this.URL_OF_LOG_BROWSER + path + $D.basename()]]
+            ),
+            '.'
+        ];
+    },
+
+    // Creates and returns the PDF link for "name"
+    create_info_for_pdf: function(name) {
+        return [
+            'For printing, please use the ',
+            $D.create('a', ['PDF'], [['href', 'fcm-' + name + '.pdf']]),
+            ' version of the document.'
+        ];
+    },
+
+    // Creates a breadcrumb trail from a list of [[href, text], ...]
+    create_trail: function(items) {
+        if (items == null) {
+            return [];
+        }
+        return [].concat(
+            $D.map(
+                function(item, i) {
+                    return [].concat(
+                        (i != 0 ? ' > ' : []),
+                        $D.create('a', [item[1]], [['href', item[0]]])
+                    );
+                },
+                items
+            ),
+            ' > ',
+            $D.get_by_name($D.DOC, 'title', 0).innerHTML
+        );
+    },
+
+    // A simple facade for doing the onload tasks
+    load: function(maintenance_path, pdf_name, trail_list) {
+        var tasks = [
+            {
+                id: this.ID_OF_CONTENT,
+                task: function() {
+                    return FCM.create_content();
+                }
+            },
+            {
+                id: this.ID_OF_MAINTENANCE,
+                task: function() {
+                    return FCM.create_info_for_maintenance(maintenance_path);
+                }
+            },
+            {
+                id: this.ID_OF_PDF,
+                task: function() {
+                    return FCM.create_info_for_pdf(pdf_name);
+                }
+            },
+            {
+                id: this.ID_OF_TRAIL,
+                task: function() {
+                    return FCM.create_trail(trail_list);
+                }
+            }
+        ];
+        for (var i = 0; i < tasks.length; i++) {
+            var node = $D.get_by_id(tasks[i].id);
+            if (node == null) {
+                continue;
+            }
+            $D.append(node, tasks[i].task());
+        }
+    }
+};
+$D = FCM.DOM;
Index: vendors/fcm/1-5/doc/user_guide/further_info.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/further_info.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/further_info.html	(revision 1981)
@@ -0,0 +1,71 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: Further Information</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - Further Information" />
+  <meta name="keywords" content="FCM, user guide" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Further Information</h1>
+
+  <dl>
+    <dt><a href="../design/">FCM Detailed Design</a></dt>
+
+    <dd>
+      <ul>
+        <li>Information about the internal workings of the FCM system.</li>
+
+        <li>Intended mainly for people maintaining or developing the FCM
+        system.</li>
+      </ul>
+    </dd>
+
+    <dt>Subversion</dt>
+
+    <dd>
+      <ul>
+        <li><a href="http://subversion.tigris.org/">Subversion Home
+        Page</a></li>
+
+        <li><a href="http://svnbook.red-bean.com/en/1.4//">Subversion
+        Book</a></li>
+
+        <li><a href="http://subversion.tigris.org/faq.html">Subversion
+        FAQ</a></li>
+
+        <li><a href="http://subversion.tigris.org/hacking.html">Hacker's Guide
+        to Subversion</a>: Contains some useful guidelines on log message,
+        filing issues, etc.</li>
+      </ul>
+    </dd>
+
+    <dt>Other links</dt>
+
+    <dd>
+      <ul>
+        <li><a href="http://trac.edgewall.org/">Trac Home Page</a></li>
+
+        <li><a href="http://furius.ca/xxdiff/">xxdiff Home Page</a></li>
+      </ul>
+    </dd>
+  </dl>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/getting_started.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/getting_started.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/getting_started.html	(revision 1981)
@@ -0,0 +1,1575 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: Getting Started</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - Getting Started" />
+  <meta name="keywords" content="FCM, user guide, getting started, tutorial" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Getting Started</h1>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="introduction">Introduction</h2>
+
+  <p>This chapter takes a <em>hands-on</em> approach to help you set up your
+  FCM session, and familiarise yourself with some of the system's basic
+  concepts and working practices. It is designed to complement other sections
+  of the User Guide.</p>
+
+  <p>You may also find it useful to refer to the <a href=
+  "annex_quick_ref.html">Annex: Quick reference</a>.</p>
+
+  <h2 id="setup">How to set yourself up to run FCM</h2>
+
+  <p>It is easy to set yourself up to run FCM. Simply follow the steps
+  below:</p>
+
+  <dl>
+    <dt>Setting up your PATH</dt>
+
+    <dd>
+      <p>The full FCM system is already available for you to use on a Met
+      Office Scientific Desktop. On other Met Office systems (e.g. the Met
+      Office HPC) only the build component is functional.</p>
+
+      <p>On a Met Office Scientifc Desktop, FCM is automatically available in
+      the standard <var>PATH</var>. On a Met Office HPC, it is also
+      automatically added to your <var>PATH</var> by most user interfaces of
+      Met Office scientific application jobs. However, if you intend to run FCM
+      from the command line on the Met Office HPC then you will need to add the
+      following to your <samp>$HOME/.profile</samp> script on that
+      platform:</p>
+      <pre>
+. ~fcm/FCM/bin/env.sh  # Add FCM environment
+</pre>
+    </dd>
+
+    <dt>Setting up the FCM GUI to work with Konqueror</dt>
+
+    <dd>
+      <p>If you like to use a graphical user interface for some common code
+      management commands, you can set it up for launching from your desktop
+      Konqueror file manager by typing:</p>
+      <pre>
+(SHELL PROMPT)$ fcm_setup_konqueror
+</pre>
+
+      <p>See the section on <a href=
+      "code_management.html#svn_gui_konqueror">Accessing the GUI from
+      Konqueror</a> for further information.</p>
+
+      <p>Note that the first time you issue a command which requires
+      authentication you may need to supply a password or run it from the
+      command line. See the section on <a href=
+      "code_management.html#svn_gui_commands">GUI Commands</a> for further
+      information.</p>
+    </dd>
+
+    <dt>Configure your editor for Subversion</dt>
+
+    <dd>
+      <p>When you attempt to create a branch or commit changes to the
+      repository, you will normally be prompted to edit your commit log message
+      using a text editor. The system chooses its editor by searching for a
+      non-empty string through a hierarchy of environment variables in this
+      order: <var>SVN_EDITOR</var>, <var>VISUAL</var>, and <var>EDITOR</var>.
+      If none of these environment variables are set, the default is to use
+      <code>nedit</code>. If you set your editor with an environment variable,
+      it is worth bearing in mind that it must be able to run in the
+      foreground. For example, you can add one of the followings in your
+      <samp>$HOME/.kshrc</samp> (ksh) or <samp>$HOME/.bashrc</samp> (bash):</p>
+      <pre>
+# GVim
+export SVN_EDITOR='gvim -f'
+
+# Emacs
+export SVN_EDITOR=emacs
+
+# NEdit client "nc"
+export SVN_EDITOR='nc -wait'
+</pre>
+    </dd>
+
+    <dt>Register your user name</dt>
+
+    <dd>
+      <p>At the Met Office, a small number of projects managed by FCM grant
+      write accesses to their Subversion repositories and Trac ticket
+      create/modify privileges to authorised users only. If you are developing
+      code for such a project, please contact the project's system manager, who
+      will arrange with the FCM team to put your user name in the register.</p>
+    </dd>
+
+    <dt>Configure your e-mail address in Trac</dt>
+
+    <dd>
+      <p>Trac can be configured to send automatic e-mail notifications to
+      authors of any ticket whenever there are changes to that ticket (and we
+      would expect most systems to be configured in this way). You should check
+      that the settings for your name and e-mail address are correct. To do
+      this you need to go to the Settings page once you are logged into Trac.
+      (Click on <kbd>Settings</kbd> just above the menu bar). Check that your
+      settings are entered correctly. Note: at the Met Office, these settings
+      are set up and maintained automatically, and so you should report any
+      errors to the FCM team.</p>
+    </dd>
+
+    <dt>Configure your web browser</dt>
+
+    <dd>
+      <p>FCM assumes that you are using Firefox as your default web browser. If
+      you use another web browser such as Mozilla, you should configure it in
+      your <samp>$HOME/.fcm</samp> file. See the section on <a href=
+      "command_ref.html#fcm_svn_trac">fcm trac</a> for further information.</p>
+    </dd>
+  </dl>
+
+  <h2 id="tutorial">Tutorial</h2>
+
+  <h3 id="tutorial_intro">Introduction</h3>
+
+  <p>This tutorial leads you through the basics of using FCM to make changes to
+  your source code, and demonstrates the recommended practices for working with
+  it. A tutorial Subversion repository, with its own Trac system, is available
+  for you to practice for working with the FCM system. You will work through
+  the following activities:</p>
+
+  <ul>
+    <li><a href="#tutorial_create-ticket">Create a new ticket</a></li>
+
+    <li><a href="#tutorial_launch-gui">Launch the GUI</a></li>
+
+    <li><a href="#tutorial_create-branch">Create a branch</a></li>
+
+    <li><a href="#tutorial_checkout">Checkout a working copy</a></li>
+
+    <li><a href="#tutorial_change">Make changes to files in your working
+    copy</a></li>
+
+    <li><a href="#tutorial_commit">Commit your changes to the
+    repository</a></li>
+
+    <li><a href="#tutorial_extract">Test your changes</a></li>
+
+    <li><a href="#tutorial_merge">Merge changes from the trunk and resolve
+    conflicts</a></li>
+
+    <li><a href="#tutorial_review-ticket">Review changes</a></li>
+
+    <li><a href="#tutorial_merge-back">Commit to the trunk</a></li>
+
+    <li><a href="#tutorial_extra-extract">Extra activities on the extract and
+    build systems</a></li>
+
+    <li><a href="#tutorial_delete-branch">Delete your branch</a></li>
+
+    <li><a href="#tutorial_delete-final-comments">Final comments</a></li>
+  </ul>
+
+  <p>We recommend that you create a work area in your filespace, for example,
+  <samp>$HOME/tutorial/work</samp> for your working copy, and
+  <samp>$HOME/tutorial/build</samp> for your build.</p>
+
+  <p>If you have not already done so, you should set up your desktop
+  environment as described above in the <a href="#setup">How to set yourself up
+  to run FCM</a> section.</p>
+
+  <p>It is also worth knowing that the <a href=
+  "http://svnbook.red-bean.com/en/1.4/">Subversion Book</a> is a great source
+  of reference of Subversion features. In particular, the <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.basic.html">Fundamental Concepts</a>
+  and <a href="http://svnbook.red-bean.com/en/1.4/svn.tour.html">Basic
+  Usage</a> chapters are well worth reading.</p>
+
+  <h3 id="tutorial_create-ticket">Create a new ticket</h3>
+
+  <p><em>Trac is an integrated web-based issue tracker and wiki system. You
+  will use it to manage and keep track of changes in your project. The issue
+  tracker is called the ticket system. When you want to report a problem or
+  submit a change request, you will create a new ticket. In a typical
+  situation, you and/or your colleagues will make changes to your system in
+  order to resolve the problem or change request, and you will monitor these
+  changes via the ticket.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>launch a Trac system,</li>
+
+    <li>create a new Trac ticket,</li>
+
+    <li>search for a Trac ticket, and</li>
+
+    <li>accept a Trac ticket.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li><a href="overview.html">System Overview</a></li>
+
+    <li>Code Management System &gt; <a href=
+    "code_management.html#svn_basic">Basic Command Line Usage</a></li>
+
+    <li>Code Management System &gt; <a href="code_management.html#trac">Using
+    Trac</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_svn_trac">fcm
+    trac</a></li>
+  </ul>
+
+  <h4>Launch a Trac system</h4>
+
+  <p>To launch the Trac system for the tutorial: type and <kbd>Enter</kbd> the
+  following command:</p>
+  <pre>
+(SHELL PROMPT)$ fcm trac fcm:tutorial
+</pre>
+
+  <p>This is probably the first time you have used the <code>fcm</code>
+  command. The command has the general syntax:</p>
+  <pre>
+fcm &lt;sub-command&gt; [&lt;options...&gt;] &lt;arguments&gt;
+</pre>
+
+  <p>For example, if you type <code>fcm help</code>, it will display a listing
+  of what sub-commands are available, and if you type <code>fcm help
+  &lt;sub-command&gt;</code>, it will display help for that particular
+  sub-command.</p>
+
+  <p>The <code>trac</code> sub-command launches the corresponding Trac system
+  browser for a Subversion URL specified in your argument. In this case, we are
+  asking it to display the Trac system browser for the tutorial. The argument
+  <samp>fcm:tutorial</samp> is a FCM URL keyword and will be expanded by FCM
+  into a real Subversion URL (e.g.
+  <samp>svn://fcm1/tutorial_svn/tutorial</samp>). You are encouraged to use FCM
+  URL keywords throughout the tutorial, as it will save you a lot of
+  typing.</p>
+
+  <p>Note: Although we use the Trac system as a browser for a Subversion
+  repository, they do not interact in any other ways. Having access to a Trac
+  system does not guarantee the same privilege to a Subversion repository. In
+  particular, you should note the differences between the URLs of a Subversion
+  repository path and its equivalence in a Trac browser.</p>
+
+  <p>There are other ways to launch the Trac system for a project. If you know
+  its URL, you can launch the Trac system by entering it in the address box of
+  your favourite browser. If you often access a Trac system for a particular
+  project, you should bookmark it in your favourite browser.</p>
+
+  <h4>Create a new Trac ticket</h4>
+
+  <p>Click on <kbd>Login</kbd> just above the menu bar, enter your Unix/Linux
+  user ID as your user name and leave the password empty. Then click on
+  <kbd>OK</kbd> to proceed.</p>
+
+  <p>Once you have logged in, the <kbd>New Ticket</kbd> link will become
+  available on the menu bar. Click on it to display a new ticket form, where
+  you can enter details about your problem or change request. In the tutorial,
+  it does not matter what you enter, but you should feel free to play around
+  with wiki formatting when entering the <em>Full description</em>. (Click on
+  <kbd>WikiFormatting</kbd> to see how you can use it.) For example:</p>
+
+  <ul>
+    <li>Short summary:
+      <pre>
+Tutorial to change repository files and resolve conflicts with the trunk
+</pre>
+    </li>
+
+    <li>Full description:
+      <pre>
+In this tutorial, I shall:
+ 1. try out the FCM GUI and its functions
+ 2. play with WikiFormatting in Trac tickets     
+ 3. create a branch and checkout a working copy
+ 4. make changes to files in it
+ 5. commit my changes and assign the ticket for review
+ 6. record the review and assign the ticket back to the author
+ 7. merge in the trunk, and resolve any conflicts 
+ 8. merge my changes back to the trunk
+ 9. close the ticket
+ 10. delete my branch
+</pre>
+    </li>
+
+    <li>Feel free to select an option you desire for each of the other ticket
+    properties: Type, Component, Priority, Version and Milestone.</li>
+  </ul>
+
+  <p>At the bottom of the page, click the <kbd>Preview</kbd> button to see what
+  the description would look like. When you are happy, click the <kbd>Submit
+  changes</kbd> button. Trac will create the new ticket and return it in a
+  state where you can append to it.</p>
+
+  <p>When the ticket is created, you should get an automatic e-mail notication
+  from the Trac system. In real life, depending on the setting, the owner of
+  your Trac system may also get a similar e-mail notification. It is worth
+  noting that each time the ticket is modified, the Trac system will send out
+  an e-mail notification to you (the reporter) and anyone who modified the
+  ticket subsequently.</p>
+
+  <h4>Search for a Trac ticket</h4>
+
+  <p>You should remember the number of your new ticket, as you will have to
+  revisit it later.</p>
+
+  <p>In real work, it is often not practical to have to remember the numbers of
+  all the tickets you have created. Trac provides a powerful custom query for
+  searching a ticket. You can search for the ticket you have just created by
+  clicking the <kbd>View Tickets</kbd> link. Feel free to play with the custom
+  query tool. Add or remove filters and try grouping your results by different
+  categories.</p>
+
+  <p>In addition, you can search your ticket using the keyword
+  <kbd>Search</kbd> utility at the top right hand corner of each Trac page. (If
+  you enter <kbd>#&lt;number&gt;</kbd> in the search box, it will take you
+  directly to that ticket.) In the tutorial, however, it may be easiest if you
+  simply leave the tutorial Trac system open, so that you do not have to login
+  again when you come back to your ticket.</p>
+
+  <h4>Start work on your Trac ticket</h4>
+
+  <p>The status of the ticket is <em>new</em>. When you start working on a
+  problem reported in a ticket it is good practice to change the status to
+  <em>in_progress</em> to indicate that you are working on it. For the purpose
+  of the tutorial, however, this is entirely optional since you know you will
+  be doing all the work any way.</p>
+
+  <p>To start work on a ticket, click on <kbd>start work</kbd> in the
+  <em>Action</em> box at the bottom of the page, and then click on <kbd>Submit
+  changes</kbd>.</p>
+
+  <h3 id="tutorial_launch-gui">Launch the GUI</h3>
+
+  <p><em>The FCM GUI is a basic graphical wrapper for some of the common code
+  management commands. Most examples in this tutorial can be done via the GUI.
+  You can skip this section and use only the command line if that is what you
+  prefer. Where appropriate, usage examples will be given for both the command
+  line and the FCM GUI.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>launch the GUI from the command line, and</li>
+
+    <li>launch the GUI from Konqueror.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; <a href=
+    "code_management.html#svn_gui">Using the GUI</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_gui">fcm
+    gui</a></li>
+  </ul>
+
+  <h4>Launch the GUI from the command line</h4>
+
+  <p>You can launch the GUI from the command line. Change directory to your
+  work area and then type <code>fcm gui</code>.</p>
+
+  <h4>Launch the GUI from Konqueror</h4>
+
+  <p>Alternatively, open Konqueror and navigate to your work area. Right-click
+  in the file-manager window to bring up a menu. Select <kbd>Open with &gt; FCM
+  GUI</kbd>.</p>
+
+  <h3 id="tutorial_create-branch">Create a branch</h3>
+
+  <p><em>You create a branch by making a copy of your project at a particular
+  revision. Most often, this will be a particular revision of the trunk, i.e.
+  the main branch/development line in your project. A branch resides in the
+  repository. It allows you to work in parallel with your colleagues without
+  affecting one another, while keeping your changes under version
+  control.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>create a branch in a Subversion repository, and</li>
+
+    <li>update a ticket with a link to a branch.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; Branching &amp; Merging &gt; <a href=
+    "code_management.html#svn_branching_create">Creating Branches</a></li>
+
+    <li>Code Management Working Practices &gt; <a href=
+    "working_practices.html#branching">Branching &amp; Merging</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_svn_br">fcm
+    branch</a></li>
+  </ul>
+
+  <h4>Create a branch in a Subversion repository</h4>
+
+  <p><strong>Important note: please ensure that your branch is created from
+  revision 1 of the trunk here, or the tutorial on merge will fail to work
+  later.</strong></p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm branch --create</code> (or
+  simply <code>fcm br -c</code>) command. E.g.:</p>
+  <pre>
+(SHELL PROMPT)$ fcm br -c -n tutorial -r 1 --type test -k 2 fcm:tutorial
+</pre>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Branch</kbd> on the top menu bar of the
+  GUI. Check the <kbd>create</kbd> radio button and configure your branch
+  details as follow:</p>
+
+  <ul>
+    <li>Enter <code>fcm:tutorial</code> for the URL of the tutorial
+    repository.</li>
+
+    <li>Enter the short branch name. This must contain only alpha-numeric
+    characters and/or underscores, e.g. <code>tutorial</code>.</li>
+
+    <li>Enter <kbd>1</kbd> for the source revision. The trunk was last changed
+    at revision 2. To facilitate the generation of conflicts when you merge
+    with it, you will branch from revision 1 of the trunk.</li>
+
+    <li>Set the branch type. In this case click on <kbd>test</kbd>. This will
+    ensure the branch you create is a user test branch.</li>
+
+    <li>Leave the source type as <kbd>trunk</kbd> and the prefix option as
+    <kbd>normal</kbd>.</li>
+
+    <li>Enter the related Trac ticket number for the ticket you created
+    earlier.</li>
+
+    <li>As this is probably the first time you access the tutorial repository,
+    you should also enter <kbd>--password ""</kbd> (i.e.
+    <code>--password</code> followed by a pair of quotes) in the <kbd>Other
+    Options</kbd> entry box.</li>
+  </ul>
+
+  <p>Click on <kbd>Run</kbd> when you are ready.</p>
+
+  <p class="image"><img src="create_branch.png" alt="create my branch" /></p>
+
+  <p>You will be prompted to edit the message log file. A standard template is
+  automatically supplied for the commit. However, if you want to add extra
+  comment for the branch, please do so <strong>above</strong> the line that
+  says <samp>--Add your commit message ABOVE - do not alter this line or those
+  below--</samp>. When you are ready, save your change and exit the editor.
+  Answer <kbd>Yes</kbd> when you are prompted to go ahead and create the
+  branch.</p>
+
+  <p>Note: If you are on the command line, the Subversion command will prompt
+  you for a password the first time you access a repository. The password will
+  normally be cached by the client, and you will not have to specify a password
+  on subsequent access. If you are using the GUI, please refer to the section
+  on <a href="code_management.html#svn_gui_commands">GUI Commands</a> in the
+  next chapter for further information.</p>
+
+  <p>When creating branches for the first time, you will notice that FCM will
+  create and commit any missing sub-directories it needs to set up your branch
+  inside the repository, before creating your branch and commiting it.</p>
+
+  <p>Take a note of the revision number the branch was created at, and its
+  branch name. (The revision number is the number following the last output
+  that says "Committed revision". In the example above, the branch created at
+  <samp>[811]</samp> is called <samp>branches/test/frsn/r1_tutorial</samp>,
+  which is a branch of the <samp>tutorial</samp> project in the
+  <samp>svn://fcm1/tutorial_svn</samp> repository.)</p>
+
+  <h4>Update your ticket with a link to your branch</h4>
+
+  <p>If you wish, you can update your ticket with details of the branch. Note
+  that this step is entirely optional. It is useful for developments which will
+  take a long time to complete. For short lived branches, this step is probably
+  unnecessary.</p>
+
+  <p>In the ticket you have created, refer to the revision number in the
+  <kbd>Add/Change</kbd> box, for example:</p>
+  <pre>
+Created the branch [source:tutorial/branches/test/frsn/r1_tutorial@811] at [811].
+</pre>
+
+  <p>Note:</p>
+
+  <ul>
+    <li><samp>[source:tutorial/branches/test/frsn/r1_tutorial@811]</samp> is a
+    Trac wiki link. In this syntax, you do not have to put in the root URL,
+    (e.g. <samp>svn://fcm1/tutorial_svn/</samp>), but you should specify your
+    branch using the project name (<samp>tutorial</samp>), the branch name
+    (<samp>branches/test/frsn/r1_tutorial</samp>), and a revision number. Trac
+    will translate this into a link to that branch.</li>
+
+    <li>Trac will translate the syntax <code>[&lt;number&gt;]</code> into a
+    link to the numbered changeset.</li>
+  </ul>
+
+  <p>Click on <kbd>Preview</kbd> and check that the links work correctly, and
+  on <kbd>Submit changes</kbd> when you are ready.</p>
+
+  <h3 id="tutorial_checkout">Checkout a working copy</h3>
+
+  <p><em>A Subversion working copy is an ordinary directory tree on your local
+  system, containing a collection of files. It is your private working area in
+  which you can make changes before publishing them back to the repository. You
+  create a working copy by using the checkout command on some subtree of the
+  repository.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>checkout a Subversion working copy.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; <a href=
+    "code_management.html#svn_concepts">Basic Concepts</a></li>
+
+    <li>FCM Command Reference &gt; <a href=
+    "command_ref.html#fcm_svn_other">Other Subversion Commands</a></li>
+  </ul>
+
+  <h4>Checkout a Subversion working copy</h4>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm checkout</code> (or simply
+  <code>fcm co</code>) command. E.g.:</p>
+  <pre>
+(SHELL PROMPT)$ fcm co fcm:tutorial_br/test/frsn/r1_tutorial
+</pre>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Checkout</kbd> in the GUI, and enter the
+  URL of your branch, e.g. <samp>fcm:tutorial_br/test/frsn/r1_tutorial</samp>.
+  Note:</p>
+
+  <ul>
+    <li>In the example, we have replaced the leading part of the Subversion URL
+    <samp>svn://fcm1/tutorial_svn/tutorial/branches</samp> with the FCM URL
+    keyword <samp>fcm:tutorial_br</samp>. This is mainly to save you from
+    having to type in the full URL. However, you may find it easier to
+    copy-and-paste the full Subversion URL from the output generated when you
+    created the branch.</li>
+
+    <li>If you do not specify a local directory <var>PATH</var> in the
+    <code>checkout</code> command, it will create a working copy in your
+    current working directory, using the basename of the URL you are checking
+    out. For example, when you checkout the branch you have just created, the
+    command should create the working copy in <samp>$PWD/r1_tutorial</samp>.
+    Make a note of the location of your working copy, in case you forget where
+    you have put it.</li>
+
+    <li>If you do not specify a revision to checkout, it will checkout the
+    HEAD, i.e. the latest, revision.</li>
+  </ul>
+
+  <p>Click on <kbd>Run</kbd> - a working copy pointing to your branch will be
+  created. The GUI will automatically change directory to the top of your new
+  working copy.</p>
+
+  <p>Example:</p>
+  <pre>
+=&gt; svn co --revision HEAD svn://fcm1/tutorial_svn/tutorial/branches/test/frsn/r1_tutorial
+A    r1_tutorial/cfg
+A    r1_tutorial/cfg/ext.cfg
+A    r1_tutorial/doc
+A    r1_tutorial/doc/hello.html
+A    r1_tutorial/src
+A    r1_tutorial/src/subroutine
+A    r1_tutorial/src/subroutine/hello_c.c
+A    r1_tutorial/src/subroutine/hello_sub.f90
+A    r1_tutorial/src/module
+A    r1_tutorial/src/module/hello_constants.f90
+A    r1_tutorial/src/program
+A    r1_tutorial/src/program/hello.f90
+Checked out revision 811.
+</pre>
+
+  <h3 id="tutorial_change">Make changes to files in your working copy</h3>
+
+  <p><em>Subversion provides various useful commands to help you monitor your
+  working copy. The most useful ones are "diff", "revert" and "status". You
+  will also find "add", "copy", "delete" and "move" useful when you are
+  rearranging your files and directories.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>make and revert changes,</li>
+
+    <li>add and remove files,</li>
+
+    <li>inspect the status of a working copy, and</li>
+
+    <li>display changes in a working copy.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; <a href=
+    "code_management.html#svn_basic">Basic Command Line Usage</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_svn_add">fcm
+    add</a>, <a href="command_ref.html#fcm_svn_diff">fcm diff</a>, <a href=
+    "command_ref.html#fcm_svn_delete">fcm delete</a>, <a href=
+    "command_ref.html#fcm_svn_other">Other Subversion Commands</a></li>
+  </ul>
+
+  <h4>Make and revert changes</h4>
+
+  <p>For the later part of the tutorial to work, you must make the following
+  modification:</p>
+
+  <ul>
+    <li>Change to the <samp>src/module/</samp> sub-directory in your working
+    copy.</li>
+
+    <li>Edit <b>hello_constants.f90</b>, using your favourite editor, and
+    change: <samp>Hello World!</samp> to <kbd>Hello Earthlings!</kbd>. Save
+    your change and exit the editor.</li>
+  </ul>
+
+  <p>Try the following so that you know how to restore a changed file:</p>
+
+  <ul>
+    <li>Change to the <samp>src/subroutine/</samp> directory of your working
+    copy.</li>
+
+    <li>Make a change in file <b>hello_c.c</b>, using your favourite
+    editor.</li>
+
+    <li>To see that you have <strong>M</strong>odified this file: <em>command
+    line</em>: issue the <code>fcm status</code> command; <dfn>FCM GUI</dfn>:
+    click on <kbd>Status</kbd> and then on <kbd>Run</kbd></li>
+
+    <li>Run the <code>revert</code> command to get the file back unmodified:
+      <pre>
+(SHELL PROMPT)$ fcm revert hello_c.c
+</pre>
+    </li>
+  </ul>
+
+  <h4>Add and remove files</h4>
+
+  <p>You may also want to try the following FCM commands in your
+  <samp>doc/</samp> sub-directory. You can safely make changes here since they
+  will not interfere with your code changes.</p>
+
+  <ul>
+    <li>change to the <samp>doc/</samp> directory of your working copy.</li>
+
+    <li>Echo some text into a new file and then run the <code>add</code>
+    command, which lets the repository know you're adding a new file at the
+    next commit. For example:
+      <pre>
+(SHELL PROMPT)$ echo 'Some text' &gt;new_file.txt
+(SHELL PROMPT)$ fcm add new_file.txt
+</pre>
+    </li>
+
+    <li>Make a copy of <samp>hello.html</samp> and remove the original, using
+    the <code>copy</code> and <code>delete</code> commands. For example:
+      <pre>
+(SHELL PROMPT)$ fcm copy hello.html add.html
+(SHELL PROMPT)$ fcm delete hello.html
+</pre>
+    </li>
+
+    <li>You can use a simple <code>move</code> sub-command for the above
+    <code>copy</code> and <code>delete</code>.</li>
+  </ul>
+
+  <h4>Inspect the status of a working copy</h4>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm status</code> (or simply
+  <code>fcm st</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Status</kbd> and then on <kbd>Run</kbd>
+  to see what has changed.</p>
+
+  <p>Example:</p>
+  <pre>
+=&gt; svn status
+D      doc/hello.html
+A      doc/new_file.txt
+A  +   doc/add.html
+M      src/module/hello_constants.f90
+</pre>
+
+  <p>This confirms the actions you have taken. You have
+  <strong>D</strong>eleted a file, <strong>A</strong>dded a new file,
+  <strong>A</strong>dded a file with history (<strong>+</strong>) and
+  <strong>M</strong>odified another. It also confirms the action of the
+  <code>revert</code> command.</p>
+
+  <h4>Display changes in a working copy</h4>
+
+  <p>You can view the changes you have made to your working copy.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm diff --graphical</code> (or
+  simply <code>fcm di -g</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Diff</kbd> and then on
+  <kbd>Run</kbd>.</p>
+
+  <p>A listing of the files you have changed will be displayed, and a graphical
+  diff tool will open up for each modified file.</p>
+
+  <h3 id="tutorial_commit">Commit your changes to the repository</h3>
+
+  <p><em>The change in your working copy remains local until you commit it to
+  the repository where it becomes permanent. If you are planning to make a
+  large number of changes, you are encouraged to commit regularly to your
+  branch at appropriate intervals.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>commit your changes, and</li>
+
+    <li>inspect your changes using Trac.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; <a href=
+    "code_management.html#svn_basic">Basic Command Line Usage</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_svn_ci">fcm
+    commit</a></li>
+  </ul>
+
+  <h4>Commit changes</h4>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm commit</code> (or simply
+  <code>fcm ci</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Commit</kbd> and then on
+  <kbd>Run</kbd>.</p>
+
+  <p>A text editor will appear to allow you to edit the commit message. You
+  must add a commit message to describe your change <strong>above</strong> the
+  line that says <samp>--Add your commit message ABOVE - do not alter this line
+  or those below--</samp>. (A suggestion is given as the highlighted text in the
+  example below.) Your commit will fail if you do not enter a commit message.</p>
+
+  <p>Save your change and exit the editor. Answer <kbd>Yes</kbd> when you are
+  prompted to confirm the commit. For example:</p>
+  <pre>
+Starting nedit to create commit message ...
+Change summary:
+------------------------------------------------------------------------
+[Branch : branches/test/frsn/r1_tutorial]
+[Sub-dir: &lt;top&gt;]
+
+D      doc/hello.html
+A      doc/new_file.txt
+A  +   doc/add.html
+M      src/module/hello_constants.f90
+------------------------------------------------------------------------
+Commit message is as follows:
+------------------------------------------------------------------------
+In my tutorial branch, for #2:
+  1. Moved hello.html to add.html, and created a new document '''new_file.txt'''
+  2. Changed greeting in hello_constants.f90 to ''Hello Earthlings!''
+------------------------------------------------------------------------
+Adding         doc/add.html
+Deleting       doc/hello.html
+Adding         doc/new_file.txt
+Sending        src/module/hello_constants.f90
+Transmitting file data ..
+Committed revision 812.
+=&gt; svn update
+At revision 812.
+</pre>
+
+  <h4>Inspect changes using Trac</h4>
+
+  <p>Click on <kbd>Timeline</kbd> in Trac. Drill down to your changeset and see
+  how it appears. (Alternatively, if you enter <samp>[&lt;number&gt;]</samp>
+  into the search box at the top right, it will take you directly to the
+  numbered changeset.) For example:</p>
+
+  <p class="image"><img src="changeset.png" alt="commit my changes" /></p>
+
+  <p>Note:</p>
+
+  <ul>
+    <li>Wiki Formatting, used in the commit message, has customised the
+    changeset message.</li>
+
+    <li>All your changes are listed.</li>
+  </ul>
+
+  <h3 id="tutorial_extract">Test your changes</h3>
+
+  <p><em>You should test the changes in your branch before asking a colleague
+  to review them. FCM features a build system that allows you to build your
+  code easily. As your changes may be located in a repository branch and/or a
+  working copy, you should work with the extract system to extract the correct
+  code to build. The extract system allows you to extract code from the
+  repository, combining changes in different branches and your working copy,
+  before generating a configuration file and a suitable source tree for feeding
+  into the build system.</em></p>
+
+  <p><em>In this sub-section of the tutorial, you will be shown how to extract
+  and build the code from your branch. (There are some <a href=
+  "#tutorial_extra-extract">extra activities on the extract and build
+  systems</a> in a later sub-section of the tutorial should you want to explore
+  the extract and build systems in more depth.) In the example here, the
+  extract and build systems will be shown to you in their simplest form. In
+  real life, the managers of the systems you are developing code for will
+  provide you with more information on how to extract and build their
+  systems.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>set up a simple extract configuration file, and</li>
+
+    <li>perform simple extracts and builds.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li><a href="extract.html">The Extract System</a></li>
+
+    <li><a href="build.html">The Build System</a></li>
+  </ul>
+
+  <p>You should extract and build your code in a different directory to your
+  working copy. For example, you may want to create a sub-directory
+  <samp>$HOME/tutorial/build/</samp> and change to it:</p>
+  <pre>
+(SHELL PROMPT)$ mkdir -p $HOME/tutorial/build
+(SHELL PROMPT)$ cd $HOME/tutorial/build
+</pre>
+
+  <h4>Set up an extract configuration file</h4>
+
+  <p>To set up an extract configuration file from scratch, launch your
+  favourite editor and add the following lines:</p>
+  <pre>
+# Extract configuration, format version 1.0
+cfg::type              ext
+cfg::version           1.0
+
+# Extract destination root directory
+dest                   $HERE
+
+# Location of the source in the "r1_tutorial" branch
+repos::tutorial::base  fcm:tutorial_br/test/$LOGNAME/r1_tutorial
+
+# Extract all sub-directories under the above URL
+expsrc::tutorial::base src
+
+# Fortran and C compiler commands respectively
+# You may need to redefine these for different platforms
+bld::tool::fc          ifort
+bld::tool::cc          gcc
+</pre>
+
+  <p>Note:</p>
+
+  <ul>
+    <li>The <samp>dest</samp> declaration is set to <samp>$HERE</samp> the
+    directory containing the extract configuration file. If you decide to
+    extract to a different directory, you should modify its value. Please do
+    not put your build in the same location as your working copy.</li>
+
+    <li>Similarly, the <samp>repos::tutorial::base</samp> declaration is set to
+    <samp>fcm:tutorial_br/test/$LOGNAME/r1_tutorial</samp>. If you have named
+    your branch differently, you should modify its value.</li>
+  </ul>
+
+  <p>Save the file as <samp>ext.cfg</samp> and exit your editor.</p>
+
+  <h4>Perform an extract and a build</h4>
+
+  <p>Issue the command <code>fcm extract</code> and you should get an output
+  similar to the following:</p>
+  <pre>
+(SHELL PROMPT)$ fcm extract
+Extract system started on Tue Apr 24 13:54:06 2007.
+-&gt;Parse configuration: start
+Config file (ext): /net/home/h01/frsn/tutorial/build/ext.cfg
+-&gt;Parse configuration: 0 second
+-&gt;Setup destination: start
+Destination: /net/home/h01/frsn/tutorial/build
+-&gt;Setup destination: 0 second
+-&gt;Extract: start
+Destination status summary:
+  No of files added: 4
+Source status summary:
+  No of files from the base: 4
+-&gt;Extract: 1 second
+-&gt;TOTAL : 1 second
+Extract finished on Tue Apr 24 13:54:07 2007.
+</pre>
+
+  <p>If nothing goes wrong, you should end up with the sub-direcories
+  <samp>src/</samp> and <samp>cfg/</samp> in your working directory. The
+  <samp>src/</samp> contains a source tree to be built, and <samp>cfg/</samp>
+  should contain two configuration files: <samp>ext.cfg</samp> and
+  <samp>bld.cfg</samp>. The former is an expanded version of your extract
+  configuration file and the latter is a build configuration file. You can now
+  build your code by running the <code>fcm build</code> command:</p>
+  <pre>
+(SHELL PROMPT)$ fcm build 2&gt;err
+Build command started on Fri Oct 14 09:15:38 2005.
+-&gt;Parse configuration: start
+Config file (bld): /net/home/h01/frsn/tutorial/build/cfg/bld.cfg
+-&gt;Parse configuration: 0 second
+-&gt;Setup destination: start
+Destination: /net/home/h01/frsn/tutorial/build
+-&gt;Setup destination: 0 second
+-&gt;Setup build: start
+-&gt;Setup build: 0 second
+-&gt;Pre-process : start
+-&gt;Pre-process : 0 second
+-&gt;Scan dependency : start
+No. of files scanned for dependency: 4
+/net/home/h01/frsn/tutorial/build/Makefile: updated
+-&gt;Scan dependency : 1 second
+-&gt;Generate interface : start
+No. of generated Fortran interface: 1
+-&gt;Generate interface : 0 second
+-&gt;Make : start
+ifort -o hello_constants.o -I/home/h01/frsn/tutorial/build/inc -c
+  /home/h01/frsn/tutorial/build/src/tutorial/src/module/hello_constants.f90
+ifort -o hello.o -I/home/h01/frsn/tutorial/build/inc -c
+  /home/h01/frsn/tutorial/build/src/tutorial/src/program/hello.f90
+ifort -o hello_sub.o -I/home/h01/frsn/tutorial/build/inc -c
+  /home/h01/frsn/tutorial/build/src/tutorial/src/subroutine/hello_sub.f90
+gcc -o hello_c.o -I/home/h01/frsn/tutorial/build/inc -c
+  /home/h01/frsn/tutorial/build/src/tutorial/src/subroutine/hello_c.c
+ifort -o hello.exe /home/h01/frsn/tutorial/build/obj/hello.o
+  -L/home/h01/frsn/tutorial/build/lib -l__fcm__hello
+-&gt;Make : 3 seconds
+-&gt;TOTAL : 4 second
+Build command finished on Fri Oct 14 09:15:42 2005.
+</pre>
+
+  <p>The executable you have built is <samp>hello.exe</samp>, which is located
+  in the <samp>bin/</samp> sub-directory. You can test your executable by
+  running it. You should get an output similar to the following:</p>
+  <pre>
+(SHELL PROMPT)$ bin/hello.exe 
+Hello: Hello Earthlings!
+Hello_Sub: Hello Earthlings!
+Hello_Sub: maximum integer: 2147483647
+Hello_C: Hello World!
+</pre>
+
+  <h3 id="tutorial_merge">Merge changes from the trunk and resolve
+  conflicts</h3>
+
+  <p><em>Your branch is normally isolated from other development lines in your
+  project. However, at some point during your development, you may need to
+  merge your changes with those of your colleagues. In some cases, it is
+  desirable to merge changes regularly from the trunk to keep your branch up to
+  date with the latest development. The automatic merge provided by FCM allows
+  you to do this easily.</em></p>
+
+  <p><em>A merge results in a conflict if changes being applied to a file
+  overlap. FCM uses a graphical merge tool to help you resolve conflicts in
+  text files.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>merge changes from the trunk into your working copy, and</li>
+
+    <li>resolve conflicts in your working copy.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; Basic Command Line Usage &gt; <a href=
+    "code_management.html#svn_basic_conflicts">Resolving Conflicts</a></li>
+
+    <li>Code Management System &gt; <a href=
+    "code_management.html#svn_branching">Branching &amp; Merging</a></li>
+
+    <li>Code Management Working Practices &gt; <a href=
+    "working_practices.html#branching">Branching &amp; Merging</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_svn_cf">fcm
+    conflicts</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_svn_merge">fcm
+    merge</a></li>
+  </ul>
+
+  <h4>Merge changes from the trunk into a working copy</h4>
+
+  <p>Perform the merge in your working copy.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm merge</code> command.
+  E.g.</p>
+  <pre>
+(SHELL PROMPT)$ fcm merge trunk
+</pre>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Merge</kbd>. Enter <kbd>trunk</kbd> into
+  the Source entry box and click on <kbd>Run</kbd> to proceed.</p>
+
+  <p>If there is more than one revision of the source that you can merge with,
+  you will be prompted for the revision number you wish to merge from. You will
+  not be prompted in this case, because there is only one revision of the
+  source that you can merge with.</p>
+
+  <p>Answer <kbd>Yes</kbd> when you are prompted to go ahead with the
+  merge.</p>
+
+  <p>Example:</p>
+  <pre>
+Available Merges From /tutorial/trunk: 2
+About to merge in changes from tutorial/trunk@2 compared with tutorial/trunk@1
+This merge will result in the following changes:
+------------------------------------------------------------------------
+U    src/subroutine/hello_c.c
+C    src/module/hello_constants.f90
+------------------------------------------------------------------------
+Performing merge ...
+U    src/subroutine/hello_c.c
+C    src/module/hello_constants.f90
+</pre>
+
+  <h4>Resolve conflicts in a working copy</h4>
+
+  <p>The <samp>C</samp> status indicates that the file you changed is now in
+  conflict. If you run <code>status</code>, you will see extra files created by
+  the merge, which enable you to resolve the conflict using the 3-way
+  difference tool xxdiff:</p>
+  <pre>
+=&gt; svn status
+M      src/subroutine/hello_c.c
+?      src/module/hello_constants.f90.merge-left.r1
+?      src/module/hello_constants.f90.merge-right.r2
+?      src/module/hello_constants.f90.working
+C      src/module/hello_constants.f90
+</pre>
+
+  <p>You will now have to resolve the conflicts.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm conflicts</code> (or simply
+  <code>fcm cf</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Conflicts</kbd> and then on
+  <kbd>Run</kbd>.</p>
+
+  <p>The xxdiff program comes into play:</p>
+
+  <p class="image"><img src="xxdiff_tutorial.png" alt="3-way diff" /></p>
+
+  <p>See the sub-section on <a href=
+  "code_management.html#svn_basic_conflicts">resolving conflicts</a>, or the
+  <cite>xxdiff User's Manual</cite> (click on <em>Help</em>) to guide you
+  through this process. (If you do not want to learn how to use
+  <code>xxdiff</code> now, you can just click on the highlighted line in the
+  left hand column, and select <kbd>Exit with MERGE</kbd> from the
+  <em>File</em> menu. This saves the file you are merging in as the result of
+  the merge, i.e. you have <em>merged</em> the changes).</p>
+
+  <p>On resolving the conflict, you will be asked to run <code>svn
+  resolved</code>. Answer <kbd>Yes</kbd>.</p>
+
+  <p>If you now run <code>status</code>, you will notice that these extra
+  conflict files have disappeared.</p>
+
+  <p>Example:</p>
+  <pre>
+Conflicts in file: src/module/hello_constants.f90
+All merge conflicts resolved
+Resolved conflicted state of 'hello_constants.f90'
+=&gt; svn status
+M      src/subroutine/hello_c.c
+M      src/module/hello_constants.f90
+</pre>
+
+  <p>It is important to remember that the <code>merge</code> command only
+  applies changes to your working copy. Therefore, you must now commit the
+  change in order for it to become permanent in the repository. Similar to
+  other changes, it is a good practice to use <code>diff</code> to inspect the
+  changes before committing.</p>
+
+  <p>When you run <code>commit</code>, you will be prompted to edit the commit
+  log as usual. However, you may notice that a standard template is already
+  provided for you by the <code>merge</code> command. In most cases, the
+  standard message should be sufficient. However, if you want to add extra
+  comment to the commit, please do so <strong>above</strong> the line that says
+  <samp>--Add your commit message ABOVE - do not alter this line or those
+  below--</samp>. This is useful, for example, if there were significant issues
+  addressed in the merge.</p>
+
+  <h3 id="tutorial_review-ticket">Review changes</h3>
+
+  <p><em>For the purpose of this tutorial, we assume that your changes are
+  complete, have been tested and committed to the repository, and are now ready
+  for review. You should assign the ticket to the reviewer and inform him/her
+  where to find the changes you wish him/her to review. The reviewer will
+  record any issues in the ticket, perhaps linking to other documents as
+  required. Once completed, he/she will record the outcome in the ticket and
+  assign it back to the you.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>display changes in a branch, and</li>
+
+    <li>re-assign a ticket.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; Branching &amp; Merging &gt; <a href=
+    "code_management.html#svn_branching_info">Getting Information About
+    Branches</a></li>
+
+    <li>Code Management System &gt; <a href="code_management.html#trac">Using
+    Trac</a></li>
+
+    <li>Code Management Working Practices &gt; <a href=
+    "working_practices.html#tickets">Using Tickets</a></li>
+
+    <li>FCM Command Reference &gt; <a href="command_ref.html#fcm_svn_diff">fcm
+    diff</a></li>
+  </ul>
+
+  <h4>Display changes in a branch</h4>
+
+  <p>Before you ask someone to review your code, it is often a good idea to
+  have a look at the changes one more time. To view the changes in a branch,
+  you can look at all the changes relative to its base.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm diff --branch
+  --graphical</code> (or simply <code>fcm di -b -g</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Diff</kbd>. Check the box <kbd>Show
+  differences relative to the base of the branch</kbd>, and click on
+  <kbd>Run</kbd>.</p>
+
+  <p>You should be presented with the differences between the branch and the
+  trunk (since the last merge).</p>
+
+  <p>Note: you can also use the <code>--trac</code> (<code>-t</code>) option
+  instead of <code>--graphical</code> (<code>-g</code>) to view the changes in
+  a branch using Trac rather than using a graphical diff tool.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm diff --branch --trac</code>
+  (or simply <code>fcm di -b -t</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Diff</kbd>. Check the box <kbd>Show
+  differences relative to the base of the branch</kbd>, and select to display
+  diff in Trac. Click on <kbd>Run</kbd>.</p>
+
+  <p>Take note of the Trac URL for displaying the differences. The part that
+  begins with <code>diff:</code> is of particular interest to you, as it is a
+  Trac link that can be inserted into a Trac wiki/ticket. In the above example,
+  the Trac link would look like:
+  <samp>diff:/tutorial/trunk@2///tutorial/branches/test/frsn/r1_tutorial@813</samp>.</p>
+
+  <h4>Re-assign a ticket to a reviewer</h4>
+
+  <p>Back in your ticket, add an appropriate comment showing where to find your
+  changes, in the <em>Add/Change</em> box. Include a link to your branch and a
+  diff link (see above) in the comment. For example:</p>
+  <pre>
+The [log:tutorial/branches/test/frsn/r1_tutorial@811:813] branch proposes
+changes to the greeting in hello_constants.f90. It also contains some new
+documents. See
+[diff:/tutorial/trunk@2///tutorial/branches/test/frsn/r1_tutorial@813] for the
+changes.
+
+Fred, could you review the change, please?
+</pre>
+
+  <p>Note: the syntax
+  <samp>[log:tutorial/branches/test/frsn/r1_tutorial@811:813]</samp> will be
+  translated by Trac into a link to the revision log browser to display the log
+  between revision 811 and 813 of the
+  <samp>branches/test/frsn/r1_tutorial</samp> branch in the
+  <samp>tutorial</samp> project; and the syntax
+  <samp>[diff:/tutorial/trunk@2///tutorial/branches/test/frsn/r1_tutorial@813]</samp>
+  will be translated into a link to display the differences between the trunk
+  at revision 2 and the branch at revision 813. Click on <kbd>Preview</kbd> and
+  check that the links work correctly.</p>
+
+  <p>To re-assign a ticket to your reviewer, click on the <kbd>reassign
+  to</kbd> button in the <em>Action</em> box section and enter the reviewer's
+  User ID.</p>
+
+  <p>When you are ready, click on <kbd>Submit changes</kbd>.</p>
+
+  <h4>Reassign the ticket back to the author</h4>
+
+  <p>For the purpose of this tutorial, you will act as the reviewer of the
+  changes you have made. Following the review, you should record its outcome
+  and re-assign the ticket back to the author. Enter the comment <kbd>No issues
+  were found during the review</kbd>. Click on the <kbd>reassign to</kbd>
+  button in the <em>Action</em> box section, and enter your guest account name.
+  Click on <kbd>Submit changes</kbd> when you are ready.</p>
+
+  <h3 id="tutorial_merge-back">Commit to the trunk</h3>
+
+  <p><em>Your changes in the branch have been tested and reviewed. It is now
+  time to merge and commit it to the trunk. Once you have committed your
+  change, you will close your ticket to complete the work cycle.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>switch a working copy,</li>
+
+    <li>merge and commit your changes into the trunk, and</li>
+
+    <li>close a ticket</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; <a href=
+    "code_management.html#svn_branching">Branching &amp; Merging</a></li>
+
+    <li>Code Management Working Practices &gt; <a href=
+    "working_practices.html#branching">Branching &amp; Merging</a></li>
+
+    <li>FCM Command Reference &gt; <a href=
+    "command_ref.html#fcm_svn_switch">fcm switch</a></li>
+  </ul>
+
+  <h4>Switch a working copy to point to the trunk</h4>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm switch</code> (or simply
+  <code>fcm sw</code>) command. E.g.:</p>
+  <pre>
+(SHELL PROMPT)$ fcm sw trunk
+</pre>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Switch</kbd>. Enter <kbd>trunk</kbd> as
+  the URL and then click on <kbd>Run</kbd>.</p>
+
+  <p>To check that your working copy is pointing to the trunk, you should:
+  <em>command line</em>: issue the <code>fcm info</code> command; <dfn>FCM
+  GUI</dfn>: inspect the corresponding URL of your working copy.</p>
+
+  <h4>Merge and commit your changes into the trunk</h4>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm merge</code> command.
+  E.g.</p>
+  <pre>
+(SHELL PROMPT)$ fcm merge branches/test/frsn/r1_tutorial
+</pre>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Merge</kbd>. Enter the name of your
+  branch in the Source entry box, (e.g.
+  <samp>branches/test/frsn/r1_tutorial</samp>). Click on <kbd>Run</kbd> to
+  proceed.</p>
+
+  <p>Example:</p>
+  <pre>
+Available Merges From /tutorial/branches/test/frsn/r1_tutorial: 813 812
+About to merge in changes from /tutorial/branches/test/frsn/r1_tutorial@813
+  compared with /tutorial/trunk@2
+This merge will result in the following changes:
+------------------------------------------------------------------------
+D    doc/hello.html
+A    doc/new_file.txt
+U    src/module/hello_constants.f90
+A    add.html
+------------------------------------------------------------------------
+Performing merge ...
+D    doc/hello.html
+A    doc/new_file.txt
+U    src/module/hello_constants.f90
+A    add.html
+</pre>
+
+  <p>Since there is more than one revision available for merging, you will be
+  prompted for the revision number you wish to merge from. The default is the
+  last changed revision of your branch. which is the revision you want to merge
+  with, so you should just proceed with the default.</p>
+
+  <p>Answer <kbd>Yes</kbd> when you are prompted to go ahead with the
+  merge.</p>
+
+  <p>Since we merged in the latest changes from the trunk into the branch,
+  there should be no conflicts from this merge.</p>
+
+  <p>Once again, please remember that the merge command only changes your
+  working copy. You need to commit the change before it becomes permanent in
+  the repository. Before you commit to the trunk, however, it is often sensible
+  to have a last look at what you are going to change using the
+  <code>diff</code> command.</p>
+
+  <p>Note: We have set up the repository to prevent any commits to the trunk to
+  preserve the tutorial for other users, so your commit to the trunk will fail.
+  However, you should try doing it any way to complete the exercise.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm commit</code> (or simply
+  <code>fcm ci</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Commit</kbd> and then on
+  <kbd>Run</kbd>.</p>
+
+  <p>A text editor will appear to allow you to edit the commit message. You
+  must add a commit message to describe your change <strong>above</strong> the
+  line that says <samp>--Add your commit message ABOVE - do not alter this line
+  or those below--</samp>. Since you are going to commit changes to the trunk,
+  you should provide a useful message, including a link to your ticket. For
+  example:</p>
+  <pre>
+For #2: complete the tutorial:
+  1. Changed greeting in hello_constants.f90 to "Hello Earthlings!"
+  2. Moved hello.html to add.html, and created a new document ''new_file.txt''
+</pre>
+
+  <p>When you are ready, save your change and exit the editor.</p>
+
+  <p>As we have said before, the command will fail when you try to proceed with
+  the commit.</p>
+
+  <h4>Close your ticket</h4>
+
+  <p>As you have completed your work, you should now update and close your
+  ticket. In real life, you will typically include a closing comment with an
+  appropriate Trac wiki link to the changeset in the trunk that fixes the
+  ticket.</p>
+
+  <p>Since you cannot commit to the trunk in the tutorial, you can include a
+  Trac link to the latest changeset in your branch. For example, you can put
+  <samp>Fixed at changeset [813].</samp> in the comment. To mark the ticket as
+  <em>fixed</em>, move down to the <em>Action</em> box section, click on
+  <kbd>resolve as</kbd> and choose <kbd>fixed</kbd>. Use <kbd>Preview</kbd> to
+  ensure that your links work correctly. When you are happy, click on
+  <kbd>Submit changes</kbd>.</p>
+
+  <h3 id="tutorial_extra-extract">Extra activities on the extract and build
+  systems</h3>
+
+  <p><em>The extract and build systems are very flexible. If you have time, you
+  may want to explore their uses in more depth.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>extract from a working copy,</li>
+
+    <li>change a compiler flag, and</li>
+
+    <li>extract from a particular branch and/or revision from the
+    repository.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li><a href="extract.html">The Extract System</a></li>
+
+    <li><a href="build.html">The Build System</a></li>
+  </ul>
+
+  <h4>Extract from a working copy</h4>
+
+  <p>Modify the source files in your working copy and commit the changes back
+  to your branch in the repository. Re-run <code>fcm extract</code> and
+  <code>fcm build</code> and see the results of the changes. [The file(s) you
+  have changed should be updated by <em>extract</em>, and <em>build</em> should
+  only re-build the necessary code.]</p>
+
+  <p>In fact, you can test changes in your working copy directly using a
+  similar extract and build mechanism. In such case, you need to modify the
+  REPOS declaration. For example:</p>
+  <pre>
+repos::tutorial::base  $HOME/fcm/work/r1_tutorial
+</pre>
+
+  <h4>Change a compiler flag</h4>
+
+  <p>Modify the compiler flags, and re-run <code>fcm extract</code> and
+  <code>fcm build</code> and see the results of the changes. To modify the
+  compiler flags, edit your extract configuration file, and add the
+  declarations for changing compiler flags. For example:</p>
+  <pre>
+# Declare extra options for Fortran compiler
+bld::tool::fflags  -i8 -O3
+</pre>
+
+  <p>For further information on how to set your compiler flags, please refer to
+  the sub-section on <a href="build.html#basic_flags">Setting the compiler
+  flags</a>.</p>
+
+  <h4>Extract from a particular branch and/or revision</h4>
+
+  <p>Try extracting from an earlier revision of your branch. Suppose the HEAD
+  of your branch is revision 813, and the branch was created at an earlier
+  revision. You can extract your branch at, say, revision 811 by adding a
+  declaration in your extract configuration file:</p>
+  <pre>
+revision::tutorial::base  811
+</pre>
+
+  <p>You can also try extracting from the trunk. In such case, you will need to
+  modify the REPOS declaration in your extract configuration file. For
+  example:</p>
+  <pre>
+repos::tutorial::base  fcm:tutorial_tr/src
+
+# Extract with and without the following line and note the difference!
+revision::tutorial::base  1
+</pre>
+
+  <h3 id="tutorial_delete-branch">Delete your branch</h3>
+
+  <p><em>You should remove your branch when it is no longer required. When you
+  remove it, it becomes invisible from the HEAD revision, but will continue to
+  exist in the repository, should you want to refer to it in the
+  future.</em></p>
+
+  <p>After completing this sub-section, you will learn how to:</p>
+
+  <ul>
+    <li>list branches owned by you, and</li>
+
+    <li>delete a branch.</li>
+  </ul>
+
+  <p>Further reading:</p>
+
+  <ul>
+    <li>Code Management System &gt; Branching &amp; Merging &gt; <a href=
+    "code_management.html#svn_branching_list">Listing Branches Created by You
+    or Other Users</a></li>
+
+    <li>Code Management System &gt; Branching &amp; Merging &gt; <a href=
+    "code_management.html#svn_branching_delete">Deleting Branches</a></li>
+  </ul>
+
+  <h4>List branches owned by you</h4>
+
+  <p>If you forget what your branch is called and/or what other branches you
+  have created, you can get a listing of all the branches you have created in a
+  project.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm branch --list</code> (or
+  simply <code>fcm br -l</code>) command</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Branch</kbd>, and then on the
+  <kbd>list</kbd> radio button. Click on <kbd>Run</kbd>.</p>
+
+  <h4>Delete a branch</h4>
+
+  <p>Switch your working copy to point back to your branch. Before you do so,
+  revert any changes you have made in the working copy by issuing the <code>fcm
+  revert -R .</code> command. If a <samp>#commit_message#</samp> file exists,
+  remove it by issuing the <code>rm '#commit_message#'</code> command.</p>
+
+  <p><dfn>Command line</dfn>: issue the <code>fcm switch &lt;URL&gt;</code> (or
+  simply <code>fcm sw &lt;URL&gt;</code>) command.</p>
+
+  <p><dfn>FCM GUI</dfn>: click on <kbd>Switch</kbd>. Enter the name of your
+  branch as the URL and click on <kbd>Run</kbd> to proceed.</p>
+
+  <p>You can continue your work in the branch if you wish, but once you have
+  finished all the work, you should delete it. <dfn>Command line</dfn>: issue
+  the <code>fcm branch --delete</code> (or simply <code>fcm br -d</code>)
+  command. <dfn>FCM GUI</dfn>: click on <kbd>Branch</kbd> in the GUI. Check the
+  <kbd>delete</kbd> radio button, and click <kbd>Run</kbd> to proceed.</p>
+
+  <p>Example:</p>
+  <pre>
+URL: svn://fcm1/tutorial_svn/tutorial/branches/test/frsn/r1_tutorial
+Repository Root: svn://fcm1/tutorial_svn
+Repository UUID: cb858ce8-0f05-0410-9e64-efa98b760b62
+Revision: 813
+Node Kind: directory
+Last Changed Author:
+Last Changed Rev: 813
+Last Changed Date: 2005-11-09 09:11:57 +0000 (Wed, 09 Nov 2005)
+--------------------------------------------------------------------------------
+Branch Create Rev: 811
+Branch Create Date: 2005-11-09 08:34:22 +0000 (Wed, 09 Nov 2005)
+Branch Parent: svn://fcm1/tutorial_svn/tutorial/trunk@1
+--------------------------------------------------------------------------------
+Last Merge From Trunk: /tutorial/branches/test/frsn/r1_tutorial@813
+                       /tutorial/trunk@2
+Avail Merges Into Trunk: 813 812
+Starting nedit to create commit message ...
+Change summary:
+------------------------------------------------------------------------
+D    svn://fcm1/tutorial_svn/tutorial/branches/test/frsn/r1_tutorial
+------------------------------------------------------------------------
+Commit message is as follows:
+------------------------------------------------------------------------
+Deleted tutorial/branches/test/frsn/r1_tutorial.
+------------------------------------------------------------------------
+Deleting branch
+  svn://fcm1/tutorial_svn/tutorial/branches/test/frsn/r1_tutorial ...
+
+Committed revision 8.
+</pre>
+
+  <p>You will be prompted to edit the commit message file. A standard template
+  is automatically supplied for the commit. However, if you want to add extra
+  comment for the branch, please do so <strong>above</strong> the line that
+  says <samp>--Add your commit message ABOVE - do not alter this line or those
+  below--</samp>. Save your change and exit the editor.</p>
+
+  <p>Answer <kbd>Yes</kbd> when you are prompted to go ahead and delete this
+  branch.</p>
+
+  <p>Your working copy is now pointing to a branch that no longer exists at the
+  HEAD revision of the repository. If you want to try the tutorial again, you
+  may want to create another branch, and switch your working copy to point to
+  the new branch. Otherwise, you can remove your working copy by issuing a
+  careful <code>rm -rf</code> command.</p>
+
+  <h3 id="tutorial_delete-final-comments">Final comments</h3>
+
+  <p>We have guided you through the basics of the complete change process,
+  using recommended ways of working. Most of the basic and important commands
+  have been covered by the tutorial. (The exceptions are <code>fcm log</code>
+  and <code>fcm update</code>, which you may have to use regularly. For
+  information on these commands, please refer to the section on <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.tour.history.html#svn.tour.history.log">
+  svn log</a> and <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.tour.cycle.html#svn.tour.cycle.update">
+  Update Your Working Copy</a> in the <a href=
+  "http://svnbook.red-bean.com/en/1.4/">Subversion book</a>.) You should now be
+  in a position to continue with your development work with FCM. However, if at
+  any time you are unsure about any aspect of using FCM, please consult the
+  relevant section of the <a href="index.html">FCM User Guide</a>.</p>
+
+  <p>Feel free to use the tutorial, at any time, for testing out any aspect of
+  the system. You may wish to do this rather than use your own repository and
+  ticket system, to avoid cluttering them with unwanted junk.</p>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/index.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/index.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/index.html	(revision 1981)
@@ -0,0 +1,471 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="FCM User Guide" />
+  <meta name="keywords" content="FCM, user guide" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload="javascript: FCM.load('doc/user_guide/', 'user-guide');">
+  <div id="ukmo-logo">
+    <img src="logo.png" alt="UK Met Office" />
+  </div>
+
+  <h1>FCM User Guide</h1>
+
+  <address>
+    Release: 1-5
+  </address>
+
+  <address>
+    Latest content update: 22 January 2010. <span id="fcm-js-maintenance"></span>
+  </address>
+
+  <address>
+    Questions regarding this document or permissions to quote from it should be
+    directed to:
+  </address>
+
+  <address>
+    <a href="mailto:iprmanager@metoffice.gov.uk">IPR Manager</a><br />
+    Met Office<br />
+    FitzRoy Road<br />
+    Exeter, Devon<br />
+    EX1 3PB<br />
+    United Kingdom
+  </address>
+
+  <address>
+    &copy; Crown Copyright 2005-10
+  </address>
+
+  <address id="fcm-js-pdf"></address>
+
+  <h2>Contents</h2>
+
+  <ol>
+    <li><a href="introduction.html">Introduction</a></li>
+
+    <li><a href="overview.html">System Overview</a></li>
+
+    <li>
+      <a href="getting_started.html">Getting Started</a>
+
+      <ul>
+        <li><a href="getting_started.html#setup">How to set yourself up to run
+        FCM</a></li>
+
+        <li><a href="getting_started.html#tutorial">Tutorial</a></li>
+      </ul>
+    </li>
+
+    <li>
+      <a href="code_management.html">Code Management System</a>
+
+      <ul>
+        <li>
+          <a href="code_management.html#svn">Using Subversion</a>
+
+          <ul>
+            <li><a href="code_management.html#svn_concepts">Basic
+            Concepts</a></li>
+
+            <li>
+              <a href="code_management.html#svn_basic">Basic Command Line
+              Usage</a>
+
+              <ul>
+                <li><a href="code_management.html#svn_basic_keywords">URL &amp;
+                Revision Keywords</a></li>
+
+                <li><a href="code_management.html#svn_basic_diff">Examining
+                Changes</a></li>
+
+                <li><a href=
+                "code_management.html#svn_basic_conflicts">Resolving
+                Conflicts</a></li>
+
+                <li><a href="code_management.html#svn_basic_check">Adding and
+                Removing Files</a></li>
+
+                <li><a href="code_management.html#svn_basic_commit">Committing
+                Changes</a></li>
+              </ul>
+            </li>
+
+            <li>
+              <a href="code_management.html#svn_branching">Branching &amp;
+              Merging</a>
+
+              <ul>
+                <li><a href=
+                "code_management.html#svn_branching_create">Creating
+                Branches</a></li>
+
+                <li><a href="code_management.html#svn_branching_list">Listing
+                Branches Created by You or Other Users</a></li>
+
+                <li><a href="code_management.html#svn_branching_info">Getting
+                Information About Branches</a></li>
+
+                <li><a href=
+                "code_management.html#svn_branching_switch">Switching your
+                working copy to point to another branch</a></li>
+
+                <li><a href=
+                "code_management.html#svn_branching_delete">Deleting
+                Branches</a></li>
+
+                <li><a href=
+                "code_management.html#svn_branching_merge">Merging</a></li>
+              </ul>
+            </li>
+
+            <li>
+              <a href="code_management.html#svn_gui">Using the GUI</a>
+
+              <ul>
+                <li><a href="code_management.html#svn_gui_start">Starting the
+                GUI</a></li>
+
+                <li><a href="code_management.html#svn_gui_commands">GUI
+                Commands</a></li>
+
+                <li><a href="code_management.html#svn_gui_konqueror">Accessing
+                the GUI from Konqueror</a></li>
+              </ul>
+            </li>
+
+            <li><a href="code_management.html#svn_problems">Known Problems with
+            Subversion</a></li>
+          </ul>
+        </li>
+
+        <li>
+          <a href="code_management.html#trac">Using Trac</a>
+
+          <ul>
+            <li><a href="code_management.html#trac_login">Logging In</a></li>
+
+            <li><a href="code_management.html#trac_wiki">Using the Wiki
+            Pages</a></li>
+
+            <li><a href="code_management.html#trac_browser">Using the
+            Repository Browser</a></li>
+
+            <li><a href="code_management.html#trac_tickets">Using the Issue
+            Tracker</a></li>
+
+            <li><a href="code_management.html#trac_roadmap">Using the
+            Roadmap</a></li>
+
+            <li><a href="code_management.html#trac_timeline">Using the
+            Timeline</a></li>
+          </ul>
+        </li>
+      </ul>
+    </li>
+
+    <li>
+      <a href="working_practices.html">Code Management Working Practices</a>
+
+      <ul>
+        <li><a href="working_practices.html#changes">Making Changes</a></li>
+
+        <li><a href="working_practices.html#wc">Working Copies</a></li>
+
+        <li>
+          <a href="working_practices.html#branching">Branching &amp;
+          Merging</a>
+
+          <ul>
+            <li><a href="working_practices.html#branching_when">When to
+            Branch</a></li>
+
+            <li><a href="working_practices.html#branching_where">Where to
+            Branch From</a></li>
+
+            <li><a href="working_practices.html#branching_update">Merging From
+            the Trunk</a></li>
+
+            <li><a href="working_practices.html#branching_commit">Merging Back
+            to the Trunk</a></li>
+
+            <li><a href="working_practices.html#branching_delete">When to
+            Delete Branches</a></li>
+          </ul>
+        </li>
+
+        <li>
+          <a href="working_practices.html#binary">Working with Binary Files</a>
+
+          <ul>
+            <li><a href="working_practices.html#binary_conflicts">Resolving
+            Conflicts in Binary Files</a></li>
+
+            <li><a href="working_practices.html#binary_locking">Using
+            Locking</a></li>
+          </ul>
+        </li>
+
+        <li><a href="working_practices.html#messages">Commit Log
+        Messages</a></li>
+
+        <li>
+          <a href="working_practices.html#tickets">Trac Tickets</a>
+
+          <ul>
+            <li><a href="working_practices.html#tickets_create">Creating
+            Tickets</a></li>
+
+            <li><a href="working_practices.html#tickets_use">Using
+            Tickets</a></li>
+          </ul>
+        </li>
+
+        <li><a href="working_practices.html#packages">Creating
+        Packages</a></li>
+
+        <li><a href="working_practices.html#releases">Preparing System
+        Releases</a></li>
+
+        <li><a href="working_practices.html#rapid">Rapid vs Staged Development
+        Practises</a></li>
+      </ul>
+    </li>
+
+    <li>
+      <a href="extract.html">The Extract System</a>
+
+      <ul>
+        <li><a href="extract.html#command">The Extract Command</a></li>
+
+        <li>
+          <a href="extract.html#simple">Simple Usage</a>
+
+          <ul>
+            <li><a href="extract.html#simple_local">Extract from a local
+            path</a></li>
+
+            <li><a href="extract.html#simple_url">Extract from a Subversion
+            URL</a></li>
+
+            <li><a href="extract.html#simple_mirror">Mirror code to an
+            alternate location</a></li>
+          </ul>
+        </li>
+
+        <li>
+          <a href="extract.html#advanced">Advanced Usage</a>
+
+          <ul>
+            <li><a href="extract.html#advanced_multi">Extract from multiple
+            repositories</a></li>
+
+            <li><a href="extract.html#advanced_branches">Extract from multiple
+            branches</a></li>
+
+            <li><a href="extract.html#advanced_inherit">Inherit from a previous
+            extract</a></li>
+
+            <li><a href="extract.html#advanced_build">Extract - Build
+            Configuration</a></li>
+          </ul>
+        </li>
+
+        <li><a href="extract.html#verbose">Diagnostic verbose level</a></li>
+
+        <li><a href="extract.html#nosvn">When Subversion Is Not
+        Available</a></li>
+      </ul>
+    </li>
+
+    <li>
+      <a href="build.html">The Build System</a>
+
+      <ul>
+        <li><a href="build.html#command">The Build Command</a></li>
+
+        <li>
+          <a href="build.html#basic">Basic Features</a>
+
+          <ul>
+            <li><a href="build.html#basic_build">Basic build
+            configuration</a></li>
+
+            <li><a href="build.html#basic_extract">Build configuration via the
+            extract system</a></li>
+
+            <li><a href="build.html#basic_exename">Naming of
+            executables</a></li>
+
+            <li><a href="build.html#basic_flags">Setting the compiler
+            flags</a></li>
+
+            <li><a href="build.html#basic_interface">Automatic Fortran 9X
+            interface block</a></li>
+
+            <li><a href="build.html#basic_dependency">Automatic
+            dependency</a></li>
+          </ul>
+        </li>
+
+        <li>
+          <a href="build.html#advanced">Advanced Features</a>
+
+          <ul>
+            <li><a href="build.html#advanced_dependency">Further dependency
+            features</a></li>
+
+            <li><a href="build.html#advanced_blockdata">Linking a Fortran
+            executable with a BLOCKDATA program unit</a></li>
+
+            <li><a href="build.html#advanced_library">Creating library
+            archives</a></li>
+
+            <li><a href="build.html#advanced_pp">Pre-processing</a></li>
+
+            <li><a href="build.html#advanced_file-type">File type</a></li>
+
+            <li><a href="build.html#advanced_inherit">Inherit from a previous
+            build</a></li>
+
+            <li><a href="build.html#advanced_data">Building data files</a></li>
+          </ul>
+        </li>
+
+        <li><a href="build.html#verbose">Diagnostic verbose level</a></li>
+
+        <li><a href="build.html#overview">Overview of the build
+        process</a></li>
+      </ul>
+    </li>
+
+    <li>
+      <a href="system_admin.html">System Administration</a>
+
+      <ul>
+        <li>
+          <a href="system_admin.html#svn">Subversion</a>
+
+          <ul>
+            <li><a href="system_admin.html#svn_design">Repository
+            design</a></li>
+
+            <li><a href="system_admin.html#svn_create">Creating a
+            repository</a></li>
+
+            <li><a href="system_admin.html#svn_access">Access control</a></li>
+
+            <li><a href="system_admin.html#svn_hosting">Repository
+            hosting</a></li>
+
+            <li><a href="system_admin.html#svn_watch">Watching changes in log
+            messages</a></li>
+          </ul>
+        </li>
+
+        <li>
+          <a href="system_admin.html#trac">Trac</a>
+
+          <ul>
+            <li><a href="system_admin.html#trac_config">Trac
+            configuration</a></li>
+
+            <li><a href="system_admin.html#trac_hosting">Trac hosting</a></li>
+          </ul>
+        </li>
+
+        <li><a href="system_admin.html#fcm-keywords">FCM keywords</a></li>
+
+        <li><a href="system_admin.html#ext-bld-cfg">Extract and build
+        configuration</a></li>
+
+        <li><a href="system_admin.html#alternate_versions">Maintaining
+        alternate versions of namelists and data files</a></li>
+
+        <li><a href="system_admin.html#work-practise">Defining working
+        practises and policies</a></li>
+      </ul>
+    </li>
+
+    <li>
+      <a href="command_ref.html">FCM Command Reference</a>
+
+      <ul>
+        <li><a href="command_ref.html#fcm_config">fcm Configuration
+        File</a></li>
+
+        <li><a href="command_ref.html#fcm_bld">fcm build</a></li>
+
+        <li><a href="command_ref.html#fcm_ext">fcm extract</a></li>
+
+        <li><a href="command_ref.html#fcm_cmp-ext-cfg">fcm cmp-ext-cfg</a></li>
+
+        <li><a href="command_ref.html#fcm_gui">fcm gui</a></li>
+
+        <li><a href="command_ref.html#fcm_keyword-print">fcm
+        keyword-print</a></li>
+
+        <li>
+          <a href="command_ref.html#fcm_svn">FCM Code Management Commands</a>
+
+          <ul>
+            <li><a href="command_ref.html#fcm_svn_add">fcm add</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_br">fcm branch</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_ci">fcm commit</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_cf">fcm conflicts</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_rm">fcm delete</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_diff">fcm diff</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_merge">fcm merge</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_mkpatch">fcm mkpatch</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_switch">fcm switch</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_trac">fcm trac</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_update">fcm update</a></li>
+
+            <li><a href="command_ref.html#fcm_svn_other">Other Code Management
+            Commands</a></li>
+          </ul>
+        </li>
+      </ul>
+    </li>
+
+    <li><a href="further_info.html">Further Information</a></li>
+  </ol>
+
+  <p>Annex:</p>
+
+  <ul>
+    <li><a href="annex_quick_ref.html">Quick reference</a></li>
+
+    <li><a href="annex_fcm_cfg.html">Declarations in FCM central/user
+    configuration file</a></li>
+
+    <li><a href="annex_ext_cfg.html">Declarations in FCM extract configuration
+    file</a></li>
+
+    <li><a href="annex_bld_cfg.html">Declarations in FCM build configuration
+    file</a></li>
+  </ul>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/introduction.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/introduction.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/introduction.html	(revision 1981)
@@ -0,0 +1,112 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: Introduction</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - Introduction" />
+  <meta name="keywords" content="FCM, user guide" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Introduction</h1>
+
+  <p>This is the User Guide for the <em>Flexible Configuration Management</em>
+  system which is known as <em>FCM</em>. It is designed to tell you everything
+  you need to know if you want to develop code which has been configured within
+  FCM. In addition it also provides the extra information you will need if you
+  are system manager of a project within FCM.</p>
+
+  <p>Note: some hyperlinks in this document lead to pages that are internal to
+  the Met Office, and so will generally not work when this document is viewed
+  externally. Please accept our apologies if this causes you any
+  inconvenience.</p>
+
+  <p>This guide consists of the following sections:</p>
+
+  <dl>
+    <dt><a href="overview.html">System Overview</a></dt>
+
+    <dd>A brief description of the main features of FCM.</dd>
+
+    <dt><a href="getting_started.html">Getting Started</a></dt>
+
+    <dd>How to start using FCM. It includes a tutorial for you to work through
+    and familiarise yourself with some FCM activities.</dd>
+
+    <dt><a href="code_management.html">Code Management System</a></dt>
+
+    <dd>How to use the code management system to manage code changes.</dd>
+
+    <dt><a href="working_practices.html">Code Management Working
+    Practices</a></dt>
+
+    <dd>Recommended ways of working with the code management system.</dd>
+
+    <dt><a href="extract.html">The Extract System</a></dt>
+
+    <dd>How to extract code from the repository ready for building.</dd>
+
+    <dt><a href="build.html">The Build System</a></dt>
+
+    <dd>How to compile code using the build system.</dd>
+
+    <dt><a href="system_admin.html">System Administration</a></dt>
+
+    <dd>How to configure and maintain a new system within FCM.</dd>
+
+    <dt><a href="command_ref.html">FCM Command Reference</a></dt>
+
+    <dd>Detailed information about each of the <code>fcm</code> commands.</dd>
+
+    <dt><a href="further_info.html">Further Information</a></dt>
+
+    <dd>Where to find further information about FCM and about configuration
+    management in general.</dd>
+
+    <dt>Annex:</dt>
+
+    <dd>
+      <dl>
+        <dt><a href="annex_quick_ref.html">Quick reference</a></dt>
+
+        <dd>A quick reference to many useful FCM code management system
+        commands.</dd>
+
+        <dt><a href="annex_fcm_cfg.html">Declarations in FCM central/user
+        configuration file</a></dt>
+
+        <dd>Detailed definitions of what declarations are allowed in a
+        central/user configuration file.</dd>
+
+        <dt><a href="annex_ext_cfg.html">Declarations in FCM extract
+        configuration file</a></dt>
+
+        <dd>Detailed definitions of what declarations are allowed in an extract
+        configuration file.</dd>
+
+        <dt><a href="annex_bld_cfg.html">Declarations in FCM build
+        configuration file</a></dt>
+
+        <dd>Detailed definitions of what declarations are allowed in a build
+        file.</dd>
+      </dl>
+    </dd>
+  </dl>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/overview.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/overview.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/overview.html	(revision 1981)
@@ -0,0 +1,199 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: System Overview</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - System Overview" />
+  <meta name="keywords" content="FCM, user guide" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>System Overview</h1>
+
+  <p>The FCM system is designed to simplify the task of managing and building
+  source code. It consists of three main components.</p>
+
+  <dl>
+    <dt>Code Management (CM) System</dt>
+
+    <dd>
+      <p>This system provides facilities for making changes to source code in a
+      controlled and straightforward manner.</p>
+
+      <p>Version control is provided by the open source tool <a href=
+      "http://subversion.tigris.org/">Subversion</a>. The source code and its
+      history are stored in a central database which is called the repository.
+      Support for parallel working is provided through the use of branches.</p>
+
+      <p>The open source web-based tool <a href=
+      "http://trac.edgewall.org/">Trac</a> allows changes to be examined and
+      documented. It provides an integrated issue tracker, wiki and repository
+      browser.</p>
+    </dd>
+
+    <dt>Build System</dt>
+
+    <dd>
+      <p>This system allows source code to be compiled with a minimal amount of
+      configuration. Compilation time can be minimised through the use of
+      pre-compiled code and by using the parallel make facilities provided by
+      the open source tool <a href="http://www.gnu.org/software/make/"><em>GNU
+      Make</em></a>.</p>
+
+      <p>It provides a number of powerful features aimed primarily at building
+      Fortran 9x code.</p>
+    </dd>
+
+    <dt>Extract System</dt>
+
+    <dd>
+      <p>This system provides the interface between the CM and build systems.
+      Code is extracted and presented in a suitable form for the build system.
+      Code can be mirrored to a different build platform if necessary.</p>
+    </dd>
+  </dl>
+
+  <p>The diagram below illustrates how these components fit together.</p>
+
+  <p class="image"><img src="fcm_overview.png" alt="FCM system overview" /></p>
+
+  <p>The following sections discuss these components in more detail.</p>
+
+  <h2>Code Management System</h2>
+
+  <p>The CM system is built using a number of open source tools, in particular
+  Subversion and Trac.</p>
+
+  <p>Subversion is a modern version control tool with a large and rapidly
+  expanding user base. For a summary of its main features please refer to the
+  <a href="http://svnbook.red-bean.com/en/1.4/svn.intro.whatis.html">What is
+  Subversion?</a> section in the <a href=
+  "http://svnbook.red-bean.com/en/1.4/">Version Control with Subversion</a>
+  book.</p>
+
+  <p>Subversion is a generalised tool which can be used in lots of different
+  ways. This makes some day-to-day tasks more complex than they need be. FCM
+  defines a simplified process and appropriate naming conventions. It then adds
+  a layer on top of Subversion to provide a natural interface which is
+  specifically tailored to this process. Where appropriate it simply makes use
+  of the command line tools provided by Subversion. However, in other cases it
+  provides significant additional functionality, for example:</p>
+
+  <ul>
+    <li>By making some assumptions about the repository layout (i.e. by
+    imposing a standard working practise) FCM simplifies the task of creating
+    branches and enforces a standard branch naming convention.</li>
+
+    <li>Having defined working practises and standard log messages allows FCM
+    to greatly simplify the process of merging changes between branches.</li>
+
+    <li>FCM makes use of <a href="http://furius.ca/xxdiff/">xxdiff</a> (a
+    graphical merge tool) to simplify the process of resolving any conflicts
+    which result from a merge.</li>
+
+    <li>Code changes can be examined in graphical form using xxdiff FCM also
+    allows you to easily examine the changes made on a branch.</li>
+
+    <li>FCM allows you to check where any particular branch has been used and
+    which version is being used.</li>
+  </ul>
+
+  <p>FCM also provides a simple GUI which allows easy access to most of the
+  common commands which you will need.</p>
+
+  <p>Trac is the other main component of the CM system. It is a powerful web
+  based tool which helps you to manage your software project. It includes the
+  following features:</p>
+
+  <ul>
+    <li>A flexible issue tracker which can be used to keep track of bugs,
+    feature requests, etc. Each issue (known as a <q title=
+    "http://trac.edgewall.org/wiki/TracTickets">ticket</q> within Trac) can be
+    given a priority and assigned to a particular person. Changes made to your
+    Subversion repository can easily be traced to the relevant ticket. Where
+    appropriate, tickets can be used to record information about who has
+    reviewed each change.</li>
+
+    <li>A <q title="http://trac.edgewall.org/wiki/TracRoadmap">roadmap</q>
+    feature which helps you to plan and manage project releases. Each ticket
+    can be associated with a particular milestone. Trac can then easily show
+    you what features or fixes went into a particular release or what work
+    remains before a particular milestone is reached.</li>
+
+    <li>A <q title="http://trac.edgewall.org/wiki/TracWiki">wiki</q> which can
+    be used for project documentation.</li>
+
+    <li>A browser for viewing your Subversion repository which allows you to
+    browse the project tree / files and examine revision logs and
+    changesets.</li>
+
+    <li>A timeline view which summarises all the activity on a project (changes
+    to the tickets, wiki pages or the Subversion repository).</li>
+  </ul>
+
+  <h2>Build System</h2>
+
+  <p>The build system provides the following features:</p>
+
+  <ul>
+    <li>Automatic generation of Makefile's at build time based on a simple
+    configuration file.</li>
+
+    <li>Full dependency analysis at build time.</li>
+
+    <li>Automatic generation of Fortran 9X interface files at build time.</li>
+
+    <li>Support for non-standard source code. You can override automatic
+    dependency and compile rules in order to deal with code which does not
+    conform to the necessary coding standards.</li>
+
+    <li>Flexible control over compiler flags. Changes to compiler flags trigger
+    the appropriate re-compilation.</li>
+
+    <li>Support for Pre-processor directives. Changes to Pre-processor flags
+    trigger the appropriate re-compilation.</li>
+
+    <li>Support for pre-compiled object code to speed compilation time.</li>
+
+    <li>GNU <code>make</code> is used to perform the build. Build times on
+    multi-processor systems can be reduced by running parallel processes.</li>
+  </ul>
+
+  <h2>Extract System</h2>
+
+  <p>The extract system provides the following features:</p>
+
+  <ul>
+    <li>Extract code to a directory tree suitable for feeding into the build
+    system. Code can be combined from multiple repositories and branches. Local
+    user code can also be included.</li>
+
+    <li>Either a complete set of source code may be extracted or just a set of
+    changes relative to pre-compiled code.</li>
+
+    <li>A simple configuration file defines what code is required (and what
+    compile options are required). Typically, standard versions of these
+    configuration files are maintained within the repository. Users can then
+    define changes relative to these standard versions.</li>
+
+    <li>If necessary, code can be transferred to a different platform ready for
+    building.</li>
+  </ul>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/style.css
===================================================================
--- vendors/fcm/1-5/doc/user_guide/style.css	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/style.css	(revision 1981)
@@ -0,0 +1,260 @@
+/**********************************************************************
+ * CSS for FCM documents.
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+a:visited
+{
+    color: purple;
+}
+
+a:active,
+a:hover
+{
+    color: blue;
+}
+
+code,
+kbd,
+var
+{
+    color: navy;
+}
+
+
+/**********************************************************************
+ * BACKGROUND COLOUR
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+code,
+kbd,
+pre,
+samp,
+.image
+{
+    background: #eee;
+}
+
+
+/**********************************************************************
+ * BORDER
+ *
+ **********************************************************************/
+dl,
+kbd,
+pre,
+samp,
+.image
+{
+    border: thin solid #ddd;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: sans-serif;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1
+{
+    font-size: 200%;
+}
+
+h2
+{
+    font-size: 175%;
+}
+
+h3
+{
+    font-size: 150%;
+}
+
+h4,
+h5
+{
+    font-size: medium;
+}
+
+address
+{
+    font-size: small;
+}
+
+address,
+var
+{
+    font-style: normal;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+#fcm-content ul li
+{
+    list-style: none;
+}
+
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT DECORATION
+ *
+ **********************************************************************/
+a:link
+{
+    text-decoration: underline;
+}
+
+a:visited
+{
+    text-decoration: none;
+}
+
+a:active,
+a:hover
+{
+    text-decoration: underline;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+#ukmo-logo,
+#document-info address
+{
+    text-align: right;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5,
+dl,
+pre,
+.image
+{
+    border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+}
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/user_guide/style.html2ps.css
===================================================================
--- vendors/fcm/1-5/doc/user_guide/style.html2ps.css	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/style.html2ps.css	(revision 1981)
@@ -0,0 +1,137 @@
+/**********************************************************************
+ * CSS for html2ps for FCM documents.
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * FOREGROUND COLOUR
+ *
+ **********************************************************************/
+a:link
+{
+    color: blue;
+}
+
+
+/**********************************************************************
+ * FONT
+ *
+ **********************************************************************/
+body
+{
+    font-family: Helvetica;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    font-family: monospace;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+dt
+{
+    font-weight: bold;
+}
+
+
+/**********************************************************************
+ * CLASSIFICATION
+ *
+ **********************************************************************/
+code,
+kbd,
+samp
+{
+    white-space: nowrap;
+}
+
+
+/**********************************************************************
+ * TEXT ALIGNMENT
+ *
+ **********************************************************************/
+address,
+h1
+{
+    text-align: center;
+}
+
+
+/**********************************************************************
+ * DISPLAY
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ * BORDER-RADIUS
+ *
+ **********************************************************************/
+
+
+/**********************************************************************
+ * MARGIN
+ *
+ **********************************************************************/
+address
+{
+    margin: 1em;
+}
+
+img
+{
+    margin: 0.25em;
+}
+
+dl,
+dd
+{
+    margin-bottom: 0.5em;
+}
+
+dl
+{
+    margin-top: 0.5em;
+}
+
+@page
+{
+    margin: 2cm;
+}
+
+
+/**********************************************************************
+ * PADDING
+ *
+ **********************************************************************/
+h1,
+h2,
+h3,
+h4,
+h5
+{
+    padding: 0.4em;
+}
+
+dl,
+li,
+pre,
+.image
+{
+    padding: 0.2em;
+}
+
+code,
+kbd,
+samp,
+var
+{
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+}
Index: vendors/fcm/1-5/doc/user_guide/system_admin.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/system_admin.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/system_admin.html	(revision 1981)
@@ -0,0 +1,612 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: System Administration</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - System Administration" />
+  <meta name="keywords" content="FCM, user guide" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>System Administration</h1>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="introduction">Introduction</h2>
+
+  <p>This chapter provides an administration guide for managers of projects or
+  systems which are using FCM.</p>
+
+  <p>Note that, where this section refers to the <em>FCM team</em> this applies
+  only to Met Office users. External users will either need to refer to the
+  equivalent team within their organisation or will need to perfom these tasks
+  themselves.</p>
+
+  <h2 id="svn">Subversion</h2>
+
+  <h3 id="svn_design">Repository design</h3>
+
+  <p>The FCM system assumes that each project directory has sub-directories
+  called <em>trunk</em>, <em>branches</em> and <em>tags</em> (Subversion
+  recommends it but doesn't insist on it). We recommend that each project
+  within a Subversion repository is in a sub-directory of the repository
+  root.</p>
+  <pre>
+&lt;root&gt;
+    |
+    |-- &lt;project 1&gt;
+    |       |
+    |       |-- trunk
+    |       |-- branches
+    |       |-- tags
+    |
+    |-- &lt;project 2&gt;
+    |       |
+    |       |-- trunk
+    |       |-- branches
+    |       |-- tags
+    |
+    |-- ...
+</pre>
+
+  <p>In theory you could also have the project as the root directory or several
+  levels below the root directory. However, this is not tested and could cause
+  problems with some <code>fcm</code> commands so is best avoided.</p>
+
+  <p>You will need to decide whether to use a single project tree for your
+  system or whether to use multiple projects.</p>
+
+  <p>Advantages of a single project tree:</p>
+
+  <ul>
+    <li>Changes to any part of the system can always be committed as a single
+    logical changeset. If you split your system into multiple projects then you
+    may have occasions when a logical change involves more than one project and
+    hence requires multiple commits (and branches).</li>
+  </ul>
+
+  <p>Disadvantages of a single project tree:</p>
+
+  <ul>
+    <li>If you have a large system then your working copies may become very
+    large and unwieldy. Basic commands such as <code>checkout</code> and
+    <code>status</code> can become frustratingly slow if your working copy is
+    too large.</li>
+
+    <li>Depending on how you work, you may end up doing lots more merges of
+    files that are unrelated to your work.</li>
+  </ul>
+
+  <p>One common approach is to split the admin type files (things that only the
+  system manager should need to touch) into a separate project from the core
+  system files (which all the developers need access to). If you include any
+  large data files under version control you may also want to use a separate
+  project for them to avoid making your working copies very large when editing
+  code.</p>
+
+  <p>Note that there is often no obvious right or wrong answer so you just have
+  to make a decision and see how it works out. You can always re-arrange your
+  repository in the future (although be aware that this will break any changes
+  being prepared on branches at the time).</p>
+
+  <p>You also need to decide whether your system requires its own repository
+  (or multiple repositories) or whether it can share with another system.</p>
+
+  <ul>
+    <li>The main disadvantage of having separate repositories for each system
+    is the maintenance overhead (although this is almost all automated by the
+    FCM team so is not a big deal).</li>
+
+    <li>We normally configure a single Trac system per repository. If the
+    repository contains multiple systems then it makes it difficult to use the
+    Trac milestones to handle system releases. However, Trac now supports
+    restricting itself to a sub-directory within a repository so, again, this
+    is not a big deal.</li>
+
+    <li>If you share a repository with other systems then your revision numbers
+    can increase even when there are no changes to your system. This doesn't
+    matter but some people don't like it.</li>
+  </ul>
+
+  <p>For simplicity, in most cases you will probably want your own repository
+  for your system.</p>
+
+  <p>You will not normally want to have multiple repositories for a system. One
+  exception may be if you are storing large data files where you might not want
+  to keep all the old versions for ever. Removing old versions can't be done
+  without changing all the revision numbers which would mess up all your code
+  history and Trac tickets. Storing the large data files in a separate
+  repository reduces the impact if you do decide to remove old versions in the
+  future. One disadvantage of this approach is that, for the moment at least,
+  Trac only handles one repository so you will need a separate Trac system for
+  the data files.</p>
+
+  <p>For further details please see the section <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.reposadmin.planning.html#svn.reposadmin.projects.chooselayout">
+  Planning Your Repository Organization</a> from the Subversion book.</p>
+
+  <h3 id="svn_create">Creating a repository</h3>
+
+  <p>Normally the FCM team will help you to set up your initial repository.
+  However, it is quite simple if you need to do it yourself. First you need to
+  issue the command <code>svnadmin create /path/to/repos</code>. This creates
+  an empty repository which is now ready to accept an initial import. To do so,
+  you should create a directory tree in a suitable location, and issue the
+  <code>svn import</code> command. At the top level of your directory tree
+  should be the project directories. Each project should then contain three
+  directories <samp>trunk</samp>, <samp>branches</samp> and <samp>tags</samp>.
+  The directories <samp>branches</samp> and <samp>tags</samp> should be empty.
+  The directory <samp>trunk</samp> should contain your source files in a
+  directory structure you have chosen. For example, if your directory tree is
+  located at <samp>$HOME/foo</samp>, you will do the following to import it to
+  a new repository:</p>
+  <pre>
+(SHELL PROMPT)$ svnadmin create FOO_svn
+(SHELL PROMPT)$ svn import $HOME/foo file://$PWD/FOO_svn -m 'Initial import'
+Adding         FOO
+Adding         FOO/trunk
+Adding         FOO/trunk/doc
+Adding         FOO/trunk/doc/hello.html
+Adding         FOO/trunk/doc/world.html
+Adding         FOO/trunk/src
+Adding         FOO/trunk/src/bar
+Adding         FOO/trunk/src/bar/egg.f90
+Adding         FOO/trunk/src/bar/ham.f90
+Adding         FOO/branches
+Adding         FOO/tags
+
+Committed revision 1.
+</pre>
+
+  <p>Note that the <code>svnadmin</code> command takes a <var>PATH</var> as an
+  argument, as opposed to a URL for the <code>svn</code> command.</p>
+
+  <p>For further details please see the section <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.reposadmin.planning.html#svn.reposadmin.projects.chooselayout">
+  Planning Your Repository Organization</a> from the Subversion book.</p>
+
+  <h3 id="svn_access">Access control</h3>
+
+  <p>You will not normally want to allow anonymous write access to your
+  repository since this means that changes do not get identified with a userid.
+  Therefore system managers need to provide the FCM team with a list of users
+  who should have write access to their repositories. You may also want to
+  arrange passwords for these users although this is only necessary if you need
+  to prevent malicious access. Further restrictions such as preventing
+  anonymous read access or restricting write access to the trunk to a limited
+  set of users can be arranged if necessary.</p>
+
+  <h3 id="svn_hosting">Repository hosting</h3>
+
+  <p>The FCM team will organise the hosting of your repository. A number of
+  facilities will be set up for you as standard.</p>
+
+  <ul>
+    <li>Your repository will be set up on a central FCM server and access will
+    be provided via <code>svnserve</code> (which we use in preference to
+    <cite>Apache</cite> for performance reasons). The FCM team will advise you
+    of the URL.</li>
+
+    <li>Each night a full backup of your repository will be taken. An integrity
+    check will also be performed using the <code>svnadmin verify</code>
+    command.</li>
+
+    <li>Standard hook scripts will be set up to handle the following
+    post-commit tasks:
+
+      <ul>
+        <li>Each time a changeset is successfully committed an incremental dump
+        of the new revision is taken. This would allow the repository to be
+        recovered should the live repository become corrupted for whatever
+        reason.</li>
+
+        <li>A file is updated which records the latest revision of your
+        repository. This can be used by system managers to regularly check for
+        new commits in a cron job and perform any required actions (updating
+        files on a remote platform for instance). The FCM team can advise you
+        of the location of this file and show you some example scripts which
+        make use of it.</li>
+      </ul>
+    </li>
+
+    <li>Standard hook scripts will be set up to handle the following tasks for
+    changes in revision properties (pre-revprop-change/post-revprop-change):
+
+      <ul>
+        <li>If a user attempts to modify the log message of a changeset and
+        he/she is not the original author of the changeset, the
+        pre-revprop-change hook 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. (See the <a href=
+        "#svn_watch">next sub-section</a> for details.)</li>
+
+        <li>The post-revprop-change hook script updates the Trac SQLite
+        database following a successful change in the log message.</li>
+      </ul>
+    </li>
+  </ul>
+
+  <p>Additional hook scripts can be put in place if you have a requirement. The
+  use of hook scripts is discussed in the section <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.reposadmin.create.html#svn.reposadmin.create.hooks">
+  Repository Creation and Configuration</a> from the Subversion book.</p>
+
+  <p>Note that if you want to use a Subversion repository for your own
+  individual use there is no need to get the FCM team to host it. You can
+  simply create your repository and then use a <code>file://</code> URL to
+  access it.</p>
+
+  <h3 id="svn_watch">Watching changes in log messages</h3>
+
+  <p>You can set up a watch facility to monitor changes in revision log
+  messages in your repository. An obvious use of this facility is for system
+  managers to monitor changes of log messages affecting the trunks of their
+  projects. To set up the facility, you will need to add a
+  <samp>watch.cfg</samp> file to the root of your repository. (To avoid
+  checking out the whole repository, including every branch, make sure that you
+  checkout the root of your repository non-recursively, i.e. <code>fcm checkout
+  -N URL</code>.) The <samp>watch.cfg</samp> file is an INI-type file, with the
+  basic format:</p>
+  <pre>
+[repos_base]
+
+path/in/repos = list,of,watchers
+</pre>
+
+  <p>For example, if your repository is <samp>svn://fcm1/FCM_svn/</samp>, and
+  you want set up particular users to monitor changes of the log messages
+  affecting several areas within the repository, you may have something like
+  this:</p>
+  <pre>
+[FCM_svn]
+
+FCM/trunk/src            = fred,bob
+FCM/trunk/doc            = fred,bob,alice
+FCM/branches/dev/*/*/src = fred
+</pre>
+
+  <p>Later on, if <samp>dave</samp> attempts to modify the log message of a
+  changeset that affects the path <samp>FCM/trunk/src</samp>, <samp>fred</samp>
+  and <samp>bob</samp> will both be notified by e-mail.</p>
+
+  <h2 id="trac">Trac</h2>
+
+  <h3 id="trac_config">Trac configuration</h3>
+
+  <p>Normally the FCM team will set up your Trac system for you (using a script
+  which helps automate the configuration). This section describes some things
+  you may wish to be configured. This can be done when the Trac system is set
+  up or later if you are unsure what you will require at first.</p>
+
+  <h4 id="trac_access">Access control</h4>
+
+  <p>You will not normally want to allow anonymous users to make changes to
+  your Trac system since this means that changes may not get identified with a
+  userid. The FCM team will normally set up your Trac system such that any
+  authenticated users can make changes. Further restrictions such as
+  restricting write access to named accounts or preventing anonymous read
+  access can be arranged if necessary.</p>
+
+  <p>The system manager will normally be given <var>TRAC_ADMIN</var>
+  privileges. This allows them to do additional things which normal users
+  cannot do such as:</p>
+
+  <ul>
+    <li>Delete wiki pages (the latest version or the entire page).</li>
+
+    <li>Add or modify milestones, components and versions.</li>
+
+    <li>Modify ticket descriptions and delete ticket attachments.</li>
+
+    <li>Make wiki pages read-only.</li>
+
+    <li>Alter the permissions.</li>
+  </ul>
+
+  <p>For further details please see the section <a href=
+  "http://trac.edgewall.org/wiki/TracPermissions">Trac Permissions</a> from the
+  Trac documentation.</p>
+
+  <h4 id="trac_email">Email notification</h4>
+
+  <p>By default, each Trac system is configured such that the owner and
+  reporter and anyone on the <var>CC</var> list are notified whenever a change
+  is made to a ticket. If system mangers wish to be notified of all ticket
+  changes then this can also be configured. Alternatively, email notifications
+  can be disabled if they are not wanted.</p>
+
+  <h4 id="trac_milestones">Milestones</h4>
+
+  <p>Milestones are useful for identifying when tickets need to be resolved.
+  Typically, milestones may be particular system releases or time periods. The
+  FCM team can configure milestones for you when they set up your Trac system.
+  However, this is not strictly necessary since milestones can be set up via
+  the web interface using the admin account (go to the <q title=
+  "http://trac.edgewall.org/wiki/TracRoadmap">Roadmap</q> page).</p>
+
+  <h4 id="trac_misc">Other configurable items</h4>
+
+  <p>There are lots of other things that can be configured in your Trac system
+  such as:</p>
+
+  <ul>
+    <li>Custom fields</li>
+
+    <li>System icon</li>
+
+    <li>Stylesheets</li>
+  </ul>
+
+  <p>For further details please see the sections <a href=
+  "http://trac.edgewall.org/wiki/TracIni">The Trac Configuration File</a> and
+  <a href="http://trac.edgewall.org/wiki/TracTickets">The Trac Ticket
+  System</a> from the Trac documentation.</p>
+
+  <h3 id="trac_hosting">Trac hosting</h3>
+
+  <p>The FCM team will organise the hosting of your Trac system. It will be set
+  up on the same server that hosts your Subversion repository and access will
+  be provided via a web server. The FCM team will advise you of the URL. Each
+  night a backup of your Trac system will be taken in case the live system
+  should become corrupted for whatever reason.</p>
+
+  <h2 id="fcm-keywords">FCM keywords</h2>
+
+  <p>When you set up a repository for a new project, you will normally want the
+  FCM team to set up a URL keyword for it in the FCM central configuration
+  file. The name of the project should be a short string containing only word
+  characters.</p>
+
+  <p>Individual projects can store revision keywords using the Subversion
+  property <code>fcm:revision</code> at registered URLs. Using the UM as an
+  example: if <samp>UM</samp> is a registered URL keyword, you can add the
+  <code>fcm:revision</code> property at the head of the UM project by doing a
+  non-recursive checkout. E.g.:</p>
+  <pre>
+(prompt)$ fcm co -q -N fcm:um um
+(prompt)$ fcm pe fcm:revision um
+</pre>
+
+  <p>In the editor, add the following and <code>fcm commit</code>:</p>
+  <pre>
+VN6.3 = 402
+VN6.4 = 1396
+VN6.5 = 2599
+VN6.6 = 4913
+VN7.0 = 6163
+</pre>
+
+  <p>In a subsequent invocation of <code>fcm</code>, if a revision keyword is
+  specified for a URL in the UM namespace, the command will attempt to load it
+  from the <code>fcm:revision</code> property at the head of the UM project.
+  Revision keywords can also be defined in the FCM central configuration file
+  if you prefer.</p>
+
+  <p>If the project has an associated Trac browser, you can also declare
+  browser URL mapping in the central configuration file. This allows FCM to
+  associate the Subversion URL with a Trac browser URL. There is an automatic
+  default for mapping URLs hosted by the FCM team at the Met Office. External
+  users of FCM may want to adjust this default for their site.</p>
+
+  <p>To change the default browser URL mapping, you need to make 3
+  <code>SET::URL_BROWSER_MAPPING_DEFAULT::&lt;key&gt;</code> declarations in
+  your <samp>$FCM/etc/fcm.cfg</samp> file. There are 3 keys to this
+  declaration: <var>LOCATION_COMPONENT_PATTERN</var>,
+  <var>BROWSER_URL_TEMPLATE</var> and <var>BROWSER_REV_TEMPLATE</var>. The
+  <var>LOCATION_COMPONENT_PATTERN</var> is a Perl regular expression, which is
+  used to separate the scheme-specific part of a version control system URL
+  into a number of components by capturing its substrings. These components are
+  then used to fill in the numbered fields in the
+  <var>BROWSER_URL_TEMPLATE</var>. The template should have one more field than
+  the number of components captured by <var>LOCATION_COMPONENT_PATTERN</var>.
+  The last field is used to place the revision, which is generated via the
+  <var>BROWSER_REV_TEMPLATE</var>. This template should have a single numbered
+  field for filling in the revision number. This is best demonstrated by an
+  example. Consider the declarations:</p>
+  <pre>
+%pattern       ^//([^/]+)/(.*)$
+%url_template  http://{1}/intertrac/source:{2}{3}
+%rev_template  @{1}
+set::url_browser_mapping_default::location_component_pattern  %pattern
+set::url_browser_mapping_default::browser_url_template        %url_template
+set::url_browser_mapping_default::browser_rev_template        %rev_template
+</pre>
+
+  <p>If we have a Subversion URL <samp>svn://repos/path/to/a/file</samp>, the
+  <var>LOCATION_COMPONENT_PATTERN</var> will capture the components
+  [<samp>repos</samp>, <samp>path/to/a/file</samp>]. When this is applied to
+  the <code>BROWSER_URL_TEMPLATE</code>, <var>{1}</var> will be translated to
+  <samp>repos</samp> and <var>{2}</var> will be translated to
+  <samp>path/to/a/file</samp>. A revision is not given in this case, and so
+  <var>{3}</var> is inserted with an empty string. The result is
+  <samp>http://repos/intertrac/path/to/a/file</samp>. If the revision is
+  <samp>1357</samp>, the <var>BROWSER_REV_TEMPLATE</var> will be used to
+  translate it to <samp>@1357</samp>, which is then inserted to <var>{3}</var>
+  of the <var>BROWSER_URL_TEMPLATE</var>. The result is therefore
+  <samp>http://repos/intertrac/path/to/a/file@1357</samp>.</p>
+
+  <p>For more information on how to set up the keywords, please refer to
+  <a href="code_management.html#svn_basic_keywords">Repository &amp; Revision
+  Keywords</a> and the <a href="annex_fcm_cfg.html">Annex: Declarations in FCM
+  central/user configuration file</a>.</p>
+
+  <h2 id="ext-bld-cfg">Extract and build configuration</h2>
+
+  <p>The extract and build systems are very flexibile and can be used in lots
+  of different ways. It is therefore difficult to give specific advice
+  explaining how to configure them. However, based on experience with a number
+  of systems, the following general advice can be offered.</p>
+
+  <ul>
+    <li>Standard extract configuration files should be defined and stored
+    within the repository. Users then include these files into their
+    configurations, before applying their local changes.</li>
+
+    <li>The files should be designed to include one another in a hierarchy. For
+    example, you may have one core file which defines all the repository and
+    source locations plus a series of platform/compiler specific files which
+    include the core file. More complex setups are also possible if you need to
+    cater for other options such as different optimisation levels, 32/64 bit,
+    etc.</li>
+
+    <li>When including other configuration files, always make use of the
+    special <var>$HERE</var> variable (rather than, for instance, referring to
+    a fixed repository location). When your configuration file is parsed, this
+    special variable is normally expanded into the container directory of the
+    current configuration file. This means that the include statements should
+    work correctly whether you are referring to configuration files in the
+    repository trunk, in a branch or in a local working copy.</li>
+
+    <li>Make good use of user variables (e.g. <samp>%fred</samp>) to simplify
+    repetitive declarations and make your configuration files easier to
+    maintain.</li>
+
+    <li>Use continuation lines to split long lines and make them easier to read
+    (see the <a href="command_ref.html#fcm_config">FCM Command Reference</a>
+    section for further details about configuration files).</li>
+  </ul>
+
+  <p>Probably the best advice is to look at what has already been set up for
+  other systems. The FCM team can advise on the best systems to examine.</p>
+
+  <p>When you create a stable build you should keep an extract configuration
+  file that can reproduce the build. One easy way to do this is to create your
+  build using the standard configuration files and the latest versions of the
+  code. You can then save the expanded extract configuration file which is
+  created when you run the extract. To help document your stable build you can
+  use the command <code>fcm cmp-ext-cfg</code> to show what has changed.</p>
+
+  <h2 id="alternate_versions">Maintaining alternate versions of namelists and
+  data files</h2>
+
+  <p>Sometimes it is useful to be able to access particular revisions of some
+  directories from a FCM repository without having to go via Subversion.
+  Typical examples are namelist or data files used as inputs to a program. The
+  script <code>fcm_update_version_dir.pl</code> is designed to help with this.
+  It can be used to maintain a set of extracted version directories from a FCM
+  repository. The script has the following options and arguments:</p>
+
+  <dl>
+    <dt><code>-f [--full]</code></dt>
+
+    <dd>Specify the full mode, in which the versioned directories of each
+    specified item will be removed before being re-extracted.</dd>
+
+    <dt><code>-d [--dest] arg</code></dt>
+
+    <dd>Specify a destination <var>arg</var> for the extraction. If not
+    specified, the current working directory will be used as the base
+    path.</dd>
+
+    <dt><code>-u [--url] arg</code></dt>
+
+    <dd>Specify the source repository URL. This option is compulsory.</dd>
+  </dl>
+
+  <p>If an argument is specified, it must be the location of a configuration
+  file. Otherwise, the command reads its configuration from the standard input.
+  The configuration file is a line-based text file. Blank lines and lines
+  beginning with a <samp>#</samp> are ignored.</p>
+
+  <p>Each configuration line must contain the relative path of a sub-directory
+  under the specified source repository URL. If the path ends in <samp>*</samp>
+  then the path is expanded recursively and any sub-directories containing
+  regular files are added to the list of relative paths to extract.</p>
+
+  <p>Optionally, each relative path may be followed by a list of space
+  separated conditions. Each condition is a conditional operator
+  (<code>&gt;</code>, <code>&gt;=</code>, <code>&lt;</code>,
+  <code>&lt;=</code>, <code>==</code> or <code>!=</code>) followed by a
+  revision number or the keyword HEAD.</p>
+
+  <p>The command uses the revision log to determine the revisions at which the
+  relative path has been updated in the source repository URL. If these
+  revisions also satisfy the conditions set by the user, they will be
+  considered in the extraction. In full mode, everything is re-extracted. In
+  incremental mode, the version directories are only updated if they do not
+  already exist.</p>
+
+  <p>Example:</p>
+  <pre>
+(SHELL PROMPT)$ fcm_update_version_dir.pl -u fcm:ver_tr &lt;&lt;EOF
+namelists/VerNL_AreaDefinition   &gt;1000 !=1234
+namelists/VerNL_GRIBToPPCode     &gt;=600 &lt;3000
+namelists/VerNL_StationList      
+elements/*                       &gt;1000
+EOF
+</pre>
+
+  <p>N.B.</p>
+
+  <ol>
+    <li>Each time a sub-directory is revised, the script assigns a sequential
+    <em>v</em> number for the item. Each <em>v</em> number for a sub-directory,
+    therefore, is associated with a revision number. For each extracted
+    revision directory, there is a corresponding <em>v</em> number symbolic
+    link pointing to it.</li>
+
+    <li>The system also creates a symbolic link <samp>latest</samp> to point to
+    the latest extracted revision directory.</li>
+  </ol>
+
+  <h2 id="work-practise">Defining working practises and policies</h2>
+
+  <p>Some options on working practises and policies are defined in the chapter
+  on <a href="working_practices.html">Code Management Working Practices</a>.
+  Individual projects should document the approach they have adopted. In
+  addition, each project may also need to define its own working practices and
+  policies to suit its local need. For example each project may need to
+  specify:</p>
+
+  <ul>
+    <li>Whether changes are allowed directly on the trunk or whether branches
+    have to be used in all cases.</li>
+
+    <li>Whether all users are allowed to make changes to the trunk.</li>
+
+    <li>Whether Trac tickets have to be raised for all changes to the
+    trunk.</li>
+
+    <li>Whether Trac tickets should be raised for all support queries or
+    whether a Trac ticket should only be raised once there is an agreed
+    "issue".</li>
+
+    <li>Whether branches should normally be made from the latest code or from a
+    stable release.</li>
+
+    <li>Whether a user is allowed to resolve conflicts directly when merging a
+    branch into the trunk or whether he/she should merge the trunk into the
+    branch and resolve the conflicts in the branch first.</li>
+
+    <li>Whether all code changes to the trunk need to be reviewed.</li>
+
+    <li>What testing is required before changes can be merged to the
+    trunk.</li>
+
+    <li>Whether history entries are maintained in source files or whether
+    individual source files changes need to be described in the Subversion log
+    message.</li>
+
+    <li>Branch deletion policy.</li>
+
+    <li>Whether any files in the project require locking before being
+    changed.</li>
+  </ul>
+</body>
+</html>
Index: vendors/fcm/1-5/doc/user_guide/working_practices.html
===================================================================
--- vendors/fcm/1-5/doc/user_guide/working_practices.html	(revision 1981)
+++ vendors/fcm/1-5/doc/user_guide/working_practices.html	(revision 1981)
@@ -0,0 +1,835 @@
+<!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>
+  <meta name="generator" content=
+  "HTML Tidy for Linux/x86 (vers 1st December 2004), see www.w3.org" />
+
+  <title>FCM User Guide: Working Practices</title>
+  <meta name="author" content="FCM development team" />
+  <meta name="descriptions" content="User Guide - Working Practices" />
+  <meta name="keywords" content="FCM, user guide" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript" src="fcm.js">
+</script>
+</head>
+
+<body onload=
+"javascript: FCM.load('doc/user_guide/', null, [['.', 'FCM User Guide']]);">
+  <div id="document-info">
+    <address id="fcm-js-trail"></address>
+
+    <address id="fcm-js-maintenance"></address>
+  </div>
+
+  <h1>Code Management Working Practices</h1>
+
+  <div id="fcm-content"></div>
+
+  <h2 id="introduction">Introduction</h2>
+
+  <p>The previous chapter described how to use the various parts of the FCM
+  code management system. They also described aspects of working practises
+  which are enforced by the system. This section discusses other recommended
+  working practises. They are optional in the sense that you don't have to
+  follow them to use FCM. It is a matter for individual projects to decide
+  which working practises to adopt (although we expect most projects/systems at
+  the Met Office to adopt similar practises).</p>
+
+  <h2 id="changes">Making Changes</h2>
+
+  <p>This sub-section gives an overview of the recommended approach for
+  preparing changes. Particular topics are discussed in more detail in later
+  sub-sections where appropriate.</p>
+
+  <p>The recommended process for making a change is as follows:</p>
+
+  <ol>
+    <li>Before work starts on any coding you should make sure that there is a
+    Trac ticket open which explains the purpose of the change.
+
+      <ul>
+        <li>Make sure that you set the ticket milestone to indicate which
+        release of the system you are aiming to include your change in.</li>
+
+        <li>Accept the ticket to indicate that you are working on the
+        change.</li>
+
+        <li>For further advice on using tickets see <a href="#tickets">Trac
+        Tickets</a> later in this section.</li>
+      </ul>
+    </li>
+
+    <li>Create a branch
+
+      <ul>
+        <li>For very simple changes you may be happy to prepare your changes
+        directly on the trunk. For further details see <a href=
+        "#branching_when">When to Branch</a> later in this section.</li>
+
+        <li>Create your branch either from the latest revision or from a stable
+        release (see <a href="#branching_where">Where to Branch From</a> later
+        in this section).</li>
+      </ul>
+    </li>
+
+    <li>Prepare your code changes on the branch
+
+      <ul>
+        <li>Commit interim versions to your branch on a regular basis as you
+        develop your change. This makes it much easier to keep track of what
+        you're changing and to revert changes if necessary.</li>
+
+        <li>You may wish to merge in changes from the trunk. For further
+        details see <a href="#branching_update">Merging From the Trunk</a>
+        later in this section.
+
+          <ul>
+            <li>Make sure that you always commit any local changes to your
+            branch before doing a merge. Otherwise it becomes impossible to
+            distinguish your changes from those you have merged in. It is also
+            impossible to revert the merge without losing your local
+            changes.</li>
+
+            <li>Likewise, always commit the merge to your branch (after
+            resolving any conflicts) before making any further changes.</li>
+          </ul>
+        </li>
+
+        <li>Don't include unrelated changes. If you want to make some changes
+        which aren't really associated with your other changes then use a
+        separate ticket and branch for these changes.</li>
+      </ul>
+    </li>
+
+    <li>Once your changes are ready for review, update the Trac ticket to
+    record which revision of the branch is to be reviewed and assign the ticket
+    to your reviewer.</li>
+
+    <li>If the reviewer is happy with the change then he/she should update the
+    ticket to record that the change is approved and assign the ticket back to
+    you.
+
+      <ul>
+        <li>The reviewer can use the command <code>fcm&nbsp;diff&nbsp;--branch
+        &lt;branch_name&gt;</code> to examine all of the changes on the
+        branch.</li>
+
+        <li>If changes are necessary then these should be prepared and then the
+        ticket updated to refer to the new revision under review.</li>
+      </ul>
+    </li>
+
+    <li>Once the change is approved it can be merged back to the trunk
+
+      <ul>
+        <li>If you have been merging the latest changes from the trunk onto
+        your branch then the merge should be automatic. If not you may have
+        conflicts to resolve.</li>
+
+        <li>Make sure that each merge is a separate commit to the trunk. i.e.
+        Don't combine changes from several branches in one commit. This makes
+        it easier to reverse changes if necessary. It also makes the changeset
+        easier to understand.</li>
+
+        <li>Make sure that you use a good log message to describe your change.
+        For further details see <a href="#messages">Commit Log Messages</a>
+        later in this section.</li>
+
+        <li>Once the changes are commited, update the ticket to refer to the
+        changeset. Then the ticket can be closed.</li>
+      </ul>
+    </li>
+
+    <li>Once you are finished with the branch it should be deleted.</li>
+  </ol>
+
+  <h2 id="wc">Working Copies</h2>
+
+  <p>Some points to consider regarding working copies:</p>
+
+  <ol>
+    <li>In general we recommend that you keep your working copies in your home
+    directory. This ensures that any local changes which you accidently delete
+    can be recovered via the snapshot facility (on Met Office Exeter and
+    Reading based systems).</li>
+
+    <li>If the size of your project is small then you will probably find it
+    easiest to work with a complete copy of the project (either the trunk or
+    your branch). This means that you always have immediate access to all the
+    files and that you are always able to perform merges using your normal
+    working copy.</li>
+
+    <li>If you have a large project then you may prefer to work on a sub-tree
+    of your project.
+
+      <p><dfn>Pros:</dfn></p>
+
+      <ul>
+        <li>Subversion operations on your working copy are faster.</li>
+
+        <li>Your working copies use up less disk space. Remember that you may
+        be working on several changes at once on separate branches so you may
+        wish to have several working copies.</li>
+      </ul>
+
+      <p><dfn>Cons:</dfn></p>
+
+      <ul>
+        <li>You cannot always perform merge operations in sub-trees (if the
+        changes which need to be merged include files outside of your
+        sub-tree). To handle this we suggest that if you need to perform a
+        merge using a complete copy of your project you check it out in your
+        <var>$LOCALDATA</var> area (local disk space which is not backed up) to
+        be used purely for doing the merge.</li>
+
+        <li>You may find that your change involves more files than you
+        originally thought and that some of the files to be changed lie outside
+        of your working copy. You then have to make sure that you have
+        committed any changes before checking out a larger working copy.</li>
+      </ul>
+    </li>
+  </ol>
+
+  <h2 id="branching">Branching &amp; Merging</h2>
+
+  <h3 id="branching_when">When to Branch</h3>
+
+  <p>If you are making a reasonably large change which will take more than a
+  hour or two to prepare then there are clear advantages to doing this work on
+  a branch.</p>
+
+  <ul>
+    <li>You can commit intermediate versions to the branch.</li>
+
+    <li>If you need to merge in changes from the trunk then you have a record
+    of your files prior to the merge.</li>
+
+    <li>The version of the code which gets reviewed is recorded. If subsequent
+    changes are required then only those changes will need reviewing.</li>
+  </ul>
+
+  <p>However, if you are only making a small change (maybe only one line)
+  should you create a branch for this? There are two possible approaches:</p>
+
+  <dl>
+    <dt>Always Branch</dt>
+
+    <dd>
+      <p>ALL coding changes are prepared on branches.</p>
+
+      <p><dfn>Pros:</dfn> Same process is followed in all cases.</p>
+
+      <p><dfn>Cons:</dfn> The extra work required to create the branch and
+      merge it back to the trunk may seem unnecessary for a very small
+      change.</p>
+    </dd>
+
+    <dt>Branch When Needed</dt>
+
+    <dd>
+      <p>Small changes can be committed directly to the trunk (after testing
+      and code review).</p>
+
+      <p><dfn>Pros:</dfn> Avoids the overhead of using branches.</p>
+
+      <p><dfn>Cons:</dfn> Danger of underestimating the size of a change. What
+      you thought was a small change may turn out to be larger than you thought
+      (although you can always move it onto a branch if this happens).</p>
+    </dd>
+  </dl>
+
+  <p>This is a matter for project policy although, in general, we would
+  recommend the <cite>Branch When Needed</cite> approach.</p>
+
+  <h3 id="branching_where">Where to Branch From</h3>
+
+  <p>When you create a new branch you have two choices for which revision to
+  create the branch from:</p>
+
+  <dl>
+    <dt>The latest revision of the trunk</dt>
+
+    <dd>
+      <p>This is the preferred choice where possible. It minimised the chances
+      of conflicts when you need to incorporate your changes back onto the
+      trunk.</p>
+    </dd>
+
+    <dt>An older revision of the trunk</dt>
+
+    <dd>
+      <p>There are a number of reasons why you may need to do this. For
+      example:</p>
+
+      <ul>
+        <li>You are using a stable version to act as your <em>control</em>
+        data.</li>
+
+        <li>You need to know that your baseline is well tested (e.g. scientific
+        changes).</li>
+
+        <li>Your change may need to be merged with other changes relative to a
+        stable version for testing purposes or for use in a package (see
+        <a href="#packages">Creating Packages</a> later in this section).</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <h3 id="branching_update">Merging From the Trunk</h3>
+
+  <p>Once you've created your branch you need to decide whether you now work in
+  isolation or whether you periodically merge in the latest changes from the
+  trunk.</p>
+
+  <ul>
+    <li>Regularly merging from the trunk minimises the work involved when you
+    are ready to merge back to the trunk. You deal with any merge issues as you
+    go along rather than all at the end (by which time your branch and the
+    trunk could have diverged significantly).</li>
+
+    <li>One downside of merging from the trunk is that the baseline for your
+    changes is a moving target. This may not be what you want if you have some
+    <em>control</em> results that you have generated.</li>
+
+    <li>Another downside of merging from the trunk is that it may introduce
+    bugs. Although any code on the trunk should have been tested and reviewed
+    it is unlikely to be as well tested as code from a stable release.</li>
+
+    <li>Unless you originally created your branch from the latest revision of
+    the trunk it is unlikely that you are going to want to merge in changes
+    from the trunk. The exception to this is once your change is complete when
+    it may make sense to merge all the changes on the trunk into your branch as
+    a final step. This is discussed in <a href="#branching_commit">Merging Back
+    to the Trunk</a> below.</li>
+  </ul>
+
+  <p>So, there are basically three methods of working:</p>
+
+  <dl>
+    <dt>Branch from a stable version and prepare all your changes in
+    isolation</dt>
+
+    <dd>Necessary if you need to make your change relative to a well tested
+    release.</dd>
+
+    <dt>Branch from the latest code but then prepare all your changes in
+    isolation</dt>
+
+    <dd>Necessary if you need a stable baseline for your <em>control</em>
+    data.</dd>
+
+    <dt>Branch from the latest code and then update your branch from the trunk
+    on a regular basis</dt>
+
+    <dd>This is considered <em>best practice</em> for parallel working and
+    should be used where possible.</dd>
+  </dl>
+
+  <h3 id="branching_commit">Merging Back to the Trunk</h3>
+
+  <p>Before merging your change back to the trunk you will need to test your
+  change and get it reviewed. There are two options for what code to test and
+  review:</p>
+
+  <dl>
+    <dt>Test and review your changes in isolation, then merge to the trunk and
+    deal with any conflicts</dt>
+
+    <dd>
+      <p>This may be the best method if:</p>
+
+      <ul>
+        <li>Your changes have already been tested against a stable baseline and
+        re-testing after merging would be impracticable.</li>
+
+        <li>Your branch needs to be available for others to merge in its
+        changes in isolation.</li>
+      </ul>
+    </dd>
+
+    <dt>Merge in the latest code from the trunk before your final test and
+    review</dt>
+
+    <dd>
+      <p>This has the advantage that you are testing and reviewing the actual
+      code which will be committed to the trunk. However, it is possible that
+      other changes could get committed to the trunk whilst you are completing
+      your testing and review. There are several ways of dealing with this:</p>
+
+      <ul>
+        <li>Use locking to prevent it happening. The danger with this is that
+        you may prevent others from being able to get their change tested and
+        reviewed, hence inhibiting parallel devlopment.</li>
+
+        <li>Insist that the change is re-tested and reviewed. The problem with
+        this is that there is no guarantee that the same thing won't happen
+        again.</li>
+
+        <li>Merge in the new changes but don't insist on further testing or
+        review.
+
+          <ul>
+            <li>In most cases any changes won't clash so there is little to
+            worry about.</li>
+
+            <li>Where there are clashes then, in most cases, they will be
+            trivial with little danger of any side-effects.</li>
+
+            <li>Where the clashes are significant then, in most cases, this
+            will be very obvious whilst you are resolving the conflicts. In
+            this case you should repeat the testing and get the updates
+            reviewed.</li>
+          </ul>This is the recommended approach since it doesn't inhibit
+          parallel development and yet the chances of a bad change being
+          committed to the trunk are still very small.
+        </li>
+      </ul>
+
+      <p>You should also consider what can be done to minimise the time taken
+      for testing and review.</p>
+
+      <ul>
+        <li>Try to keep your changes small by breaking them down where
+        possible. Smaller changes are easier and quicker to review. This also
+        helps to minimise merge problems by getting changes back onto the trunk
+        earlier.</li>
+
+        <li>Automate your testing as far as possible to speed up the
+        process.</li>
+      </ul>
+    </dd>
+  </dl>
+
+  <p>Most projects will require the developer who prepared the change to merge
+  it back to the trunk once it is complete. However, larger projects may wish
+  to consider restricting this to a number of experienced / trusted
+  developers.</p>
+
+  <ul>
+    <li>This makes it easier to control and prioritise the merges.</li>
+
+    <li>It applies an extra level of quality control.</li>
+
+    <li>It minimises the risk of mistakes being merged back on to the trunk by
+    less experienced developers</li>
+
+    <li>Scientific developers can concentrate on the scientific work.</li>
+
+    <li>One issue is that the person doing the merge to the trunk may need help
+    from the original developer to prepare a suitable log message.</li>
+  </ul>
+
+  <h3 id="branching_delete">When to Delete Branches</h3>
+
+  <p>Once you are finished with your branch it is best to delete it to avoid
+  cluttering up the directory tree (remember that the branch and all its
+  history will still be available). There are two obvious approaches to
+  deleting branches:</p>
+
+  <dl>
+    <dt>Delete the branch as soon as it has been merged back to the trunk
+    (prior to closing any associated Trac ticket)</dt>
+
+    <dd>This is the tidiest approach which minimises the chances of old
+    branches being left around.</dd>
+
+    <dt>Delete the branch once a stable version of the system has been released
+    which incorporates your change</dt>
+
+    <dd>If a bug is found in your change during integration testing then you
+    can prepare the fix on the original branch (without having to do any
+    additional work to restore the branch).</dd>
+  </dl>
+
+  <h2 id="binary">Working with Binary Files</h2>
+
+  <p>The <code>fcm conflicts</code> command and <code>xxdiff</code> can only
+  help you resolve conflicts in text files. If you have binary files in your
+  repository you need to consider whether conflicts in these files would cause
+  a problem.</p>
+
+  <h3 id="binary_conflicts">Resolving Conflicts in Binary Files</h3>
+
+  <p>Conflicts in some types of binary files can be resolved manually. When you
+  are satisfied that the conflicts are resolved, issue the <code>fcm
+  resolved</code> command on the file to remove the conflict status. (You will
+  be prevented from committing if you have a conflicting file in your working
+  copy.)</p>
+
+  <p>If you have a conflicting MS Office 2003+ document, you may be able to
+  take advantage of the <kbd>Tools &gt; Compare and Merge Documents</kbd>
+  facility. Consider a working copy, which you have just updated from revision
+  100 to revision 101, and someone else has committed some changes to a file
+  <samp>doument.doc</samp> you are editing, you will get:</p>
+  <pre>
+(SHELL PROMPT)$ fcm conflicts
+Conflicts in file: document.doc
+document.doc: ignoring binary file, please resolve conflicts manually.
+(SHELL PROMPT)$ fcm status
+=&gt; svn st
+?      document.doc.r100
+?      document.doc.r101
+C      document.doc
+</pre>
+
+  <p>Open <samp>document.doc.r101</samp> with MS Word. In <kbd>Tools &gt;
+  Compare and Merge Documents...</kbd>, open <samp>document.doc</samp>. You
+  will be in Track Changes mode automatically. Go through the document to
+  accept, reject or merge any changes. Save the document and exit MS Word when
+  you are ready. Finally, issue the <code>fcm resolved</code> command to remove
+  the conflict status:</p>
+  <pre>
+(SHELL PROMPT)$ fcm resolved document.doc
+=&gt; svn resolved document.doc
+Resolved conflicted state of 'document.doc'
+(SHELL PROMPT)$ fcm status
+=&gt; svn st
+M      document.doc
+</pre>
+
+  <p>Another type of conflict that you may be able to resolve manually is where
+  the binary file is generated from another file which can be merged. For
+  instance, some people who use LaTeX also store a PDF version of the document
+  in the repository. In such cases it is easy to resolve the conflict by
+  re-generating the PDF file from the merged LaTeX file and then issuing the
+  <code>fcm resolved</code> command to remove the conflict status. Note that,
+  in this particular case, a better approach might be to automate the
+  generation of the PDF file outside of the repository.</p>
+
+  <h3 id="binary_locking">Using Locking</h3>
+
+  <p>For files with binary formats, such as artwork or sound, it is often
+  impossible to merge conflicting changes. In these situations, it is necessary
+  for users to take strict turns when changing the file in order to prevent
+  time wasted on changes that are ultimately discarded.</p>
+
+  <p>Subversion supports <q title=
+  "http://svnbook.red-bean.com/en/1.4/svn.advanced.locking.html">locking</q> to
+  allow you to prevent other users from modifying a file while you are
+  preparing changes. For details please refer to the chapter <a href=
+  "http://svnbook.red-bean.com/en/1.4/svn.advanced.locking.html">Locking</a>
+  from the Subversion book. Note that:</p>
+
+  <ul>
+    <li>FCM does not add any functionality to the locking commands provided by
+    Subversion.</li>
+
+    <li>If you need to lock a file you must do this in a working copy of the
+    trunk. There is nothing to stop you preparing the changes in a branch
+    (maybe you want to prepare the change in combination with a number of other
+    changes which do not require locking). However, you must always remember to
+    lock the file in the trunk first to prevent other users from preparing
+    changes to the file in parallel.</li>
+
+    <li>Locking isn't the only way of preventing conflicts with binary files.
+    If you only have a small project team and a small number of binary files
+    you may find it easier to use other methods of communication such as emails
+    or just talking to each other. Alternatively, you may have a working
+    practise that particular files are only modified by particular users.</li>
+  </ul>
+
+  <h2 id="messages">Commit Log Messages</h2>
+
+  <p>Certain guidelines should be adhered to when writing log messages for code
+  changes when committing to the trunk:</p>
+
+  <ul>
+    <li>Try to start off the log message with one line indicating the general
+    nature of the change. This helps developers to tell whether a change is
+    important to them when viewing the Trac timeline view.</li>
+
+    <li>If you want to use bullets in your message then make them compatible
+    with <a href="http://trac.edgewall.org/wiki/WikiFormatting">Wiki
+    Formatting</a>. For example:
+      <pre>
+No bullet
+ * First level bullet (single space at beginning)
+   * Second level bullet (three spaces at beginning)
+ 1. Numbered item instead of a bullet
+</pre>
+
+      <p>This will ensure that the log message is displayed with proper bullets
+      in the Trac changeset view. You can also include other types of wiki
+      formatting but please be aware that the message still needs to be
+      readable when simply viewed as text (e.g. via <code>fcm log</code>).</p>
+    </li>
+
+    <li>If your changes close a Trac ticket, make sure that your log message
+    refers to this using <a href="http://trac.edgewall.org/wiki/TracLinks">Trac
+    Links</a>, e.g. <code>Closes issue #26.</code></li>
+
+    <li>Don't leave blank lines at the end of your log message since they get
+    included in the message and, therefore, get included in the ouptut from
+    <code>fcm log</code>.</li>
+
+    <li>Take care to avoid making mistakes in your log messages since
+    correcting them involves additional work. However, if you realise that that
+    you've made a mistake don't leave it - get it corrected.
+
+      <ul>
+        <li>A log message can be corrected using the <code>propedit</code>
+        command,<br />
+        e.g. <code>fcm propedit svn:log --revprop -r REV</code><br />
+        Take care since this is an <q title=
+        "http://svnbook.red-bean.com/en/1.4/svn.advanced.props.html">unversioned</q>
+        property so you run the risk of losing information if you aren't
+        careful with your edits.</li>
+
+        <li>By default, FCM repositories are configured such that all users can
+        update log messages. If you are not the original author of the
+        changeset then the original author will be sent an e-mail informing
+        them of the change. Other users can also be informed of log message
+        changes if they wish (see the section <a href=
+        "system_admin.html#svn_watch">Watching changes in log messages</a> for
+        details).</li>
+      </ul>
+    </li>
+  </ul>
+
+  <p>There are two possible approaches to recording the changes to individual
+  files:</p>
+
+  <dl>
+    <dt>Maintain history entries in file headers</dt>
+
+    <dd>
+      <p><dfn>Pros:</dfn> You don't need access to the Subversion repository in
+      order to be able to view a files change history (e.g. external
+      collaborators).</p>
+
+      <p><dfn>Cons:</dfn></p>
+
+      <ul>
+        <li>History entries will produce clashes whenever files are changed in
+        parallel (although these conflicts are trivial to resolve).</li>
+
+        <li>Source files which are changed regularly can become cluttered with
+        very long history entries.</li>
+
+        <li>It is not possible to include history entries in some types of
+        file.</li>
+      </ul>
+    </dd>
+
+    <dt>Record which files have changed in the commit log message</dt>
+
+    <dd>
+      <p>The log message should name every modified file and explain why it was
+      changed. Make sure that the log message includes some sort of description
+      for every change. The value of the log becomes much less if developers
+      cannot rely on its completeness. Even if you've only changed comments,
+      note this in the message. For example:</p>
+      <pre>
+ * working_practices.html:
+   Added guidelines for writing log messages.
+</pre>
+
+      <p>If you make exactly the same change in several files, list all the
+      changed files in one entry. For example:</p>
+      <pre>
+ * code_management.html, system_admin.html, index.html:
+   Ran pages through tidy to fix HTML errors.
+</pre>
+
+      <p>It shouldn't normally be necessary to include the full path in the
+      file name - just make sure it is clear which of the changed files you are
+      referring to. You can get a full list of the files changed using
+      <code>fcm log -v</code>.</p>
+    </dd>
+  </dl>
+
+  <p>When you're committing to your own branch then you can be much more
+  relaxed about log messages. Use whatever level of detail you find helpful.
+  However, if you follow similar guidelines then this will help when it comes
+  to preparing the log message when your change is merged back to the
+  trunk.</p>
+
+  <h2 id="tickets">Trac Tickets</h2>
+
+  <h3 id="tickets_create">Creating Tickets</h3>
+
+  <p>There are two different approaches to using the issue tracker within
+  Trac:</p>
+
+  <dl>
+    <dt>All problems should be reported using Trac tickets</dt>
+
+    <dd>
+      <p><dfn>Pros:</dfn> The issue tracker contains a full record of all the
+      problems reported and enhancements requested.</p>
+
+      <p><dfn>Cons:</dfn> The issue tracker gets cluttered up with lots of
+      inappropriate tickets, (which can make it much harder to search the
+      issues and can slow down the response to simple issues).</p>
+
+      <ul>
+        <li>Duplicate tickets.</li>
+
+        <li>Issues already discussed in the documentation.</li>
+
+        <li>Problems which turn out to be unrelated to the system.</li>
+
+        <li>Problems which are poorly described.</li>
+
+        <li>Things which would be better solved by a quick conversation.</li>
+      </ul>
+    </dd>
+
+    <dt>A Trac ticket shouldn't be created until the issue has been agreed</dt>
+
+    <dd>
+      <p>Problems and issues should first be discussed with the project team /
+      system maintainers. Depending on the project, this could be via email, on
+      the newsgroups or through a quick chat over coffee.</p>
+
+      <p>Nothing is lost this way. Issues which are appropriate for the issue
+      tracker still get filed. It just happens slightly later, after initial
+      discussion has helped to clarify the best description for the issue.</p>
+    </dd>
+  </dl>
+
+  <h3 id="tickets_use">Using Tickets</h3>
+
+  <p>This sub-section provides advice on the best way of using tickets:</p>
+
+  <ol>
+    <li>In general, mature systems will require that there is a Trac ticket
+    related to every changeset made to the trunk. However this doesn't mean
+    that there should be a separate ticket for each change.
+
+      <ul>
+        <li>If a change is made to the trunk and then a bug is subsequently
+        found then, if this happens before the next release of the system, the
+        subsequent change can be recorded on the same ticket.</li>
+
+        <li>There can often be changes which don't really affect the system
+        itself since they are just system administration details. One way of
+        dealing with this is to open a ticket for each release in which to
+        record all such miscellaneous changes. It will probably be acceptable
+        to review these changes after they have been committed, prior to the
+        system release.</li>
+      </ul>
+    </li>
+
+    <li>Whenever you refer to source files/directories in tickets, make sure
+    that you refer to particular revisions of the files. This ensures that the
+    links will work in the future, even if those files are no longer in the
+    latest revision. For example:<br />
+    <samp>Changes now ready for review:
+    source:/OPS/branches/dev/frdm/r123_MyBranch@234</samp></li>
+
+    <li>For some types of information, simply appending to the ticket may not
+    be the best way of working. For example, design notes or test results may
+    be best recorded elsewhere, preferably in a wiki page. If using wiki pages
+    we recommend using a naming convention to identify the wiki page with the
+    associated ticket, for example:<br />
+    <samp>Please refer to [wiki:ticket/123/Design design notes]</samp><br />
+    <samp>See separate [wiki:ticket/123/TestResults test results]</samp><br />
+    Note that the square brackets have to be used since a page name containing
+    numbers is not recognised automatically.</li>
+  </ol>
+
+  <h2 id="packages">Creating Packages</h2>
+
+  <p>Sometimes you may need to combine the changes from several different
+  branches. For example:</p>
+
+  <ul>
+    <li>Your branch is just part of a larger change which needs to be tested in
+    its entirety before committing to the trunk.</li>
+
+    <li>You have some diagnostic code stored on a branch which you want to
+    combine with another branch for testing purposes.</li>
+  </ul>
+
+  <p>We refer to this as creating a <em>package</em>.</p>
+
+  <p>To create a package you simply create a new branch as normal. The
+  <em>type</em> should be a <em>package</em> or possibly a
+  <em>configuration</em> branch to help you distinguish it from your other
+  branches. You then simply merge in all of the branches that you want to
+  combine using <code>fcm merge</code>.</p>
+
+  <ul>
+    <li>The chance of conflicts will be reduced if the branches you are
+    combining have been created from the same point on the trunk. Your package
+    branch should also be created from the same point on the trunk.
+
+      <ul>
+        <li><em>Currently, <code>fcm merge</code> will not work unless this is
+        true.</em></li>
+      </ul>
+    </li>
+
+    <li>If further changes are made on a branch you are using in a package then
+    you can incorporate these changes into your package using <code>fcm
+    merge</code>. Note, however, that if you have a branch which is being used
+    in a package then you should avoid merging changes from the trunk into your
+    branch. If you do then it will be very difficult to get updates to your
+    branch merged into the package.</li>
+  </ul>
+
+  <p>The <code>fcm branch --info</code> command is very useful for maintaining
+  packages. It tells you all of the branches which have been merged into your
+  package and whether there are any more recent changes on those branches.</p>
+
+  <h2 id="releases">Preparing System Releases</h2>
+
+  <p>There are two ways of preparing system releases:</p>
+
+  <dl>
+    <dt>A system release is simply a particular revision of the trunk</dt>
+
+    <dd>
+      <p>In order to do this it will be necessary to restrict changes on the
+      trunk whilst the release is being prepared.</p>
+
+      <ul>
+        <li>Users can continue to develop changes not intended for inclusion in
+        this release on branches.</li>
+
+        <li>This may be a problem if preparing the release takes too long.</li>
+      </ul>
+    </dd>
+
+    <dt>Create a release branch where the release is finalised</dt>
+
+    <dd>
+      <p>You then lose the ability to be able to branch from the release.</p>
+
+      <p>It may be harder to identify what changes have been made between
+      releases (since you can't simply look at all the changesets made between
+      two revisions of the trunk).</p>
+    </dd>
+  </dl>
+
+  <h2 id="rapid">Rapid vs Staged Development Practises</h2>
+
+  <p>Most of this section on working practises has focussed on projects/systems
+  which are quite mature. Such systems are likely to have regular releases and
+  will, for example, insist that all changes to the trunk are reviewed and
+  tested.</p>
+
+  <p>If your system is still undergoing rapid development and has not yet
+  reached any sort of formal release then you will probably want to adopt a
+  much more relaxed set of working practises. For example:</p>
+
+  <ul>
+    <li>Changes don't need to be reviewed.</li>
+
+    <li>More changes will be committed to the trunk. Only very large changes
+    will be prepared on branches.</li>
+
+    <li>No requirement to have a Trac ticket associated with each change.</li>
+  </ul>
+
+  <p>We have tried to avoid building too many assumptions about working
+  practises into the FCM system. This gives projects the flexibility to decide
+  which working practises are appropriate for their system. Hopefully this
+  means that FCM can be used for large or small systems and for rapidly
+  evolving or very stable systems.</p>
+</body>
+</html>
Index: vendors/fcm/1-5/etc/fcm.cfg.eg
===================================================================
--- vendors/fcm/1-5/etc/fcm.cfg.eg	(revision 1981)
+++ vendors/fcm/1-5/etc/fcm.cfg.eg	(revision 1981)
@@ -0,0 +1,266 @@
+# ------------------------------------------------------------------------------
+# FCM central configuration file
+# ------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
+# Standard repository locations
+# ------------------------------------------------------------------------------
+
+# 3dVOM repository
+set::url::3dvom              svn://fcm9/3dVOM_svn/3dVOM
+
+# AAPP repository
+set::url::aapp               svn://fcm7/AAPP_svn/AAPP
+
+# AMV repository
+set::url::amv                svn://fcm7/AMV_svn/AMV
+
+# ANCIL repository
+set::url::ancil              svn://fcm8/ANCIL_svn/ANCIL
+
+# ATSR repository
+set::url::atsr               svn://fcm7/ATSR_svn/ATSR
+
+# BLASIUS repository
+set::url::blasius            svn://fcm2/BLASIUS_svn/BLASIUS		      
+
+# CICE repository
+set::url::cice               svn://fcm3/CICE_svn/CICE
+
+# CMA repository
+set::url::cma                svn://fcm9/CMA_svn/CMA
+
+# CVC repository
+set::url::cvc_admin          svn://fcm6/CVC_svn/Admin
+set::url::bufr               svn://fcm6/CVC_svn/BUFR
+set::url::bullseye           svn://fcm6/CVC_svn/Bullseye
+set::url::cat                svn://fcm6/CVC_svn/CAT
+set::url::deicing            svn://fcm6/CVC_svn/Deicing
+set::url::ea                 svn://fcm6/CVC_svn/EA  
+set::url::ensemble           svn://fcm6/CVC_svn/Ensemble
+set::url::gales              svn://fcm6/CVC_svn/Gales
+set::url::ifv                svn://fcm6/CVC_svn/IFV
+set::url::mogreps            svn://fcm6/CVC_svn/MOGREPS
+set::url::openroad           svn://fcm6/CVC_svn/OpenRoad
+set::url::powertable         svn://fcm6/CVC_svn/PowerTable
+set::url::qnh                svn://fcm6/CVC_svn/QNH
+set::url::tafs               svn://fcm6/CVC_svn/TAFS
+set::url::warnings           svn://fcm6/CVC_svn/WARNINGS
+
+# DA repository
+set::url::da                 svn://fcm5/DA_svn/DA
+
+# ENS repository
+set::url::ens                svn://fcm9/ENS_svn/ENS
+
+# ERSEM repository
+set::url::ersem              svn://fcm3/ERSEM_svn/ERSEM
+set::url::ersem_pml          svn://fcm3/ERSEM_svn/ERSEM_PML
+
+# FCM repository
+set::url::fcm                svn://fcm1/FCM_svn/FCM
+set::url::fcm_admin          svn://fcm1/FCM_svn/Admin
+
+
+# FLUME repository
+set::url::flume_metadata     svn://fcm2/FLUME_svn/metadata
+set::url::flume_framework    svn://fcm2/FLUME_svn/framework
+set::url::flume_models       svn://fcm2/FLUME_svn/models
+set::url::flume_jobs         svn://fcm2/FLUME_svn/jobs
+
+# FORMOST repository
+set::url::formost_local      svn://fcm9/FORMOST_svn/FORMOST_LOCAL
+set::url::formost_remote     svn://fcm9/FORMOST_svn/FORMOST_REMOTE
+
+# GEN repository
+set::url::gen                svn://fcm1/GEN_svn/GEN
+
+# GS repository
+set::url::gs                 svn://fcm9/GS_svn/GS
+
+# HadGOA repository
+set::url::hadgoa             svn://fcm9/HadGOA_svn/HadGOA
+
+# HadISD repository
+set::url::hadisd_gen         svn://fcm9/HadISD_svn/general
+set::url::hadisd_homog       svn://fcm9/HadISD_svn/homogenisation
+set::url::hadisd_qc          svn://fcm9/HadISD_svn/quality_control
+
+# IRIS repository
+set::url::iris               svn://fcm9/IRIS_svn/IRIS
+
+# LEM repository
+set::url::lem                svn://fcm2/LEM_svn/LEM
+
+# LINK repository
+set::url::link               svn://fcm1/LINK_svn/LINK
+
+# MASS_MIG repository
+set::url::mass_mig           svn://fcm9/MASS_MIG_svn/MASS_MIG
+
+# MOOSE repository
+set::url::moose              svn://fcm9/MOOSE_svn/MOOSE
+
+# MOSIG repository
+set::url::mosig              svn://fcm9/MOSIG_svn/MOSIG
+
+# MUMTI repository
+set::url::mumti              svn://fcm1/MUMTI_svn/Project
+
+# NEMO repository
+set::url::nemosys            svn://fcm3/NEMO_svn/NEMOSYS
+set::url::nemovar            svn://fcm3/NEMO_svn/NEMOVAR
+set::url::nemo               svn://fcm3/NEMO_svn/NEMO
+set::url::ioipsl             svn://fcm3/NEMO_svn/IOIPSL
+set::url::ocnasm             svn://fcm3/NEMO_svn/OCNASM
+set::url::nemoukmo           svn://fcm3/NEMO_svn/UKMO
+
+# NWPSAF repository
+
+set::url::meto_1dvar         svn://fcm7/NWPSAF_svn/MetOffice_1DVar		   
+set::url::ssmis_1dvar        svn://fcm7/NWPSAF_svn/ssmis_1DVar		   
+set::url::ssmis_pp           svn://fcm7/NWPSAF_svn/ssmis_PP		   
+
+# NWPWEB repository
+set::url::www_nwp            svn://fcm1/NWPWEB_svn/www_nwp
+
+# obsmon repository
+set::url::obsmon_dc          svn://fcm4/obsmon_svn/DC
+set::url::obsmon_rtm         svn://fcm4/obsmon_svn/RTM
+
+# ODB repository
+set::url::odb                svn://fcm4/ODB_svn/ODB
+
+# OCN repository
+set::url::polcoms            svn://fcm3/OCN_svn/POLCOMS
+
+# OPFC repository
+set::url::opfc               svn://fcm9/OPFC_svn/OPFC
+
+# OPS repository
+set::url::ops                svn://fcm4/OPS_svn/OPS
+set::url::ops_admin          svn://fcm4/OPS_svn/Admin
+set::url::ops_data           svn://fcm4/OPS_svn/Data
+set::url::ops_external       svn://fcm4/OPS_svn/External
+
+# OSTIA repository
+set::url::ostia              svn://fcm3/OSTIA_svn/OSTIA
+
+# PF repository
+set::url::pf                 svn://fcm5/PF_svn/PF
+
+# PostProc repository
+set::url::pp                 svn://fcm9/PostProc_svn/PostProc
+set::url::ppancil            svn://fcm9/PostProc_svn/PostProcAncil
+set::url::ppvssps            svn://fcm9/PostProc_svn/VerificationSSPS
+
+# PRISM repository
+set::url::oasis3             svn://fcm2/PRISM_svn/OASIS3
+set::url::oasis4             svn://fcm2/PRISM_svn/OASIS4
+set::url::prism_ukmo         svn://fcm2/PRISM_svn/PRISM_UKMO
+
+# radarnet repository
+set::url::radarnet4          svn://fcm9/radarnet_svn/radarnet4
+
+# RADSAT repository
+set::url::polar              svn://fcm7/RADSAT_svn/POLAR
+set::url::radsat             svn://fcm7/RADSAT_svn/RADSAT
+
+# ROPP repository
+set::url::ropp_doc           svn://fcm7/ROPP_svn/ropp_doc
+set::url::ropp_src           svn://fcm7/ROPP_svn/ropp_src
+set::url::ropp_test          svn://fcm7/ROPP_svn/ropp_test
+set::url::ropp_web           svn://fcm7/ROPP_svn/ropp_web
+
+# RTTOV repository
+set::url::rttov              svn://fcm7/RTTOV_svn/RTTOV 
+set::url::rttov8             svn://fcm7/RTTOV_svn/RTTOV8
+set::url::rttov9             svn://fcm7/RTTOV_svn/RTTOV9
+
+# SAUtils repository
+set::url::autoscat_global    svn://fcm7/SAUtils_svn/AUTOSCAT_Global
+set::url::autoscat_nae       svn://fcm7/SAUtils_svn/AUTOSCAT_NAE
+set::url::climetop           svn://fcm7/SAUtils_svn/CLIMETOP
+set::url::dataflow           svn://fcm7/SAUtils_svn/DataFlow
+set::url::gpsiwv_mon         svn://fcm7/SAUtils_svn/GPSIWV_Mon
+set::url::gpswv_nrt          svn://fcm7/SAUtils_svn/GPSWV_NRT
+set::url::gpsro_mon          svn://fcm7/SAUtils_svn/GPSRO_Mon
+set::url::iasi_mon           svn://fcm7/SAUtils_svn/IASI_Mon
+set::url::metstrike          svn://fcm7/SAUtils_svn/METSTRIKE
+set::url::scatwind_mon       svn://fcm7/SAUtils_svn/Scatwind_Mon
+
+# SBV repository
+set::url::sbv                svn://fcm6/SBV_svn/SBV
+set::url::sbv_admin          svn://fcm6/SBV_svn/Admin
+
+# SCS repository
+set::url::scs                svn://fcm1/SCS_svn/SCS
+set::url::scs_admin          svn://fcm1/SCS_svn/Admin
+set::url::tik                svn://fcm1/SCS_svn/TIK
+set::url::tt                 svn://fcm1/SCS_svn/TT
+
+# SPS repository
+set::url::sps                svn://fcm7/SPS_svn/SPS
+set::url::tigger             svn://fcm7/SPS_svn/Tigger
+set::url::sps_archive        svn://fcm7/SPS_svn/Archive
+
+# SURF repository
+set::url::surf               svn://fcm8/SURF_svn/SURF
+
+# SWARV repository
+set::url::swarv              svn://fcm9/SWARV_svn/SWARV
+
+# test repository
+set::url::test               svn://fcm1/test_svn/OPS
+
+# tutorial repository
+set::url::tutorial           svn://fcm1/tutorial_svn/tutorial
+
+# THORPEX repository
+set::url::thorpex            svn://fcm9/ENS_svn/ENS
+
+# TRUI repository
+set::url::trui               svn://fcm1/TRUI_svn/TRUI
+
+# UM repository
+set::url::um                 svn://fcm2/UM_svn/UM
+set::url::um_admin           svn://fcm2/UM_svn/Admin
+set::url::gcom               svn://fcm2/UM_svn/GCOM
+
+# UM tutorial repository
+set::url::um_tutorial        svn://fcm2/UM_TUTORIAL_svn/UM
+
+# utils repository
+set::url::app_publications   svn://fcm9/utils_svn/APP_publications
+set::url::asyncios           svn://fcm9/utils_svn/asyncIOS
+set::url::avapps_coldsoak    svn://fcm9/utils_svn/avapps_coldsoak
+set::url::avapps_verCB       svn://fcm9/utils_svn/avapps_verCB
+set::url::crmtest            svn://fcm9/utils_svn/cr_model_testing
+set::url::cr_valnote         svn://fcm9/utils_svn/cr_validation_note
+set::url::fray_utils         svn://fcm9/utils_svn/fray_utils
+set::url::hpss_tests         svn://fcm9/utils_svn/HPSS_tests
+set::url::jules_benchmarking svn://fcm9/utils_svn/jules_benchmarking
+set::url::jules_standalone   svn://fcm9/utils_svn/jules_standalone
+set::url::kid                svn://fcm9/utils_svn/KiD
+set::url::numerical_methods  svn://fcm9/utils_svn/numerical_methods
+set::url::wavefc             svn://fcm9/utils_svn/wave_forecasting
+
+# VAR repository
+set::url::var                svn://fcm5/VAR_svn/VAR
+set::url::var_admin          svn://fcm5/VAR_svn/Admin
+set::url::var_data           svn://fcm5/VAR_svn/Data
+
+# VER repository
+set::url::ver                svn://fcm6/VER_svn/VER
+set::url::ver_admin          svn://fcm6/VER_svn/Admin
+set::url::ver_archive        svn://fcm6/VER_svn/Archive
+
+# VMM repository
+set::url::vmm                svn://fcm9/VMM_svn/VMM
+
+# WW3 repository
+set::url::ww3                svn://fcm3/WW3_svn/WW3
+set::url::ww3_config         svn://fcm3/WW3_svn/WW3CONFIG
+set::url::ww3_utils          svn://fcm3/WW3_svn/WW3UTILS
+
+# EOF
Index: vendors/fcm/1-5/etc/fcm_gui.desktop
===================================================================
--- vendors/fcm/1-5/etc/fcm_gui.desktop	(revision 1981)
+++ vendors/fcm/1-5/etc/fcm_gui.desktop	(revision 1981)
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Comment=
+Exec=fcm gui %f
+Hidden=false
+Icon=wizard
+MimeType=inode/directory
+Name=FCM GUI
+Path=
+Terminal=0
+TerminalOptions=
+Type=Application
+X-KDE-SubstituteUID=false
+X-KDE-Username=
Index: vendors/fcm/1-5/examples/etc/regular-update.eg
===================================================================
--- vendors/fcm/1-5/examples/etc/regular-update.eg	(revision 1981)
+++ vendors/fcm/1-5/examples/etc/regular-update.eg	(revision 1981)
@@ -0,0 +1,50 @@
+#!/bin/sh
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+lockfile='lock'
+
+if [[ -f $lockfile ]]; then
+  exit
+fi
+
+touch $lockfile
+
+headrevfile='latest'
+lastrevfile='lastrev'
+
+# Current HEAD revision
+if [[ ! -r $headrevfile ]]; then
+  echo "HEAD revision file $headrevfile cannot be read, abort" >&2
+  rm -f $lockfile
+  exit 1
+fi
+headrev=`cat $headrevfile`
+headrev=`echo $headrev`
+
+# Revision at which this script is last run
+if [[ -r $lastrevfile ]]; then
+  lastrev=`cat $lastrevfile`
+  lastrev=`echo $lastrev`
+else
+  lastrev=0
+fi
+
+# Exit if HEAD revision is the same as the last run revision
+if (($lastrev == $headrev)); then
+  rm -f $lockfile
+  exit 0
+fi
+
+# ------------------------------------------------------------------------------
+# Do something...
+# ------------------------------------------------------------------------------
+
+# Update last run revision file
+echo $headrev >$lastrevfile
+rm -f $lockfile
+
+# EOF
Index: vendors/fcm/1-5/examples/lib/FCM/Admin/Config.pm
===================================================================
--- vendors/fcm/1-5/examples/lib/FCM/Admin/Config.pm	(revision 1981)
+++ vendors/fcm/1-5/examples/lib/FCM/Admin/Config.pm	(revision 1981)
@@ -0,0 +1,225 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Config;
+
+use File::Spec;
+
+my $USER      = (getpwuid($<))[0];
+my $HOME      = (getpwuid($<))[7];
+my $LOCALDATA = File::Spec->catfile(q{/project/local}, $USER);
+
+# Default values for read-only attributes
+my %DEFAULT_R = (
+    admin_email          => q{fcm@metoffice.gov.uk},
+    fcm_home             => $HOME,
+    fcm_dist_on_desktops => q{/project/ukmo/utils/supported/portable},
+    fcm_dist_on_HPCs     => ['hpc1f:FCM/work/FCM/'],
+    fcm_wc               => File::Spec->catfile($HOME, qw{FCM work FCM}),
+    fcm_admin_wc         => File::Spec->catfile($HOME, qw{FCM work Admin}),
+    trac_gid             => scalar(getgrnam(q{apache})),
+    user_number_min      => 500,
+);
+
+# Default values for read-write attributes
+my %DEFAULT_RW = (
+    svn_backup_dir     => File::Spec->catfile($HOME, qw{svn backups}),
+    svn_dump_dir       => File::Spec->catfile($HOME, qw{svn dumps}),
+    svn_hook_dir       => File::Spec->catfile($HOME, qw{FCM work Admin svn-hooks}),
+    svn_live_dir       => File::Spec->catfile($LOCALDATA, qw{svn live}),
+    svn_passwd_file    => q{passwd},
+    svn_project_suffix => q{_svn},
+    trac_backup_dir    => File::Spec->catfile($HOME, qw{trac backups}),
+    trac_live_dir      => File::Spec->catfile($LOCALDATA, qw{trac live}),
+    trac_ini_file      => q{trac.ini},
+    trac_passwd_file   => q{trac.htpasswd},
+);
+
+my $INSTANCE;
+
+# ------------------------------------------------------------------------------
+# Returns a unique instance of this class.
+sub instance {
+    my ($class) = @_;
+    if (!$INSTANCE) {
+        $INSTANCE = bless({%DEFAULT_R, %DEFAULT_RW}, $class);
+    }
+    return $INSTANCE;
+}
+
+# ------------------------------------------------------------------------------
+# Getters
+for my $name (keys(%DEFAULT_R), keys(%DEFAULT_RW)) {
+    no strict qw{refs};
+    my $getter = qq{get_$name};
+    *$getter = sub {
+        my ($self) = @_;
+        return $self->{$name};
+    };
+}
+
+# ------------------------------------------------------------------------------
+# Setters
+for my $name (keys(%DEFAULT_RW)) {
+    no strict qw{refs};
+    my $setter = qq{set_$name};
+    *$setter = sub {
+        my ($self, $value) = @_;
+        $self->{$name} = $value;
+    };
+}
+
+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.
+
+=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.
+
+=item $config->get_admin_email()
+
+Returns the e-mail address of the FCM administrator.
+
+=item $config->get_fcm_home()
+
+Returns the HOME directory of the FCM administrator.
+
+=item $config->get_fcm_dist_on_desktops()
+
+Returns the path for distributing FCM on the desktops.
+
+=item $config->get_fcm_dist_on_HPCs()
+
+Returns a list of locations for distributing FCM on the HPCs.
+
+=item $config->get_fcm_wc()
+
+Returns the (working copy) source path of the default FCM distribution.
+
+=item $config->get_fcm_admin_wc()
+
+Returns the (working copy) source path of the default FCM admin distribution.
+
+=item $config->get_svn_backup_dir()
+
+Returns the path to a directory containing the backups of SVN repositories.
+
+=item $config->get_svn_dump_dir()
+
+Returns the path to a directory containing the revision dumps of SVN
+repositories.
+
+=item $config->get_svn_hook_dir()
+
+Returns the path to a directory containing source files of SVN hook scripts.
+
+=item $config->get_svn_live_dir()
+
+Returns the path to a directory containing the live SVN repositories.
+
+=item $config->get_svn_passwd_file()
+
+Returns the base name of the SVN password file.
+
+=item $config->get_svn_project_suffix()
+
+Returns the suffix added to the name of each SVN repository.
+
+=item $config->get_trac_backup_dir()
+
+Returns the path to a directory containing the backups of Trac environments.
+
+=item $config->get_trac_gid()
+
+Returns the group ID of the Trac server.
+
+=item $config->get_trac_live_dir()
+
+Returns the path to a directory containing the live Trac environments.
+
+=item $config->get_trac_ini_file()
+
+Returns the base name of the Trac INI file.
+
+=item $config->get_trac_passwd_file()
+
+Returns the base name of the Trac password file.
+
+=item $config->get_user_number_min()
+
+Returns the expected minimum number of users.
+
+=item $config->set_svn_backup_dir($value)
+
+Sets the path to a directory containing the backups of SVN repositories.
+
+=item $config->set_svn_dump_dir($value)
+
+Sets the path to a directory containing the revision dumps of SVN
+repositories.
+
+=item $config->set_svn_hook_dir($value)
+
+Sets the path to a directory containing source files of SVN hook scripts.
+
+=item $config->set_svn_live_dir($value)
+
+Sets the path to a directory containing the live SVN repositories.
+
+=item $config->set_svn_passwd_file($value)
+
+Sets the base name of the SVN password file.
+
+=item $config->set_svn_project_suffix($value)
+
+Sets the suffix added to the name of each SVN repository.
+
+=item $config->set_trac_backup_dir($value)
+
+Sets the path to a directory containing the backups of Trac environments.
+
+=item $config->set_trac_live_dir($value)
+
+Sets the path to a directory containing the live Trac environments.
+
+=item $config->set_trac_ini_file($value)
+
+Sets the base name of the Trac INI file.
+
+=item $config->set_trac_passwd_file($value)
+
+Sets the base name of the Trac password file.
+
+=back
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/lib/FCM/Admin/Project.pm
===================================================================
--- vendors/fcm/1-5/examples/lib/FCM/Admin/Project.pm	(revision 1981)
+++ vendors/fcm/1-5/examples/lib/FCM/Admin/Project.pm	(revision 1981)
@@ -0,0 +1,225 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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(),
+    );
+}
+
+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.
+
+=back
+
+=head1 SEE ALSO
+
+L<FCM::Admin::Config|FCM::Admin::Config>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/lib/FCM/Admin/Runner.pm
===================================================================
--- vendors/fcm/1-5/examples/lib/FCM/Admin/Runner.pm	(revision 1981)
+++ vendors/fcm/1-5/examples/lib/FCM/Admin/Runner.pm	(revision 1981)
@@ -0,0 +1,286 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::Runner;
+
+use IO::Handle;
+
+# 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},
+        scalar(localtime()), $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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/lib/FCM/Admin/System.pm
===================================================================
--- vendors/fcm/1-5/examples/lib/FCM/Admin/System.pm	(revision 1981)
+++ vendors/fcm/1-5/examples/lib/FCM/Admin/System.pm	(revision 1981)
@@ -0,0 +1,1227 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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::Find qw{find};
+use File::Spec;
+use File::Temp qw{tempdir tempfile};
+use IO::Dir;
+use IO::Pipe;
+use List::Util qw{first};
+
+our @EXPORT_OK = qw{
+    add_trac_environment
+    backup_svn_repository
+    backup_trac_environment
+    backup_trac_ini_file
+    backup_trac_passwd_file
+    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
+    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_ini_file
+    recover_trac_passwd_file
+    vacuum_trac_env_db
+};
+
+# ------------------------------------------------------------------------------
+# Adds a new Trac environment.
+sub add_trac_environment {
+    my ($project_name, $admin_user_list_ref, $authorised_option) = @_;
+    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 @repository_arguments = (q{}, q{});
+    if (-d $project->get_svn_live_path()) {
+        @repository_arguments = (q{svn}, $project->get_svn_live_path());
+    }
+    my @TRAC_ADMIN = (q{trac-admin}, $project->get_trac_live_path());
+    FCM::Admin::Runner->instance()->run(
+        "initialising Trac environment",
+        sub {return !system(
+            @TRAC_ADMIN,
+            q{initenv},
+            $project_name,
+            q{sqlite:db/trac.db},
+            @repository_arguments,
+            q{--inherit=../../trac.ini},
+        )},
+    );
+    _chgrp_and_chmod_trac_environment_for($project);
+    for my $item (qw{component1 component2}) {
+        FCM::Admin::Runner->instance()->run(
+            "removing example component $item",
+            sub {return !system(@TRAC_ADMIN, q{component remove}, $item)},
+        );
+    }
+    for my $item (qw{1.0 2.0}) {
+        FCM::Admin::Runner->instance()->run(
+            "removing example version $item",
+            sub {return !system(@TRAC_ADMIN, q{version remove}, $item)},
+        );
+    }
+    for my $item (qw{milestone1 milestone2 milestone3 milestone4}) {
+        FCM::Admin::Runner->instance()->run(
+            "removing example milestone $item",
+            sub {return !system(@TRAC_ADMIN, q{milestone remove}, $item)},
+        );
+    }
+    for my $item (
+        ['major'    => 'normal'  ],
+        ['critical' => 'major'   ],
+        ['blocker'  => 'critical'],
+    ) {
+        my ($old, $new) = @{$item};
+        FCM::Admin::Runner->instance()->run(
+            "changing priority $old to $new",
+            sub {return !system(@TRAC_ADMIN, qw{priority change}, $old, $new)},
+        );
+    }
+    FCM::Admin::Runner->instance()->run(
+        "adding admin permission",
+        sub {return !system(@TRAC_ADMIN, qw{permission add admin TRAC_ADMIN})},
+    );
+    if (ref($admin_user_list_ref) eq 'ARRAY') {
+        for my $item (@{$admin_user_list_ref}) {
+            FCM::Admin::Runner->instance()->run(
+                "adding admin user $item",
+                sub {return !system(
+                    @TRAC_ADMIN, qw{permission add}, $item, q{admin},
+                )},
+            );
+        }
+    }
+    if ($authorised_option) {
+        for my $item (qw{TICKET_CREATE TICKET_MODIFY WIKI_CREATE WIKI_MODIFY}) {
+            FCM::Admin::Runner->instance()->run(
+                "removing authenticated write permission",
+                sub {return !system(
+                    @TRAC_ADMIN, qw{permission remove authenticated}, $item,
+                )},
+            );
+            FCM::Admin::Runner->instance()->run(
+                "adding authorised write permission",
+                sub {return !system(@TRAC_ADMIN, qw{permission add authorised}, $item)},
+            );
+	}
+    }
+    my $auth = $authorised_option ? q{authorised} : q{authenticated};
+    FCM::Admin::Runner->instance()->run(
+        "adding TICKET_EDIT_CC permission to $auth",
+        sub {return !system(@TRAC_ADMIN, qw{permission add}, $auth, qw{TICKET_EDIT_CC})},
+    );
+    FCM::Admin::Runner->instance()->run(
+        "updating configuration file",
+        sub {
+            my $project_trac_ini_file = $project->get_trac_live_ini_path();
+            my $project_trac_ini = Config::IniFiles->new(
+                q{-file} => $project_trac_ini_file,
+            );
+            if (!$project_trac_ini) {
+                die("$project_trac_ini_file: cannot open.\n");
+            }
+            my $SECTION = q{project};
+            if (!$project_trac_ini->SectionExists($SECTION)) {
+                $project_trac_ini->AddSection($SECTION);
+            }
+            if (!$project_trac_ini->newval($SECTION, q{descr}, $project->get_name())) {
+                die("$project_trac_ini_file: cannot set value.\n");
+            }
+            return $project_trac_ini->RewriteConfig();
+        },
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Backup the SVN repository of a project.
+sub backup_svn_repository {
+    my ($project, $housekeep_dumps_option, $verify_integrity_option) = @_;
+    if ($verify_integrity_option) {
+        _verify_svn_repository($project);
+    }
+    my $work_dir = tempdir(CLEANUP => 1);
+    my $work_path
+        = File::Spec->catfile($work_dir, $project->get_svn_base_name());
+    FCM::Admin::Runner->instance()->run(
+        sprintf(
+            "hotcopying %s to %s", $project->get_svn_live_path(), $work_path,
+        ),
+        sub {return !system(
+            qw{svnadmin hotcopy}, $project->get_svn_live_path(), $work_path,
+        )},
+        # Note: "hotcopy" is not yet possible via SVN::Repos
+    );
+    _create_backup_archive(
+        $work_path,
+        FCM::Admin::Config->instance()->get_svn_backup_dir(),
+        $project->get_svn_archive_base_name(),
+    );
+    if ($housekeep_dumps_option) {
+        my $dump_path = $project->get_svn_dump_path();
+        my $youngest = _svnlook_youngest($work_path);
+        # Note: could use SVN::Repos for "youngest"
+        FCM::Admin::Runner->instance()->run(
+            "housekeeping dumps in $dump_path",
+            sub {
+                my @rev_dump_paths;
+                _get_files_from(
+                    $dump_path,
+                    sub {
+                        my ($base_name, $path) = @_;
+                        if ($base_name !~ qr{\A\d+\z}xms) { # is numeric
+                            return;
+                        }
+                        if ($base_name > $youngest) {
+                            return;
+                        }
+                        push(@rev_dump_paths, $path);
+                    },
+                );
+                for my $rev_dump_path (@rev_dump_paths) {
+                    run_rmtree($rev_dump_path);
+                }
+                return 1;
+            }
+        );
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Backup the Trac environment of a project.
+sub backup_trac_environment {
+    my ($project, $verify_integrity_option) = @_;
+    my $trac_live_path = $project->get_trac_live_path();
+    if ($verify_integrity_option) {
+        my $db_path = $project->get_trac_live_db_path();
+        FCM::Admin::Runner->instance()->run(
+            "checking $db_path 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;
+            },
+        );
+    }
+    # Make sure the project INI file is owned by the correct group
+    my $project_trac_ini_file = $project->get_trac_live_ini_path();
+    my $gid = FCM::Admin::Config->instance()->get_trac_gid();
+    FCM::Admin::Runner->instance()->run(
+        "changing group ownership for $project_trac_ini_file",
+        sub {return chown(-1, $gid, $project_trac_ini_file)},
+    );
+    my $work_dir = tempdir(CLEANUP => 1);
+    my $work_path = File::Spec->catfile($work_dir, $project->get_name());
+    FCM::Admin::Runner->instance()->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,
+            );
+        },
+    );
+    _create_backup_archive(
+        $work_path,
+        FCM::Admin::Config->instance()->get_trac_backup_dir(),
+        $project->get_trac_archive_base_name(),
+    );
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Backup the Trac (central) INI file.
+sub backup_trac_ini_file {
+    # (no argument)
+    return _backup_trac_file(
+        FCM::Admin::Config->instance()->get_trac_ini_file()
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Backup the Trac password file.
+sub backup_trac_passwd_file {
+    # (no argument)
+    return _backup_trac_file(
+        FCM::Admin::Config->instance()->get_trac_passwd_file()
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Distributes the central FCM working copy to standard locations.
+sub distribute_wc {
+    my $rc = 1;
+    my $CONFIG = FCM::Admin::Config->instance();
+    my $RUNNER = FCM::Admin::Runner->instance();
+    my @RSYNC_OPTS = qw{-v --timeout=1800 --exclude=.*};
+    my $FCM_WC = $CONFIG->get_fcm_wc();
+    my @SOURCES = map {File::Spec->catfile($FCM_WC, $_)} qw{bin etc lib man};
+    for my $location (@{$CONFIG->get_fcm_dist_on_HPCs()}) {
+        $rc = $RUNNER->run_continue(
+            "distributing FCM to the HPCs",
+            sub {
+                run_rsync(
+                    \@SOURCES, $location,
+                    [@RSYNC_OPTS, qw{-a --delete-excluded}],
+                );
+            },
+        ) && $rc;
+    }
+    $rc = $RUNNER->run_continue(
+        "distributing FCM to the desktop sync location",
+        sub {
+            run_rsync(
+                \@SOURCES, $CONFIG->get_fcm_dist_on_desktops(),
+                [@RSYNC_OPTS, q{-rltoD}],
+            );
+        },
+    ) && $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
+        = FCM::Admin::Config->instance()->get_svn_project_suffix();
+    my @projects;
+    _get_files_from(
+        FCM::Admin::Config->instance()->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
+        = FCM::Admin::Config->instance()->get_svn_project_suffix();
+    my @projects;
+    _get_files_from(
+        FCM::Admin::Config->instance()->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(
+        FCM::Admin::Config->instance()->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(
+        FCM::Admin::Config->instance()->get_trac_live_dir(),
+        sub {
+            my ($name, $path) = @_;
+            if (!-d $path) {
+                return;
+            }
+            push(@projects, FCM::Admin::Project->new({name => $name}));
+        },
+    );
+    return @projects;
+}
+
+# ------------------------------------------------------------------------------
+# Gets a list of users using the mail aliases and the POSIX password DB.
+sub get_users {
+    # (no dummy argument)
+    my %email_of;
+    FCM::Admin::Runner->instance()->run(
+        "retrieving entries from the mail aliases",
+        sub {
+            my $pipe = IO::Pipe->new();
+            $pipe->reader(qw{getent aliases});
+            ALIASES_LINE:
+            while (my $line = $pipe->getline()) {
+                chomp($line);
+                my ($name, @emails) = split(qr{\s*[:,]\s*}xms, $line);
+                if (scalar(@emails) != 1) {
+                    next ALIASES_LINE;
+                }
+                $emails[0] =~ s{\s}{}gxms;
+                $emails[0] =~ s{\@metoffice\.com\z}{\@metoffice.gov.uk}xms;
+                if ($emails[0] !~ qr{\.uk\z}xms) { # is a .uk e-mail address
+                    next ALIASES_LINE;
+                }
+                $email_of{$name} = $emails[0];
+            }
+            return $pipe->close();
+        },
+    );
+    my %user_of;
+    USER:
+    while (my ($name, $gecos, $dir, $shell) = (getpwent())[0, 6, 7, 8]) {
+        if (exists($user_of{$name})) {
+            next USER;
+        }
+        if (!$dir || index($dir, '/home') != 0) {
+            next USER;
+        }
+        if (!$shell || $shell =~ qr{false\z}xms) { # ends with "false"
+            next USER;
+        }
+        my $email = $email_of{$name};
+        if (!$email && $name =~ qr{\A([a-z]+(?:\.[a-z]+)+)\z}xms) {
+            # Handles user IDs such as john.smith
+            $email = $name . q{@metoffice.gov.uk};
+        }
+        if (!$email) {
+            next USER;
+        }
+        $user_of{$name} = FCM::Admin::User->new({
+            name         => $name,
+            display_name => (split(qr{\s*,\s*}xms, $gecos))[0],
+                            # $gecos contains "display name, location, phone"
+            email        => $email,
+        });
+    }
+    endpwent();
+    if (keys(%user_of) < FCM::Admin::Config->instance()->get_user_number_min()) {
+        die("Number of users below minimum threshold.\n");
+    }
+    return (wantarray() ? %user_of : \%user_of);
+}
+
+# ------------------------------------------------------------------------------
+# Installs hook scripts to a SVN project.
+sub install_svn_hook {
+    my ($project) = @_;
+    my %path_of;
+    my $svn_hook_dir = FCM::Admin::Config->instance()->get_svn_hook_dir();
+    my $project_svn_hook_dir
+        = File::Spec->catfile($svn_hook_dir, $project->get_name());
+    for my $dir ($svn_hook_dir, $project_svn_hook_dir) {
+        _get_files_from(
+            $dir,
+            sub {
+                my ($base_name, $path) = @_;
+                if (index($base_name, q{.}) == 0 || !-f $path) {
+                    return;
+                }
+                $path_of{$base_name} = $path;
+            },
+        );
+    }
+    for my $key (keys(%path_of)) {
+        my $hook_source = $path_of{$key};
+        my $hook_dest
+            = File::Spec->catfile($project->get_svn_live_hook_path(), $key);
+        if (-l $hook_dest) {
+            my $symlink = readlink($hook_dest);
+            if ($symlink ne $hook_source) {
+                run_rmtree($hook_dest);
+                run_symlink($hook_source, $hook_dest);
+            }
+        }
+        else {
+            if (-e $hook_dest) {
+                run_rename($hook_dest, "$hook_dest.old");
+            }
+            run_symlink($hook_source, $hook_dest);
+        }
+    }
+    return 1;
+}
+
+# ------------------------------------------------------------------------------
+# Updates the SVN password file.
+sub manage_users_in_svn_passwd {
+    my ($user_ref) = @_;
+    my $svn_passwd_file = File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_svn_live_dir(),
+        FCM::Admin::Config->instance()->get_svn_passwd_file(),
+    );
+    FCM::Admin::Runner->instance()->run(
+        "updating $svn_passwd_file",
+        sub {
+            my $USERS_SECTION = q{users};
+            my $svn_passwd_ini;
+            my $is_changed;
+            if (-f $svn_passwd_file && -r $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})) {
+                    FCM::Admin::Runner->instance()->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"))) {
+                    FCM::Admin::Runner->instance()->run(
+                        "adding $user to $svn_passwd_file",
+                        sub {return $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) = @_;
+    my $trac_passwd_file = File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_trac_live_dir(),
+        FCM::Admin::Config->instance()->get_trac_passwd_file(),
+    );
+    FCM::Admin::Runner->instance()->run(
+        "updating $trac_passwd_file",
+        sub {
+            my %old_names;
+            my %new_names = %{$user_ref};
+            if (-f $trac_passwd_file && -r $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)) {
+                    FCM::Admin::Runner->instance()->run(
+                        "removing $name from $trac_passwd_file",
+                        sub {
+                            return !system(
+                                qw{htpasswd -D}, $trac_passwd_file, $name,
+                            );
+                        },
+                    );
+                }
+                for my $name (keys(%new_names)) {
+                    FCM::Admin::Runner->instance()->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 FCM::Admin::Runner->instance()->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) = @_;
+    my $config = FCM::Admin::Config->instance();
+    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 = File::Spec->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 @rev_dump_paths;
+        _get_files_from(
+            $project->get_svn_dump_path(),
+            sub {
+                my ($base_name, $path) = @_;
+                if ($base_name !~ qr{\A\d+\z}xms) { # is numeric
+                    return;
+                }
+                if ($base_name <= $youngest) {
+                    return;
+                }
+                push(@rev_dump_paths, $path);
+            },
+        );
+        # Note: sorts basenames of @rev_dump_paths into numeric ascending order
+        # using a Schwartzian Transform
+        @rev_dump_paths
+            = map {$_->[0]}
+              sort {$a->[1] <=> $b->[1]}
+              map {[$_, basename($_)]}
+              @rev_dump_paths;
+        for my $rev_dump_path (@rev_dump_paths) {
+            FCM::Admin::Runner->instance()->run(
+                "loading $rev_dump_path into $work_path",
+                sub {
+                    my $pipe = IO::Pipe->new();
+                    $pipe->writer(qw{svnadmin load}, $work_path);
+                    read_file($rev_dump_path, sub {$pipe->print($_[0])});
+                    return ($pipe->close());
+                },
+            );
+        }
+    }
+    run_rename($work_path, $project->get_svn_live_path());
+    if ($recover_hooks_option) {
+        install_svn_hook($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(),
+        ));
+    }
+    my $config = FCM::Admin::Config->instance();
+    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 = File::Spec->catfile($work_dir, $base_name);
+    _extract_backup_archive($project->get_trac_backup_path(), $work_path);
+    run_rename($work_path, $project->get_trac_live_path());
+    _chgrp_and_chmod_trac_environment_for($project);
+}
+
+# ------------------------------------------------------------------------------
+# Recover the Trac (central) INI file.
+sub recover_trac_ini_file {
+    # (no argument)
+    return _recover_trac_file(
+        FCM::Admin::Config->instance()->get_trac_ini_file()
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Recover the Trac password file.
+sub recover_trac_passwd_file {
+    # (no argument)
+    return _recover_trac_file(
+        FCM::Admin::Config->instance()->get_trac_passwd_file()
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Vacuum the database of a Trac environment.
+sub vacuum_trac_env_db {
+    my ($project) = @_;
+    FCM::Admin::Runner->instance()->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();
+        },
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Backup a file in the Trac live directory to the Trac backup directory.
+sub _backup_trac_file {
+    my ($base_name) = @_;
+    my $live_path = File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_trac_live_dir(), $base_name);
+    my $backup_path = File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_trac_backup_dir(), $base_name);
+    return
+        run_mkpath(FCM::Admin::Config->instance()->get_trac_backup_dir())
+        && run_copy($live_path, $backup_path);
+}
+
+# ------------------------------------------------------------------------------
+# Changes/restores ownership and permission of a project's Trac environment.
+sub _chgrp_and_chmod_trac_environment_for {
+    my ($project) = @_;
+    my $gid  = FCM::Admin::Config->instance()->get_trac_gid();
+    find(
+        sub {
+            my $file = $File::Find::name;
+            FCM::Admin::Runner->instance()->run(
+                "changing group ownership for $file",
+                sub {return chown(-1, $gid, $file)},
+            );
+            my $mode = (stat($file))[2] | S_IWGRP;
+            FCM::Admin::Runner->instance()->run(
+                "adding group write permission for $file",
+                sub {return chmod($mode, $file)},
+            );
+        },
+        $project->get_trac_live_path(),
+    );
+    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 = File::Spec->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 = File::Spec->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 {
+                FCM::Admin::Runner->instance()->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})) {
+                FCM::Admin::Runner->instance()->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 == ?",
+        );
+        $attribute_select_statement->execute();
+        my %attribute_old_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)) {
+                $attribute_old_users{$sid} = 1;
+                my $getter
+                    = $name eq 'name'  ? 'get_display_name'
+                    : $name eq 'email' ? 'get_email'
+                    :                    undef;
+                if (!defined($getter)) {
+                    next ROW;
+                }
+                if ($user->$getter() ne $value) {
+                    my $new_value = $user->$getter();
+                    FCM::Admin::Runner->instance()->run(
+                        "session_attribute: updating $name: $sid: $new_value",
+                        sub {return $attribute_update_statement->execute(
+                            $new_value, $sid, $name,
+                        )},
+                    );
+                }
+            }
+            else {
+                FCM::Admin::Runner->instance()->run(
+                    "session_attribute: removing $sid",
+                    sub {return $attribute_delete_statement->execute($sid)},
+                );
+            }
+        }
+        USER:
+        for my $sid (keys(%{$user_ref})) {
+            if (exists($attribute_old_users{$sid})) {
+                next USER;
+            }
+            my $user = $user_ref->{$sid};
+            my $display_name = $user->get_display_name();
+            my $email        = $user->get_email();
+            FCM::Admin::Runner->instance()->run(
+                "session_attribute: adding name: $sid: $display_name",
+                sub {return $attribute_insert_statement->execute(
+                    $sid, 'name', $display_name,
+                )},
+            );
+            FCM::Admin::Runner->instance()->run(
+                "session_attribute: adding email: $sid: $email",
+                sub {return $attribute_insert_statement->execute(
+                    $sid, 'email', $email,
+                )},
+            );
+        }
+        $attribute_select_statement->finish();
+        $attribute_insert_statement->finish();
+        $attribute_update_statement->finish();
+        $attribute_delete_statement->finish();
+    }
+    return $db_handle->disconnect();
+}
+
+# ------------------------------------------------------------------------------
+# Recover a file from the Trac backup directory to the Trac live directory.
+sub _recover_trac_file {
+    my ($base_name) = @_;
+    my $live_path = File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_trac_live_dir(), $base_name);
+    if (-e $live_path) {
+        die(sprintf("$live_path: file exists.\n"));
+    }
+    my $backup_path = File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_trac_backup_dir(), $base_name);
+    return
+        run_mkpath(FCM::Admin::Config->instance()->get_trac_live_dir())
+        && run_copy($backup_path, $live_path);
+}
+
+# ------------------------------------------------------------------------------
+# 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;
+}
+
+# ------------------------------------------------------------------------------
+# Verifies the integrity of a SVN repository.
+sub _verify_svn_repository {
+    my ($project) = @_;
+    my $VERIFIED_REVISION_REGEX = qr{\A\*\s+Verified\s+revision\s+\d+\.}xms;
+    FCM::Admin::Runner->instance()->run(
+        "verifying integrity of SVN repository of $project",
+        sub {
+            my $pipe = IO::Pipe->new();
+            $pipe->reader(sprintf(
+                qq{svnadmin verify %s 2>&1}, $project->get_svn_live_path(),
+            ));
+            while (my $line = $pipe->getline()) {
+                if ($line !~ $VERIFIED_REVISION_REGEX) { # don't print
+                    print($line);
+                }
+            }
+            return $pipe->close();
+            # Note: "verify" is not yet possible via SVN::Repos
+        },
+    );
+}
+
+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_trac_environment($project_name, $admin_user_list_ref, $authorised_option)
+
+Creates a new Trac environment.
+
+=item backup_svn_repository($project,$housekeep_dumps_option,$verify_integrity_option)
+
+Creates an archived hotcopy of $project's live SVN repository, and put it in the
+SVN backup directory. If $verify_integrity_option is set to true, it verifies
+the integrity of the live repository before creating the hotcopy. If
+$housekeep_dumps_option is set to true, 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($project,$verify_integrity_option)
+
+Creates an archived hotcopy of $project's live Trac environment, and put it in
+the Trac backup directory. If $verify_integrity_option is set to true, 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_ini_file()
+
+Copies the live Trac (central) INI file to the Trac backup directory.
+
+=item backup_trac_passwd_file()
+
+Copies the live Trac password file 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()
+
+Retrieves a list of users using the mail aliases and the POSIX password
+database. It also makes a naive attempt to filter out admin accounts. In LIST
+context, returns a hash with keys = user IDs and values = user details (as
+L<FCM::Admin::System::User|FCM::Admin::System::User> objects). In SCALAR
+context, returns a reference to the same hash.
+
+=item install_svn_hook($project)
+
+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.
+
+=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_ini_file()
+
+Copies the backup Trac (central) INI file to the Trac live directory (if it does
+not exist).
+
+=item recover_trac_passwd_file()
+
+Copies the backup Trac password file to the Trac live directory (if it does not
+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>,
+L<FCM::Admin::Util|FCM::Admin::Util>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/lib/FCM/Admin/User.pm
===================================================================
--- vendors/fcm/1-5/examples/lib/FCM/Admin/User.pm	(revision 1981)
+++ vendors/fcm/1-5/examples/lib/FCM/Admin/User.pm	(revision 1981)
@@ -0,0 +1,101 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package FCM::Admin::User;
+
+use overload q{""} => \&get_name;
+
+# The default values of the attributes
+my %DEFAULT = (
+    name         => undef,
+    display_name => undef,
+    email        => undef,
+);
+
+# ------------------------------------------------------------------------------
+# Returns a new instance of this class.
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%DEFAULT, %{$args_ref}}, $class);
+}
+
+# ------------------------------------------------------------------------------
+# Getters and setters.
+for my $key (keys(%DEFAULT)) {
+    no strict qw{refs};
+    my $getter = qq{get_$key};
+    my $setter = qq{set_$key};
+    *$getter = sub {
+        my ($self) = @_;
+        return $self->{$key};
+    };
+    *$setter = sub {
+        my ($self, $value) = @_;
+        $self->{$key} = $value;
+    };
+}
+
+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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/lib/FCM/Admin/Util.pm
===================================================================
--- vendors/fcm/1-5/examples/lib/FCM/Admin/Util.pm	(revision 1981)
+++ vendors/fcm/1-5/examples/lib/FCM/Admin/Util.pm	(revision 1981)
@@ -0,0 +1,421 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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 File::Temp qw{tempfile};
+use IO::File;
+use SVN::Client;
+
+our @EXPORT_OK = qw{
+    option2config
+    read_file
+    run_copy
+    run_create_archive
+    run_extract_archive
+    run_html2pdf
+    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 $rc = copy($source_path, $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 {
+            return !system(
+                qw{tar -c -z},
+                q{-C} => $work_dir,
+                q{-f} => $archive_path,
+                @base_names,
+            );
+            # 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 html2ps and ps2pdf.
+sub run_html2pdf {
+    my ($html_path, $pdf_path, $option_ref) = @_;
+    # Note: "html2ps" is currently installed at ~fcm/bin/
+    my $FCM_HOME_BIN = File::Spec->catfile(
+        FCM::Admin::Config->instance()->get_fcm_home(), q{bin},
+    );
+    local(%ENV) = %ENV;
+    $ENV{PATH} = join(q{:}, $FCM_HOME_BIN, $ENV{PATH});
+    my $html2ps_config;
+    if (ref($option_ref) && exists($option_ref->{config})) {
+        $html2ps_config = $option_ref->{config};
+    }
+    else {
+        my $file
+            = File::Spec->catfile(dirname($html_path), q{style.html2ps.css});
+        if (-f $file && -r $file) {
+            $html2ps_config = $file;
+        }
+    }
+    my ($fh, $ps_path) = tempfile(UNLINK => 1);
+    close($fh);
+    my @html2ps = (
+        @HTML2PS,
+        ($html2ps_config ? (q{-f} => $html2ps_config) : ()),
+        q{-o} => $ps_path,
+        $html_path,
+    );
+    FCM::Admin::Runner->instance()->run(
+        "converting $html_path to PS format",
+        sub {return !system(@html2ps)},
+    );
+    my @ps2pdf = (@PS2PDF, $ps_path, $pdf_path);
+    FCM::Admin::Runner->instance()->run(
+        "converting PS format to $pdf_path",
+        sub {return !system(@ps2pdf)},
+    );
+}
+
+# ------------------------------------------------------------------------------
+# 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) = @_;
+    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_html2pdf($html_path,$pdf_path,$option_ref)
+
+Converts a HTML document at $html_path to PDF format, and writes the results to
+$pdf_path. If $option_ref is set, it must be a reference to a HASH with the
+element "config". If the value of $option_ref->{config} is set to an existing
+path, it is used by "html2ps" as the configuration file. Depends on "html2ps"
+and "ps2pdf".
+
+=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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-add-trac-env
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-add-trac-env	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-add-trac-env	(revision 1981)
@@ -0,0 +1,119 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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{admin-user|u=s@},
+        q{authorised|a},
+        q{help|usage|h},
+        q{svn-live-dir=s},
+        q{svn-project-suffix=s},
+        q{trac-live-dir=s},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    if (exists($option{'admin-user'})) {
+        $option{'admin-user'} = [
+            split(q{,}, join(q{,}, @{$option{'admin-user'}})),
+        ];
+    }
+    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,
+        (exists($option{'admin-user'}) ? $option{'admin-user'} : []),
+        exists($option{'authorised'}),
+    );
+}
+
+__END__
+=head1 NAME
+
+fcm-add-trac-env
+
+=head1 SYNOPSIS
+
+    fcm-add-trac-env [OPTIONS] PROJECT
+
+=head1 OPTIONS
+
+=over 4
+
+=item --admin-user=LIST, -uLIST
+
+Specifies one or more admin users, in a comma separated list.
+
+=item --authorised, -a
+
+Adds an "authorised" permission. Only those users granted this permission
+will have "_CREATE" and "_MODIFY" access. The default is to grant all
+authenticated users "_CREATE" and "_MODIFY" access.
+
+=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-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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-backup-svn-repos
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-backup-svn-repos	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-backup-svn-repos	(revision 1981)
@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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-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(
+            $project,
+            !$option{q{no-housekeep-dumps}},
+            !$option{q{no-verify-integrity}},
+        );
+    }
+}
+
+__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-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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-backup-trac-env
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-backup-trac-env	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-backup-trac-env	(revision 1981)
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    backup_trac_environment
+    backup_trac_ini_file
+    backup_trac_passwd_file
+    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-ini-file},
+        q{no-passwd-file},
+        q{no-verify-integrity},
+        q{trac-backup-dir=s},
+        q{trac-ini-file=s},
+        q{trac-live-dir=s},
+        q{trac-passwd-file=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($project, !$option{q{no-verify-integrity}});
+    }
+    if (!@ARGV && !exists($option{q{no-ini-file}})) {
+        backup_trac_ini_file();
+    }
+    if (!@ARGV && !exists($option{q{no-passwd-file}})) {
+        backup_trac_passwd_file();
+    }
+}
+
+__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-ini-file
+
+Do not backup the (central) INI file. This option is automatic if one or more
+project name is specified in the argument list.
+
+=item --no-passwd-file
+
+Do not backup the password file. This option is automatic if one or more project
+name is specified in the argument list.
+
+=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-ini-file=FILE
+
+Specifies the base name of the Trac INI 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. 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 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.
+
+No attempt to backup the (central) INI file and the password file is made if one
+or more project name is specified.
+
+=back
+
+=head1 DESCRIPTION
+
+This program archives Trac environments, the (central) INI file and the password
+file in the live directory to the backup directory.
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-commit-update
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-commit-update	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-commit-update	(revision 1981)
@@ -0,0 +1,204 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use File::Basename qw{basename};
+use File::Spec;
+use Pod::Usage qw{pod2usage};
+
+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_html2pdf
+    run_mkpath
+    run_rmtree
+    run_svn_info
+    run_svn_update
+    write_file
+};
+
+# ------------------------------------------------------------------------------
+my $CONFIG = FCM::Admin::Config->instance();
+my %PATTERN_OF = (
+    q{}             => qr{.*}xms,
+    DOC_COLLAB      => qr{doc/collaboration/}xms,
+    DOC_STD_FORTRAN => qr{doc/standards/fortran_standard\.html}xms,
+    DOC_STD_PERL    => qr{doc/standards/perl_standard\.html}xms,
+    DOC_UG          => qr{doc/user_guide/}xms,
+    SRC             => qr{(?:bin|etc|lib|man)/}xms,
+    SRC_HOOK        => qr{svn-hooks/}xms,
+);
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+# ------------------------------------------------------------------------------
+# The main logic.
+sub main {
+    if (@_ != 2) {
+        my $message = sprintf(qq{Expect 2 arguments, %d given}, scalar(@_));
+        pod2usage({q{-exitval} => 1, q{-message} => $message});
+    }
+    my ($repos_name, $log_dir_path) = @_;
+    create_lock($repos_name, $log_dir_path) || return;
+    my $RUNNER = FCM::Admin::Runner->instance();
+    UPDATE:
+    while (1) {
+        my @updates_in_fcm   = run_svn_update($CONFIG->get_fcm_wc());
+        my @updates_in_admin = run_svn_update($CONFIG->get_fcm_admin_wc());
+        if (!@updates_in_fcm && !@updates_in_admin) {
+            last UPDATE;
+        }
+        for my $task ((
+            {
+                name => '(re-)installing hook scripts',
+                main => sub {
+                    for my $project (get_projects_from_svn_live()) {
+                        install_svn_hook($project);
+                    }
+                    return 1;
+                },
+                want => get_pattern_grepper('SRC_HOOK', 1), # in "Admin"
+            },
+            {
+                name => 'updating FCM release number file',
+                main => sub {
+                    my $root = $CONFIG->get_fcm_wc();
+                    write_file(
+                        File::Spec->catfile($root, qw{etc fcm_rev}),
+                        run_svn_info($root)->last_changed_rev(),
+                    );
+                },
+                want => get_pattern_grepper(q{}),
+            },
+            {
+                name => 'generating PDF for the user guide',
+                main => get_fcm_html2pdf('user_guide', 'user-guide'),
+                want => get_pattern_grepper('DOC_UG'),
+            },
+            {
+                name => 'generating PDF for the collaboration guide',
+                main => get_fcm_html2pdf('collaboration', 'collaboration'),
+                want => get_pattern_grepper('DOC_COLLAB'),
+            },
+            {
+                name => 'generating PDF for the FCM Fortran Standard document',
+                main => get_fcm_html2pdf(
+                    'standards', 'fortran-standard', 'fortran_standard',
+                ),
+                want => get_pattern_grepper('DOC_STD_FORTRAN'),
+            },
+            {
+                name => 'generating PDF for the FCM Perl Standard document',
+                main => get_fcm_html2pdf(
+                    'standards', 'perl-standard', 'perl_standard',
+                ),
+                want => get_pattern_grepper('DOC_STD_PERL'),
+            },
+            {
+                name => 'distributing FCM to standard locations',
+                main => \&distribute_wc,
+                want => get_pattern_grepper('SRC'),
+            },
+        )) {
+            if ($task->{want}->(\@updates_in_fcm, \@updates_in_admin)) {
+                $RUNNER->run($task->{name}, $task->{main});
+            }
+        }
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Returns a wrapper of run_html2pdf, base on the relative $path in the doc/
+# sub-directory of the central FCM working copy pointing to the container
+# directory of the input and output.
+sub get_fcm_html2pdf {
+    my ($path, $name_of_output, $name_of_input) = @_;
+    my $container = File::Spec->catfile($CONFIG->get_fcm_wc(), 'doc', $path);
+    my $input = File::Spec->catfile(
+        $container, ($name_of_input ? $name_of_input : 'index') . '.html',
+    );
+    my $output = File::Spec->catfile($container, "fcm-$name_of_output.pdf");
+    return sub {run_html2pdf($input, $output)};
+}
+
+# ------------------------------------------------------------------------------
+# Returns a function that "grep" for a known pattern ($pattern_key). The
+# returned function expects a list reference in the $index'th (default = 0)
+# element of its argument list.
+sub get_pattern_grepper {
+    my ($pattern_key, $index) = @_;
+    if (!defined($index)) {
+        $index = 0;
+    }
+    return sub {grep({$_ =~ $PATTERN_OF{$pattern_key}} @{$_[$index]})};
+}
+
+# ------------------------------------------------------------------------------
+# Creates a lock. Returns true on success. Removes lock when program finishes.
+sub create_lock {
+    my ($repos_name, $log_dir_path) = @_;
+    my $lock = File::Spec->catfile($log_dir_path, $repos_name . '.lock');
+    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 REPOS-NAME LOG-DIR-PATH
+
+=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 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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-create-release
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-create-release	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-create-release	(revision 1981)
@@ -0,0 +1,317 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Fcntl qw{:mode}; # for S_IXUSR, S_IXGRP, S_IXOTH, etc
+use File::Basename qw{dirname basename};
+use File::Spec;
+use File::Temp qw{tempdir};
+use FCM::Admin::Project;
+use FCM::Admin::Runner;
+use FCM::Admin::Util qw{
+    option2config
+    run_create_archive
+    run_html2pdf
+    run_mkpath
+    run_rename
+    run_rmtree
+    sed_file
+    write_file
+};
+use Getopt::Long qw{GetOptions};
+use IO::Dir;
+use Pod::Usage qw{pod2usage};
+use SVN::Client;
+
+main();
+
+sub main {
+    my %option = ();
+    my $result = GetOptions(
+        \%option,
+        q{help|usage|h},
+        q{revision|r=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 $user_revision = exists($option{revision}) ? $option{revision} : q{HEAD};
+    if (@ARGV != 1) {
+        my $message = sprintf(
+            qq{Expected exactly 1 argument, %d given.}, scalar(@ARGV),
+        );
+        pod2usage({q{-exitval} => 1, q{-message} => $message});
+    }
+    my ($release_name) = @ARGV;
+    my $out_file = "fcm-$release_name.tgz";
+    if (-e $out_file) {
+        die("$out_file: already exists.\n");
+    }
+
+    my $fcm_project = FCM::Admin::Project->new({name => q{FCM}});
+    my $fcm_repos_uri = $fcm_project->get_svn_file_uri();
+    my $revision = svn_info_revision($fcm_repos_uri, $user_revision);
+
+    # Exports the trees of FCM and Admin to a temporary working area
+    my $work_dir = tempdir(CLEANUP => 1);
+    my $fcm_work_path = File::Spec->catfile($work_dir, q{fcm-} . $release_name);
+    my $admin_work_path = File::Spec->catfile($work_dir, q{admin});
+    svn_export(
+        join(q{/}, $fcm_repos_uri , qw{FCM trunk}),
+        $fcm_work_path,
+        $revision,
+    );
+    svn_export(
+        join(q{/}, $fcm_repos_uri , qw{Admin trunk}),
+        $admin_work_path,
+        $revision,
+    );
+
+    manipulate_src_tree($fcm_work_path);
+    manipulate_doc_tree($fcm_work_path);
+    add_examples($fcm_work_path, $admin_work_path);
+    add_tutorial(($fcm_work_path, $admin_work_path));
+    add_readme($fcm_work_path, $release_name, $revision);
+
+    run_create_archive($out_file, $work_dir, basename($fcm_work_path));
+}
+
+# ------------------------------------------------------------------------------
+# Adds a README to the distribution.
+sub add_readme {
+    my ($fcm_work_path, $release_name, $revision) = @_;
+    write_file(
+        File::Spec->catfile($fcm_work_path, q{README}),
+        <<"README_FILE",
+FCM release $release_name created from revision $revision.
+
+For further details please refer to the release notes
+which can be found in the directory doc/release_notes.
+README_FILE
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Adds admin examples to the distribution.
+sub add_examples {
+    my ($fcm_work_path, $admin_work_path) = @_;
+    # Add utilities
+    my $fcm_examples_path = File::Spec->catfile($fcm_work_path, qw{examples});
+    run_mkpath($fcm_examples_path);
+    for my $path (
+        File::Spec->catfile($admin_work_path, qw{etc regular-update.eg}),
+        glob(File::Spec->catfile($admin_work_path, qw{lib FCM*})),
+        glob(File::Spec->catfile($admin_work_path, qw{sbin fcm-*})),
+    ) {
+        my $rel_path = File::Spec->abs2rel($path, $admin_work_path);
+        my $new_path = File::Spec->catfile($fcm_examples_path, $rel_path);
+        run_rename($path, $new_path);
+    }
+
+    # Add hooks
+    my $fcm_example_hook_path
+        = File::Spec->catfile($fcm_work_path, qw{examples svn-hooks});
+    my $admin_hook_path = File::Spec->catfile($admin_work_path, qw{svn-hooks});
+    run_mkpath($fcm_example_hook_path);
+    for my $path (glob(File::Spec->catfile($admin_hook_path, q{*}))) {
+        if (!-d $path) {
+            my $new_path
+                = File::Spec->catfile($fcm_example_hook_path, basename($path));
+            run_rename($path, $new_path);
+        }
+    }
+    run_rename(
+        File::Spec->catfile($admin_hook_path, qw{FCM background_updates.pl}),
+        File::Spec->catfile($fcm_example_hook_path, q{background_updates.pl}),
+    );
+
+    # Replaces FCM e-mail with an anonymous one
+    for my $path (
+        glob(File::Spec->catfile($fcm_examples_path, qw{FCM Admin *})),
+        glob(File::Spec->catfile($fcm_example_hook_path, q{*})),
+    ) {
+        sed_file(
+            $path,
+            sub {
+                my ($line) = @_;
+                $line =~ s{fcm\@metoffice\.gov\.uk}
+                          {my.name\@somewhere.org}gxms;
+                return $line;
+            },
+        );
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Adds the tutorial to the distribution.
+sub add_tutorial {
+    my ($fcm_work_path, $admin_work_path) = @_;
+    my $fcm_tutorial_path = File::Spec->catfile($fcm_work_path, q{tutorial});
+    my $admin_tutorial_path
+        = File::Spec->catfile($admin_work_path, q{tutorial});
+    run_rename($admin_tutorial_path, $fcm_tutorial_path);
+    my $admin_hook_tutorial_path
+        = File::Spec->catfile($admin_work_path, qw{svn-hooks tutorial});
+    my $fcm_tutorial_hook_path
+        = File::Spec->catfile($fcm_tutorial_path, q{hooks});
+    for my $path (glob(File::Spec->catfile($admin_hook_tutorial_path, q{*}))) {
+        my $new_path
+            = File::Spec->catfile($fcm_tutorial_hook_path, basename($path));
+        run_rename($path, $new_path);
+    }
+    my $fcm_example_svnperms_path = File::Spec->catfile(
+        $fcm_work_path, qw{examples svn-hooks svnperms.py},
+    );
+    my $fcm_tutorial_svnperms_path
+        = File::Spec->catfile($fcm_tutorial_hook_path, q{svnperms.py});
+    run_rename($fcm_example_svnperms_path, $fcm_tutorial_svnperms_path);
+}
+
+# ------------------------------------------------------------------------------
+# Modifies the doc/ tree for the distribution.
+sub manipulate_doc_tree {
+    my ($fcm_work_path) = @_;
+    # Renames items in the documentation tree
+    for my $path (glob(File::Spec->catfile($fcm_work_path, qw{doc * *.ppt}))) {
+        run_rmtree($path);
+    }
+    sed_file(
+        File::Spec->catfile($fcm_work_path, qw{doc etc fcm.js}),
+        sub {
+            my ($line) = @_;
+            $line =~ s{\A(\s*URL_OF_TEAM:)\s*'.*',}{$1 null,}gxms;
+            return $line;
+        },
+    );
+
+    # Creates PDF versions of the documentations
+    for (
+        [[qw{user_guide    index.html}           ], q{fcm-user-guide}      ],
+        [[qw{collaboration index.html}           ], q{fcm-collaboration}   ],
+        [[qw{standards     fortran_standard.html}], q{fcm-fortran-standard}],
+        [[qw{standards     perl_standard.html}   ], q{fcm-perl-standard}   ],
+    ) {
+        my ($html_path_ref, $pdf_name) = @{$_};
+        my $html_path
+            = File::Spec->catfile($fcm_work_path, q{doc}, @{$html_path_ref});
+        my $pdf_path
+            = File::Spec->catfile(dirname($html_path), "$pdf_name.pdf");
+        run_html2pdf($html_path, $pdf_path);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Modifies the src/ tree for the distribution.
+sub manipulate_src_tree {
+    my ($fcm_work_path) = @_;
+    run_rename(
+        File::Spec->catfile($fcm_work_path, qw{etc fcm.cfg}),
+        File::Spec->catfile($fcm_work_path, qw{etc fcm.cfg.eg}),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Exports the Subversion URI "$from" (at $revision) to a path at "$to".
+sub svn_export {
+    my ($from, $to, $revision) = @_;
+    my $svn_client = SVN::Client->new();
+    FCM::Admin::Runner->instance()->run(
+        "exporting $from to $to",
+        sub {return $svn_client->export($from, $to, $revision, 0)},
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns the actual revision of $svn_repos_uri (at a specified $user_revision).
+sub svn_info_revision {
+    my ($svn_repos_uri, $user_revision) = @_;
+    my $revision;
+    my $svn_client = SVN::Client->new();
+    $svn_client->info(
+        $svn_repos_uri,
+        $user_revision,
+        $user_revision,
+        sub {$revision = $_[1]->rev()},
+        1,
+    );
+    if (!$revision) {
+        die("$svn_repos_uri: cannot determine actual revision.\n");
+    }
+    return $revision;
+}
+
+__END__
+
+=head1 NAME
+
+fcm-create-release
+
+=head1 SYNOPSIS
+
+    fcm-create-release [--revision=REV] RELEASE-NAME
+
+=head1 DESCRIPTION
+
+Creates a release of FCM in a compressed-tar ball.
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item RELEASE-NAME
+
+The name of the release
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --revision=REV, -rREV
+
+Specifies a revision of the trunk for creating the release. If not specified,
+the program uses the last commit revision at the HEAD of the trunk.
+
+=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 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::Util|FCM::Admin::Util>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-daily-update
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-daily-update	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-daily-update	(revision 1981)
@@ -0,0 +1,175 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use File::Basename qw{basename};
+use File::Spec;
+use IO::File;
+use Mail::Mailer;
+
+use FCM::Admin::Config;
+use FCM::Admin::Runner;
+use FCM::Admin::System qw{
+    backup_svn_repository
+    backup_trac_environment
+    backup_trac_ini_file
+    backup_trac_passwd_file
+    distribute_wc
+    get_projects_from_svn_live
+    get_projects_from_trac_live
+    get_users
+    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 $OUT_FILE
+    = File::Spec->catfile($CONFIG->get_fcm_home(), q{FCM}, qq{$THIS.out});
+
+main();
+
+sub main {
+    # Redirects STDOUT and STDERR to the $OUT_FILE
+    open(my $old_out, q{>&}, \*STDOUT)
+        or die("$THIS: cannot duplicate STDOUT ($!)\n");
+    open(my $old_err, q{>&}, \*STDERR)
+        or die("$THIS: cannot duplicate STDERR ($!)\n");
+    open(STDOUT, q{>}, $OUT_FILE)
+        or die("$THIS: cannot redirect STDOUT ($!)\n");
+    open(STDERR, q{>&}, \*STDOUT)
+        or die("$THIS: cannot redirect STDERR ($!)\n");
+
+    do_tasks();
+
+    # Restores STDOUT and STDERR
+    open(STDERR, q{>&}, $old_err)
+        or die("$THIS: cannot reinstate STDERR ($!)\n");
+    open(STDOUT, q{>&}, $old_out)
+        or die("$THIS: cannot reinstate STDOUT ($!)\n");
+
+    notify();
+}
+
+# ------------------------------------------------------------------------------
+# 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(); return 1;},
+    );
+    if (defined($user_ref)) {
+        $RUNNER->run_continue(
+            "updating SVN user accounts",
+            sub {return manage_users_in_svn_passwd($user_ref)},
+        );
+        $RUNNER->run_continue(
+            "updating Trac user accounts",
+            sub {return manage_users_in_trac_passwd($user_ref)},
+        );
+        for my $project (@trac_projects) {
+            $RUNNER->run_continue(
+                "updating Trac accounts in $project",
+                sub {
+                    return manage_users_in_trac_db_of($project, $user_ref);
+                },
+            );
+        }
+    }
+    for my $project (@svn_projects) {
+        $RUNNER->run_continue(
+            "backing up SVN repository for $project",
+            sub {return backup_svn_repository($project, 1, 1)},
+        );
+    }
+    for my $project (@trac_projects) {
+        $RUNNER->run_continue(
+            "backing up Trac environment for $project",
+            sub {return backup_trac_environment($project, 1)},
+        );
+    }
+    $RUNNER->run_continue(
+        "backing up Trac (central) INI file",
+        sub {return backup_trac_ini_file()},
+    );
+    $RUNNER->run_continue(
+        "backing up Trac password file",
+        sub {return backup_trac_passwd_file()},
+    );
+    $RUNNER->run_continue(
+        "distributing FCM to standard locations",
+        sub {return distribute_wc()},
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Notifies on completion.
+sub notify {
+    # (no argument)
+    # 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_admin_email(),
+        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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-html2pdf
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-html2pdf	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-html2pdf	(revision 1981)
@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use File::Basename qw{dirname};
+use File::Spec;
+use FCM::Admin::Util qw{run_html2pdf};
+use Getopt::Long qw{GetOptions};
+use Pod::Usage qw{pod2usage};
+
+main();
+
+sub main {
+    my %option = ();
+    my $result = GetOptions(
+        \%option,
+        q{config|c=s},
+        q{help|usage|h},
+    );
+    if (!$result) {
+        pod2usage(1);
+    }
+    if (exists($option{help})) {
+        pod2usage(q{-verbose} => 1);
+    }
+    if (@ARGV != 2) {
+        my $message = sprintf(qq{Expect 2 arguments, %d given}, scalar(@ARGV));
+        pod2usage({q{-exitval} => 1, q{-message} => $message});
+    }
+    my ($html_path, $out_name) = @ARGV;
+    my $pdf_path = File::Spec->catfile(dirname($html_path), "$out_name.pdf");
+    run_html2pdf($html_path, $pdf_path, \%option);
+}
+
+__END__
+
+=head1 NAME
+
+fcm-html2pdf
+
+=head1 SYNOPSIS
+
+    fcm-html2pdf [--config=HTML2PS-CSS] HTML-PATH OUT-NAME
+
+=head1 DESCRIPTION
+
+Converts a self contained set of HTML documents into a PDF.
+
+=head1 ARGUMENTS
+
+=over 4
+
+=item HTML-PATH
+
+The path to the input HTML file.
+
+=item OUT-NAME
+
+The root name (basename without the extension) of the output file. The output is
+normally written to a file called I<OUT-NAME>.pdf in the directory containing
+L<HTML-PATH|/"HTML-PATH">
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --config=HTML2PS -c HTML2PS
+
+Specifies a configuration file for the I<html2ps> command. If not specified, the
+configuration file is assumed to be a file called I<style.html2ps.css> in the
+directory containing L<HTML-PATH|/"HTML-PATH">.
+
+=back
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-install-svn-hook
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-install-svn-hook	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-install-svn-hook	(revision 1981)
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use FCM::Admin::System qw{
+    filter_projects
+    get_projects_from_svn_live
+    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{help|usage|h},
+        q{svn-hook-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) {
+        install_svn_hook($project);
+    }
+}
+
+__END__
+
+=head1 NAME
+
+fcm-install-svn-hook
+
+=head1 SYNOPSIS
+
+    fcm-install-svn-hook [OPTIONS] [PROJECT ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item --help, -h, --usage
+
+Prints help and exits.
+
+=item --svn-hook-dir=DIR
+
+Specifies the root location of the directory containing the source files of the
+hook scripts. 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 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.
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-manage-users
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-manage-users	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-manage-users	(revision 1981)
@@ -0,0 +1,106 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-recover-svn-repos
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-recover-svn-repos	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-recover-svn-repos	(revision 1981)
@@ -0,0 +1,129 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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-hook-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 (@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-hook-dir=DIR
+
+Specifies the root location of the directory containing the source files of the
+hook scripts. 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 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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-recover-trac-env
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-recover-trac-env	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-recover-trac-env	(revision 1981)
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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_ini_file
+    recover_trac_passwd_file
+};
+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-ini-file},
+        q{no-passwd-file},
+        q{trac-backup-dir=s},
+        q{trac-live-dir=s},
+        q{trac-ini-file=s},
+        q{trac-passwd-file=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 && !exists($option{q{no-ini-file}})) {
+        recover_trac_ini_file();
+    }
+    if (!@ARGV && !exists($option{q{no-passwd-file}})) {
+        recover_trac_passwd_file();
+    }
+}
+
+__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 --no-ini-file
+
+Do not recover the (central) INI file. This option is automatic if one or more
+project name is specified in the argument list.
+
+=item --no-passwd-file
+
+Do not recover the password file. This option is automatic if one or more
+project name is specified in the argument list.
+
+=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-ini-file=FILE
+
+Specifies the base name of the Trac INI 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. 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 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.
+
+No attempt to recover the (central) INI file and the password file is made if
+one or more project name is specified.
+
+=back
+
+=head1 DESCRIPTION
+
+This program recovers Trac environments, the (central) INI file and the
+password file from the backup directory to the live directory.
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/sbin/fcm-vacuum-trac-env-db
===================================================================
--- vendors/fcm/1-5/examples/sbin/fcm-vacuum-trac-env-db	(revision 1981)
+++ vendors/fcm/1-5/examples/sbin/fcm-vacuum-trac-env-db	(revision 1981)
@@ -0,0 +1,88 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/examples/svn-hooks/background_updates.pl
===================================================================
--- vendors/fcm/1-5/examples/svn-hooks/background_updates.pl	(revision 1981)
+++ vendors/fcm/1-5/examples/svn-hooks/background_updates.pl	(revision 1981)
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use File::Spec;
+
+my $home    = (getpwuid($<))[7];
+my $command = File::Spec->catfile($home, qw{FCM sbin fcm-commit-update});
+exec($command, @ARGV) || die("$command: cannot execute.\n");
+__END__
Index: vendors/fcm/1-5/examples/svn-hooks/post-commit
===================================================================
--- vendors/fcm/1-5/examples/svn-hooks/post-commit	(revision 1981)
+++ vendors/fcm/1-5/examples/svn-hooks/post-commit	(revision 1981)
@@ -0,0 +1,68 @@
+#!/bin/ksh
+
+# POST-COMMIT HOOK
+#
+# The post-commit hook is invoked after a commit.  Subversion runs
+# this hook by invoking a program (script, executable, binary, etc.)
+# named 'post-commit' (for which this file is a template) with the 
+# following ordered arguments:
+#
+#   [1] REPOS-PATH   (the path to this repository)
+#   [2] REV          (the number of the revision just committed)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# Because the commit has already completed and cannot be undone,
+# the exit code of the hook program is ignored.  The hook program
+# can use the 'svnlook' utility to help it examine the
+# newly-committed tree.
+#
+# On a Unix system, the normal procedure is to have 'post-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-commit.bat' or 'post-commit.exe',
+# but the basic idea is the same.
+# 
+# The hook program typically does not inherit the environment of
+# its parent process.  For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+# 
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+REPOS=$1
+REV=$2
+
+CMD=${REPOS}/hooks/post-commit-background
+ERR=''
+
+if [[ -x $CMD ]]; then
+  $CMD $REPOS $REV >/dev/null 2>&1 &
+  RC=$?
+
+  if ((RC != 0)); then
+    ERR="$CMD failed ($RC)"
+  fi
+
+else
+  ERR="$CMD does not exist"
+fi
+
+if [[ -n $ERR ]]; then
+  /bin/mail -s "$ERR" my.name@somewhere.org <<EOF
+$(date): $ERR
+EOF
+fi
Index: vendors/fcm/1-5/examples/svn-hooks/post-commit-background
===================================================================
--- vendors/fcm/1-5/examples/svn-hooks/post-commit-background	(revision 1981)
+++ vendors/fcm/1-5/examples/svn-hooks/post-commit-background	(revision 1981)
@@ -0,0 +1,92 @@
+#!/bin/ksh
+# ------------------------------------------------------------------------------
+# NAME
+#   post-commit-background
+#
+# SYNOPSIS
+#   post-commit-background REPOS REV
+#
+# DESCRIPTION
+#   This script performs the Subversion post-commit tasks in the background. The
+#   first argument must be the path to the current Subversion repository, and
+#   the second argument must be the revision of the current commit. Errors in
+#   the script are automatically e-mailed to the FCM user account.
+#
+#   The script does the following:
+#    1. updates the "latest" file in the log directory with the latest revision
+#       number of the repository.
+#    2. creates an incremental revision dump of the current revision.
+#    3. runs "background_updates.pl" if it exists in the hook script directory.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+REPOS=$1
+REV=$2
+
+REPOS_NAME=${REPOS##*/}
+LOG_DIR=~fcm/svn/post-commit
+LOG_FILE=$LOG_DIR/${REPOS_NAME}.log
+DUMP_DIR=~fcm/svn/dumps/$REPOS_NAME
+DUMP_FILE=$DUMP_DIR/$REV
+ERR=''
+
+{
+  # Update the "latest" file with the latest revision number
+  # ----------------------------------------------------------------------------
+  echo "$(date): Updating ${REPOS_NAME}.latest ..."
+  echo $REV > $LOG_DIR/${REPOS_NAME}.latest
+
+  if (($? != 0)); then
+    ERR="${ERR}Update of ${REPOS_NAME}.latest: failed. "
+  fi
+
+  # Dump the current revision
+  # ----------------------------------------------------------------------------
+  if [[ ! -d $DUMP_DIR ]]; then
+    mkdir -p $DUMP_DIR
+  fi
+
+  svnadmin dump -r $REV --incremental --deltas $REPOS 1>$DUMP_FILE
+
+  if (($? != 0)); then
+    ERR="${ERR}$REPOS_NAME@$REV: svnadmin dump: failed. "
+  fi
+
+  # Warn us of large changesets (>1Mb)
+  dump_size=$(du -b -s $DUMP_FILE | cut -f 1)
+
+  if (($dump_size > 1048576)); then
+    dump_size=$(du -h -s $DUMP_FILE | cut -f 1)
+    ERR="${ERR}Dump size exceeds 1Mb. "
+    echo "WARNING: large changeset dump, size = ${dump_size}">&2 
+  fi
+
+  # Perform background update, if necessary
+  # ----------------------------------------------------------------------------
+  if [[ -e ${REPOS}/hooks/background_updates.pl ]]; then
+    echo "$(date): Running background tasks and exiting ..."
+    export TMPDIR=/var/tmp
+    ${REPOS}/hooks/background_updates.pl $REPOS_NAME $LOG_DIR
+
+    if (($? != 0)); then
+      ERR="${ERR}background_updates.pl: failed. "
+    fi
+
+  else
+    echo "$(date): No background tasks required - exiting"
+  fi
+
+} 1>$LOG_FILE 2>&1
+
+# If there are errors, e-mail the log message to FCM
+# ------------------------------------------------------------------------------
+if [[ -n $ERR ]]; then
+  ERR=${ERR% *}
+  /bin/mail -s "$REPOS@$REV: $ERR" my.name@somewhere.org <<EOF
+$(<$LOG_FILE)
+EOF
+fi
Index: vendors/fcm/1-5/examples/svn-hooks/post-revprop-change
===================================================================
--- vendors/fcm/1-5/examples/svn-hooks/post-revprop-change	(revision 1981)
+++ vendors/fcm/1-5/examples/svn-hooks/post-revprop-change	(revision 1981)
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# POST-REVPROP-CHANGE HOOK
+#
+# The post-revprop-change hook is invoked after a revision property
+# has been added, modified or deleted.  Subversion runs this hook by
+# invoking a program (script, executable, binary, etc.) named
+# 'post-revprop-change' (for which this file is a template), with the
+# following ordered arguments:
+#
+#   [1] REPOS-PATH   (the path to this repository)
+#   [2] REV          (the revision that was tweaked)
+#   [3] USER         (the username of the person tweaking the property)
+#   [4] PROPNAME     (the property that was changed)
+#   [5] ACTION       (the property was 'A'dded, 'M'odified, or 'D'eleted)
+#
+#   [STDIN] PROPVAL  ** the old property value is passed via STDIN.
+#
+# Because the propchange has already completed and cannot be undone,
+# the exit code of the hook program is ignored.  The hook program
+# can use the 'svnlook' utility to help it examine the
+# new property value.
+#
+# On a Unix system, the normal procedure is to have 'post-revprop-change'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-revprop-change' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-revprop-change.bat' or 'post-revprop-change.exe',
+# but the basic idea is the same.
+# 
+# The hook program typically does not inherit the environment of
+# its parent process.  For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+# 
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+REPOS="$1"
+REV="$2"
+USER="$3"
+PROPNAME="$4"
+ACTION="$5"
+
+REPOS_NAME=${REPOS##*/}
+LOG_DIR=~fcm/svn/revprop-change
+LOG_FILE=$LOG_DIR/${REPOS_NAME}.post.log
+TRAC=${REPOS%/svn/live/*}/trac/live/${REPOS_NAME%_svn}
+CMD="trac-admin $TRAC resync $REV"
+
+echo "$(date): Updating log of revision $REV ..." >$LOG_FILE
+$CMD 1>>$LOG_FILE 2>&1
+RC=$?
+
+if ((RC != 0)); then
+  /bin/mail -s "$CMD failed" my.name@somewhere.org <<EOF
+$(<$LOG_FILE)
+EOF
+fi
+
+exit $RC
Index: vendors/fcm/1-5/examples/svn-hooks/pre-commit
===================================================================
--- vendors/fcm/1-5/examples/svn-hooks/pre-commit	(revision 1981)
+++ vendors/fcm/1-5/examples/svn-hooks/pre-commit	(revision 1981)
@@ -0,0 +1,83 @@
+#!/bin/ksh
+
+# PRE-COMMIT HOOK
+#
+# The pre-commit hook is invoked before a Subversion txn is
+# committed.  Subversion runs this hook by invoking a program
+# (script, executable, binary, etc.) named 'pre-commit' (for which
+# this file is a template), with the following ordered arguments:
+#
+#   [1] REPOS-PATH   (the path to this repository)
+#   [2] TXN-NAME     (the name of the txn about to be committed)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the txn is committed; but
+# if it exits with failure (non-zero), the txn is aborted, no commit
+# takes place, and STDERR is returned to the client.   The hook
+# program can use the 'svnlook' utility to help it examine the txn.
+#
+# On a Unix system, the normal procedure is to have 'pre-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+#   ***  NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT  ***
+#   ***  FOR REVISION PROPERTIES (like svn:log or svn:author).   ***
+#
+#   This is why we recommend using the read-only 'svnlook' utility.
+#   In the future, Subversion may enforce the rule that pre-commit
+#   hooks should not modify the versioned data in txns, or else come
+#   up with a mechanism to make it safe to do so (by informing the
+#   committing client of the changes).  However, right now neither
+#   mechanism is implemented, so hook writers just have to be careful.
+#
+# Note that 'pre-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-commit.bat' or 'pre-commit.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process.  For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+# 
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+REPOS="$1"
+TXN="$2"
+
+# Check that the author of this commit has the rights to perform
+# the commit on the files and directories being modified.
+script=$REPOS/hooks/svnperms.py
+config=$REPOS/hooks/svnperms.conf
+if [[ -x $script && -a $config ]]; then
+  $script -r "$REPOS" -t "$TXN" -f $config || exit 1
+elif [[ -L $config ]]; then
+  echo \
+"Error: $config is missing.
+Please contact your system manager to fix this problem." >&2
+  exit 1
+fi
+
+# Check that the size of the transaction does not exceed 5Mb
+size=$(du -b -s $REPOS/db/transactions/$TXN.txn | cut -f 1)
+if (($size > 5242880)); then
+  size=$(du -h -s $REPOS/db/transactions/$TXN.txn | cut -f 1)
+  echo \
+"Your changeset requires a disk space ($size) exceeding our 5Mb limit.
+Please contact the FCM team if you need to bypass this restriction." >&2
+  exit 1
+fi
+
+# All checks passed, so allow the commit.
+exit 0
Index: vendors/fcm/1-5/examples/svn-hooks/pre-revprop-change
===================================================================
--- vendors/fcm/1-5/examples/svn-hooks/pre-revprop-change	(revision 1981)
+++ vendors/fcm/1-5/examples/svn-hooks/pre-revprop-change	(revision 1981)
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+# PRE-REVPROP-CHANGE HOOK
+#
+# The pre-revprop-change hook is invoked before a revision property
+# is added, modified or deleted.  Subversion runs this hook by invoking
+# a program (script, executable, binary, etc.) named 'pre-revprop-change'
+# (for which this file is a template), with the following ordered
+# arguments:
+#
+#   [1] REPOS-PATH   (the path to this repository)
+#   [2] REVISION     (the revision being tweaked)
+#   [3] USER         (the username of the person tweaking the property)
+#   [4] PROPNAME     (the property being set on the revision)
+#   [5] ACTION       (the property is being 'A'dded, 'M'odified, or 'D'eleted)
+#
+#   [STDIN] PROPVAL  ** the new property value is passed via STDIN.
+#
+# If the hook program exits with success, the propchange happens; but
+# if it exits with failure (non-zero), the propchange doesn't happen.
+# The hook program can use the 'svnlook' utility to examine the 
+# existing value of the revision property.
+#
+# WARNING: unlike other hooks, this hook MUST exist for revision
+# properties to be changed.  If the hook does not exist, Subversion 
+# will behave as if the hook were present, but failed.  The reason
+# for this is that revision properties are UNVERSIONED, meaning that
+# a successful propchange is destructive;  the old value is gone
+# forever.  We recommend the hook back up the old value somewhere.
+#
+# On a Unix system, the normal procedure is to have 'pre-revprop-change'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'pre-revprop-change' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-revprop-change.bat' or 'pre-revprop-change.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process.  For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+# 
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+REPOS="$1"
+REV="$2"
+USER="$3"
+PROPNAME="$4"
+ACTION="$5"
+
+REPOS_NAME=${REPOS##*/}
+LOG_DIR=~fcm/svn/revprop-change
+LOG_FILE=$LOG_DIR/${REPOS_NAME}.pre.log
+CMD=${REPOS}/hooks/pre-revprop-change.pl
+
+$CMD "$@" <&0 1>$LOG_FILE 2>&1
+RC=$?
+
+if ((RC != 0)); then
+  /bin/mail -s "$CMD failed" my.name@somewhere.org <<EOF
+$(<$LOG_FILE)
+EOF
+fi
+
+exit $RC
Index: vendors/fcm/1-5/examples/svn-hooks/pre-revprop-change.pl
===================================================================
--- vendors/fcm/1-5/examples/svn-hooks/pre-revprop-change.pl	(revision 1981)
+++ vendors/fcm/1-5/examples/svn-hooks/pre-revprop-change.pl	(revision 1981)
@@ -0,0 +1,221 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   pre-revprop-change.pl
+#
+# SYNOPSIS
+#   pre-revprop-change.pl REPOS REV USER PROPNAME ACTION <&0
+#
+# DESCRIPTION
+#   This script e-mails authors and watchers when a user attempts to modify the
+#   svn:log of a particular revision. The new property value is passed via
+#   STDIN. Watchers are set in the "watch.cfg" file, which should be located in
+#   the root within the Subversion repository. The watch.cfg file is a standard
+#   INI-type configuration file with the basic format:
+#
+#     [repos_base]
+#     path/in/repos = list,of,watchers
+#
+#   E.g.:
+#
+#     [FCM_svn]
+#
+#     FCM/trunk/src            = fcm,frsn
+#     FCM/trunk/doc            = fcm,frsn,frdm,frbj
+#     FCM/branches/dev/*/*/src = fcm,frsn
+#     FCM/branches/dev/*/*/doc = fcm,frsn,frdm,frbj
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use File::Basename;
+use File::Spec;
+use File::Temp qw/tempfile/;
+use Mail::Mailer;
+use Config::IniFiles;
+
+# Arguments
+# ------------------------------------------------------------------------------
+my ($repos, $rev, $user, $propname, $action) = @ARGV;
+
+# Basename of repository
+my $base = basename $repos;
+
+# Top level variables
+# ------------------------------------------------------------------------------
+# The watch configuration file, at the root of the current repository
+my $watch_config = 'watch.cfg';
+
+# Determine whether change is permitted
+# ------------------------------------------------------------------------------
+# Switch off for most revision properties
+my $return = 1;
+
+# Switch on only for "svn:log"
+$return = 0 if $propname eq 'svn:log' and $action eq 'M';
+
+if ($return == 0) {
+  # Diagnostic
+  print $repos, ': ', $propname, ' at revision ', $rev,
+        ' is being modified by ', $user, '.', "\n";
+
+  my %mail_to = ();
+
+  # Mail original author, if he/she is not the current user
+  # ----------------------------------------------------------------------------
+  # Find out who is the author of the changeset at $rev
+  my @command = (qw/svnlook author -r/, $rev, $repos);
+  my $author  = qx(@command);
+  chomp $author;
+
+  # Add author to mail list, if necessary
+  $mail_to{$author} = 1 if $author ne $user;
+
+  # Mail watchers, if changeset involves files being watched
+  # ----------------------------------------------------------------------------
+  # Find out what files were involved in the changeset
+  @command    = (qw/svnlook changed -r/, $rev, $repos);
+  my @changed = qx(@command);
+
+  # Get list of watchers for current repository
+  my %watch = &get_watchers ();
+
+  for my $file (@changed) {
+    # Remove trailing line break and leading status
+    chomp $file;
+    $file = substr ($file, 4);
+
+    # Find out who are watching this file
+    my @watchers = &who_watch ($file, \%watch);
+
+    # If necessary, add watchers to list, unless he/she is the current user
+    for my $watcher (@watchers) {
+      $mail_to{$watcher} = 1 if $user ne $watcher;
+    }
+  }
+
+  # Send mail if necessary
+  # ----------------------------------------------------------------------------
+  if (keys %mail_to) {
+    # Old value of revision property
+    my @command = (qw/svnlook pg -r/, $rev, '--revprop', $repos, $propname);
+    my $oldval  = qx(@command);
+
+    # Addresses as a comma-separated list
+    my $address = join (',', sort keys %mail_to);
+
+    # Invoke a new Mail::Mailer object
+    my $mailer  = Mail::Mailer->new ();
+    $mailer->open ({
+      From    => 'my.name@somewhere.org',
+      To      => $address,
+      Subject => $base . '@' . $rev . ': ' . $propname . ' modified by ' . $user,
+    }) or die 'Cannot e-mail ', $address, ' (', $!, ')';
+
+    # Write the mail
+    # Old value
+    print $mailer <<EOF;
+Old value:
+----------
+$oldval
+
+New value:
+----------
+EOF
+
+    # New value from STDIN
+    print $mailer $_ while (<STDIN>);
+
+    # Send the mail
+    $mailer->close;
+
+    print 'Mail notification has been sent to ', $address, '.', "\n";
+
+  } else {
+    print 'No mail notification is required for this change.', "\n";
+  }
+}
+
+exit $return;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %watch = &get_watchers ();
+#
+# DESCRIPTION
+#   From the list of watch configuration files, get a list of watched files and
+#   their watchers for the current repository. Returns the results in a hash
+#   containing the watched paths (keys) and their corresponding list of
+#   watchers (values, array references).
+# ------------------------------------------------------------------------------
+
+sub get_watchers {
+  my %watch;
+
+  # Get contents in watch file
+  my @command = (qw/svnlook cat/, $repos, $watch_config);
+  my @output  = qx(@command);
+
+  if (@output) {
+    # Write result to temporary file
+    my ($fh, $temp_file) = tempfile (UNLINK => 1);
+    print $fh @output;
+    close $fh;
+
+    # Parse the configuration
+    my $cfg = Config::IniFiles->new ('-file' => $temp_file);
+
+    # Check if current repository name exists in the configuration file
+    if ($cfg and $cfg->SectionExists ($base)) {
+      # The name of the parameter is a sub-path in the repository
+      # The value of the parameter is a comma-delimited list of the watchers
+      my $separator = '/';
+      for my $parameter ($cfg->Parameters ($base)) {
+        # Parameter may contain wildcards * and ?
+        $parameter =~ s#\*#[^$separator]*#g;
+        $parameter =~ s#\?#[^$separator]#g;
+
+        $watch{$parameter} = [split (/,/, $cfg->val ($base, $parameter))];
+      }
+    }
+  }
+
+  return %watch;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   my @watchers = &who_watch ($file, \%watch);
+#
+# DESCRIPTION
+#   Using the %watch hash, determine who are the watchers watching $file.
+#   Returns the list of watchers.
+# ------------------------------------------------------------------------------
+
+sub who_watch {
+  my $file  = $_[0];
+  my %watch = %{ $_[1] };
+
+  my %watchers;
+  my $separator = '/';
+
+  for my $watched (keys %watch) {
+    # Test if $file or its parent path is being watched
+    next unless $file =~ m#^$watched(?:$separator+|$)#;
+
+    # Add watchers to the return list
+    $watchers{$_} = 1 for (@{ $watch{$watched} });
+  }
+
+  return keys %watchers;
+}
+
+# ------------------------------------------------------------------------------
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/Base.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Base.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Base.pm	(revision 1981)
@@ -0,0 +1,112 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Base
+#
+# DESCRIPTION
+#   This is base class for all FCM OO packages.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Base;
+
+# Standard pragma
+use strict;
+use warnings;
+
+use Fcm::Config;
+
+my @scalar_properties = (
+  'config', # instance of Fcm::Config, configuration setting
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::Base->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::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} = Fcm::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: vendors/fcm/1-5/lib/Fcm/Build.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Build.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Build.pm	(revision 1981)
@@ -0,0 +1,1606 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Build
+#
+# DESCRIPTION
+#   This is the top level class for the FCM build system.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package Fcm::Build;
+use base qw(Fcm::ConfigSystem);
+
+use Carp             qw{croak}                                       ;
+use Cwd              qw{cwd}                                         ;
+use Fcm::BuildSrc                                                    ;
+use Fcm::BuildTask                                                   ;
+use Fcm::Config                                                      ;
+use Fcm::Dest                                                        ;
+use Fcm::CfgLine                                                     ;
+use Fcm::Timer       qw{timestamp_command}                           ;
+use Fcm::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 = $Fcm::Config::DELIMITER_LIST;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::Build->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::Build class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::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 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)->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 (
+        /$Fcm::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, Fcm::CfgLine->new (
+      LABEL => $prefix . $Fcm::Config::DELIMITER . $key,
+      VALUE => join ($Fcm::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, Fcm::CfgLine->new (
+      LABEL => $prefix . $Fcm::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),
+    [Fcm::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} = Fcm::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} = Fcm::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')} = Fcm::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} = Fcm::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} = Fcm::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} = Fcm::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} = Fcm::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 = not -r $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 .= $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 (/$Fcm::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/$Fcm::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 (@Fcm::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 (not -r $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 unless -f $file and -r $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} = Fcm::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} = Fcm::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.
+  # ----------------------------------------------------------------------------
+  for my $name (keys %pkg) {
+    (my $dname = $name) =~ s/\.\w+$//;
+    next if $dname eq $name;
+    next if $self->srcpkg ($dname);
+
+    $self->dummysrcpkg ($dname, Fcm::BuildSrc->new (PKGNAME => $dname))
+      unless $self->dummysrcpkg ($dname);
+    push @{ $self->dummysrcpkg ($dname)->children }, $pkg{$name};
+  }
+
+  # 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: vendors/fcm/1-5/lib/Fcm/Build/Fortran.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Build/Fortran.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Build/Fortran.pm	(revision 1981)
@@ -0,0 +1,536 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+# ------------------------------------------------------------------------------
+package Fcm::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
+
+Fcm::Build::Fortran
+
+=head1 SYNOPSIS
+
+    use Fcm::Build::Fortran;
+    my $fortran_util = Fcm::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
+
+(C) Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/BuildSrc.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/BuildSrc.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/BuildSrc.pm	(revision 1981)
@@ -0,0 +1,1498 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::BuildSrc
+#
+# DESCRIPTION
+#   This is a class to group functionalities of source in a build.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+package Fcm::BuildSrc;
+use base qw{Fcm::Base};
+
+use Carp qw{croak};
+use Cwd qw{cwd};
+use Fcm::Build::Fortran;
+use Fcm::CfgFile;
+use Fcm::CfgLine;
+use Fcm::Config;
+use Fcm::Timer qw{timestamp_command};
+use Fcm::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 Fcm::Build::Fortran.
+  my $FORTRAN_UTIL;
+  sub _get_fortran_util {
+    $FORTRAN_UTIL ||= Fcm::Build::Fortran->new();
+    return $FORTRAN_UTIL;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::BuildSrc->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::BuildSrc class. See
+#   above for allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my ($class, %args) = @_;
+  my $self = bless(Fcm::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 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 (
+        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
+#   This method returns the property flagsbase (derived from pkgname) the base
+#   name of the flags-file (to indicate changes in a particular build tool) for
+#   $flag, which can have the value:
+#     *FLAGS  - compiler flags flags-file
+#     *PPKEYS - pre-processor keys (i.e. macro definitions) flags-file
+#     LD      - linker flags-file
+#     LDFLAGS - linker flags flags-file
+#   If $index is set, the $index'th element in pkgnames is used for the package
+#   name.
+# ------------------------------------------------------------------------------
+
+sub flagsbase {
+  my ($self, $flag, $index) = @_;
+
+  (my $pkg = $index ? $self->pkgnames->[$index] : $self->pkgname) =~ s/\.\w+$//;
+
+  if ($self->is_type_all ('SOURCE')) {
+    if ($flag eq 'FLAGS' or $flag eq 'PPKEYS' and $self->lang) {
+      my %tool_src = %{ $self->setting ('TOOL_SRC') };
+      $flag = $tool_src{$self->lang}{$flag} ? $tool_src{$self->lang}{$flag} : '';
+    }
+  }
+
+  if ($flag) {
+    return join ('__', ($flag, $pkg ? $pkg : ())) .
+           $self->setting (qw/OUTFILE_EXT FLAGS/);
+
+  } else {
+    return undef;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# 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(/$Fcm::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 . $Fcm::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(/$Fcm::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 (
+        /$Fcm::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(/$Fcm::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 .= $Fcm::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 /$Fcm::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 = Fcm::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()},
+            Fcm::CfgLine->new(
+              label => $LABEL_OF_EXCL_DEP,
+              value => $key . $Fcm::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: vendors/fcm/1-5/lib/Fcm/BuildTask.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/BuildTask.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/BuildTask.pm	(revision 1981)
@@ -0,0 +1,340 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::BuildTask
+#
+# DESCRIPTION
+#   This class hosts information of a build task in the FCM build system.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::BuildTask;
+@ISA = qw(Fcm::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 Fcm::Base;
+use Fcm::Timer;
+use Fcm::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 Fcm::BuildSrc instance
+  'output',      # output file
+  'outputmtime', # output file modification time
+  'target',      # target name for this task
+  'targetpath',  # search path for the target
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::BuildTask->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::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 = Fcm::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 Fcm::BuildTask instances. The method
+#   returns true if the task has been performed to create a new version of the
+#   target.
+# ------------------------------------------------------------------------------
+
+sub action {
+  my $self     = shift;
+  my %args     = @_;
+  my $tasklist = exists $args{TASKLIST} ? $args{TASKLIST} : {};
+
+  return unless $self->actiontype;
+
+  my $uptodate     = 1;
+  my $dep_uptodate = 1;
+
+  # Check if dependencies are up to date
+  # ----------------------------------------------------------------------------
+  for my $depend (@{ $self->dependency }) {
+    if (exists $tasklist->{$depend}) {
+      if (not $tasklist->{$depend}->output) {
+        # Dependency task output is not set, performs its task action
+        if ($tasklist->{$depend}->action (TASKLIST => $tasklist)) {
+          $uptodate     = 0;
+          $dep_uptodate = 0;
+        }
+      }
+
+    } elsif ($self->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 -r $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 -r $oldfile) {
+        # Read old file
+        open FILE, '<', $oldfile;
+        my @oldlines = readline 'FILE';
+        close FILE;
+
+        # Compare old contents and new contents
+        if (@oldlines eq @$newlines) {
+          $update_required = grep {
+            $oldlines[$_] ne $newlines->[$_];
+          } (0 .. $#oldlines);
+        }
+      }
+
+      if ($update_required) {
+        # Update the pre-processed source or interface file
+        # ----------------------------------------------------------------------
+        # Determine container directory of the  pre-processed or interface file
+        my $newfile = @path ? catfile ($path[0], $base) : $base;
+
+        # Create the container directory if necessary
+        if (not -d $path[0]) {
+          print 'Make directory: ', $path[0], "\n"
+            if $self->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: vendors/fcm/1-5/lib/Fcm/CLI.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI.pm	(revision 1981)
@@ -0,0 +1,172 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI;
+
+use Carp qw{croak};
+use Fcm::CLI::Config;
+use Fcm::CLI::Exception;
+use Fcm::Util::ClassLoader;
+use File::Basename qw{basename};
+use Getopt::Long qw{GetOptions};
+use Scalar::Util qw{blessed};
+
+################################################################################
+# Invokes the FCM command line interface
+sub invoke {
+    local(@ARGV) = @ARGV;
+    my $config          = Fcm::CLI::Config->instance();
+    my $subcommand_name = @ARGV ? shift(@ARGV) : q{};
+    my $subcommand      = $config->get_subcommand_of($subcommand_name);
+    eval {
+        if (!$subcommand) {
+            croak(Fcm::CLI::Exception->new({message => 'unknown command'}));
+        }
+        my ($opts_ref, $args_ref, $is_help) = _parse_argv_using($subcommand);
+        my ($invoker_class, $invoker);
+        if ($is_help) {
+            $invoker_class
+                = _load_invoker_class_of($config->get_subcommand_of(q{}));
+            $invoker = $invoker_class->new({
+                command   => $subcommand_name,
+                arguments => [$subcommand_name],
+            });
+        }
+        else {
+            $invoker_class = _load_invoker_class_of($subcommand);
+            $invoker = $invoker_class->new({
+                command   => $subcommand_name,
+                options   => $opts_ref,
+                arguments => $args_ref,
+                (
+                    $subcommand->get_invoker_config()
+                    ? %{$subcommand->get_invoker_config()}
+                    : ()
+                ),
+            });
+        }
+        $invoker->invoke();
+    };
+    if ($@) {
+        if (Fcm::CLI::Exception->caught($@)) {
+            die(sprintf(
+                qq{%s%s: %s\nType "%s help%s" for usage\n},
+                basename($0),
+                ($subcommand_name ? qq{ $subcommand_name} : q{}),
+                $@->get_message(),
+                basename($0),
+                defined($subcommand) ? qq{ $subcommand_name} : q{},
+            ));
+        }
+        else {
+            die($@);
+        }
+    }
+}
+
+################################################################################
+# Parses options in @ARGV using the options settings of a subcommand
+sub _parse_argv_using {
+    my ($subcommand) = @_;
+    my %options = ();
+    my $is_help = undef;
+    if (($subcommand->get_options())) {
+        my $problem = q{};
+        local($SIG{__WARN__}) = sub {
+            ($problem) = @_;
+        };
+        my $success = GetOptions(
+            \%options,
+            (map {$_->get_arg_for_getopt_long()} ($subcommand->get_options())),
+        );
+        if (!$success) {
+            croak(Fcm::CLI::Exception->new({message => sprintf(
+                "option parse failed: %s", $problem,
+            )}));
+        }
+
+        OPTION:
+        for my $option ($subcommand->get_options()) {
+            if (!exists($options{$option->get_name()})) {
+                next OPTION;
+            }
+            if ($option->is_help()) {
+                $is_help = 1;
+            }
+            if (
+                $option->has_arg() == $option->ARRAY_ARG
+                && $option->get_delimiter()
+            ) {
+                $options{$option->get_name()} = [split(
+                    $option->get_delimiter(),
+                    join(
+                        $option->get_delimiter(),
+                        @{$options{$option->get_name()}},
+                    ),
+                )];
+            }
+        }
+    }
+    return (\%options, [@ARGV], $is_help);
+}
+
+################################################################################
+# Loads and returns the invoker class of a subcommand
+sub _load_invoker_class_of {
+    my ($subcommand) = @_;
+    my $invoker_class
+        = $subcommand->get_invoker_class() ? $subcommand->get_invoker_class()
+        :                                    'Fcm::CLI::Invoker'
+        ;
+    return Fcm::Util::ClassLoader::load($invoker_class);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI
+    Fcm::CLI::invoke();
+
+=head1 DESCRIPTION
+
+Invokes the FCM command line interface.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item invoke()
+
+Invokes the FCM command line interface.
+
+=back
+
+=head1 TO DO
+
+Move option/argument parsing to L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>?
+
+Use an OO interface?
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Config|Fcm::CLI::Config>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>,
+L<Fcm::CLI::Option|Fcm::CLI::Option>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Config.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Config.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Config.pm	(revision 1981)
@@ -0,0 +1,133 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Config;
+
+use Fcm::CLI::Config::Default;
+use List::Util qw{first};
+use Scalar::Util qw{blessed};
+
+my $INSTANCE;
+
+################################################################################
+# Class method: returns an instance of this class
+sub instance {
+    my ($class, $args_ref) = @_;
+    if ($args_ref || !$INSTANCE) {
+        $INSTANCE = bless({
+            core_subcommands => [@Fcm::CLI::Config::Default::CORE_SUBCOMMANDS],
+            vc_subcommands   => [@Fcm::CLI::Config::Default::VC_SUBCOMMANDS],
+            (defined($args_ref) ? %{$args_ref} : ()), 
+        }, $class);
+    }
+    return $INSTANCE;
+}
+
+################################################################################
+# Returns a subcommand matching $key
+sub get_subcommand_of {
+    my ($self, $key) = @_;
+    if (blessed($key) && $key->isa('Fcm::CLI::Subcommand')) {
+        return first {"$_" eq "$key"} ($self->get_subcommands());
+    }
+    else {
+        return first {$_->has_a_name($key)} ($self->get_subcommands());
+    }
+}
+
+################################################################################
+# Returns the subcommands
+sub get_subcommands {
+    my ($self) = @_;
+    my @return = ($self->get_core_subcommands(), $self->get_vc_subcommands());
+    return (wantarray() ? @return : \@return);
+}
+
+################################################################################
+# Returns the core subcommands
+sub get_core_subcommands {
+    my ($self) = @_;
+    return (
+        wantarray() ? @{$self->{core_subcommands}} : $self->{core_subcommands}
+    );
+}
+
+################################################################################
+# Returns the subcommands that are relevant only with a VC system
+sub get_vc_subcommands {
+    my ($self) = @_;
+    return (wantarray() ? @{$self->{vc_subcommands}} : $self->{vc_subcommands});
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Config
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Config;
+    $cli_config = Fcm::CLI::Config->instance();
+    $subcommand = $cli_config->get_subcommand_of($key);
+    @subcommands = $cli_config->get_subcommands();
+    @core_subcommands = $cli_config->get_core_subcommands();
+    @vc_subcommands = $cli_config->get_vc_subcommands();
+
+=head1 DESCRIPTION
+
+This class provides the configuration of the FCM command line interface.
+
+=head1 METHODS
+
+=over 4
+
+=item instance($arg_ref)
+
+Returns an instance of this class.
+
+Creates the instance on first call, or replaces it with a new one if $args_ref
+is defined in subsequent call. $args_ref should be a reference to a hash. The
+hash can contain I<core_subcommands> and I<vc_subcommands>. Each of these
+settings should point to an array reference containing L<Fcm::CLI::Subcommand>
+objects. If the setting is unspecified, it uses the default from
+L<Fcm::CLI::Config::Default|Fcm::CLI::Config::Default>.
+
+=item get_subcommand_of($key)
+
+Returns a L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand> object matching the
+search $key. Returns undef if there is no match.
+
+=item get_subcommands()
+
+Short-hand for:
+    ($self->get_core_subcommands(), $self->get_vc_subcommands())
+
+=item get_core_subcommands()
+
+Returns the core subcommands.
+
+=item get_vc_subcommands()
+
+Returns the subcommands that are relevant only in the presence of a VC system.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Config::Default|Fcm::CLI::Config::Default>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>,
+L<Fcm::CLI::Option|Fcm::CLI::Option>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Config/Default.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Config/Default.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Config/Default.pm	(revision 1981)
@@ -0,0 +1,412 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Config::Default;
+
+use Fcm::CLI::Option;
+use Fcm::CLI::Subcommand;
+
+my %DESCRIPTION_OF = (
+    # --------------------------------------------------------------------------
+    BROWSER => <<'END_DESCRIPTION',
+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.
+END_DESCRIPTION
+    # --------------------------------------------------------------------------
+    BUILD => <<'END_DESCRIPTION',
+The path to a CFGFILE may be provided. Otherwise, the build system searches the
+default locations for a bld cfg file.
+
+If no option is specified, the options "-s 5 -t all -j 1 -v 1" are assumed.
+
+If the option for full build is specified, the sub-directories created by
+previous builds will be removed, so that the current build can start cleanly.
+
+The -s option can be used to limit the actions performed by the build system up
+to a named stage. The stages are:
+    "1", "s" or "setup"                - stage 1, setup
+    "2", "pp" or "pre_process"         - stage 2, pre-process
+    "3", "gd" or "generate_dependency" - stage 3, generate dependency
+    "4", "gi" or "generate_interface"  - stage 4, generate Fortran 9X interface
+    "5", "m", "make"                   - stage 5, make
+
+If a colon separated list of targets is specified using the -t option, the
+default targets specified in the configuration file will not be used.
+
+If archive mode is switched on, build sub-directories that are only used in the
+build process will be archived to TAR files. The default is off.
+
+If specified, the verbose level must be an integer greater than 0. Verbose
+level 0 is the quiet mode. Increasing the verbose level will increase the
+amount of diagnostic output.
+
+When a build is invoked, it sets up a lock file in the build root directory.
+The lock is normally removed at the end of the build. While the lock file is in
+place, 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 the --ignore-lock option.
+END_DESCRIPTION
+    # --------------------------------------------------------------------------
+    CFG_PRINTER => <<'END_DESCRIPTION',
+If no option is specified, the output will be sent to standard output.
+END_DESCRIPTION
+    # --------------------------------------------------------------------------
+    EXTRACT => <<'END_DESCRIPTION',
+The path to a CFG file may be provided. Otherwise, the extract system searches
+the default locations for an ext cfg file.
+
+If no option is specified, the system will attempt an incremental extract where
+appropriate.
+
+If specified, the verbose level must be an integer greater than 0. Verbose
+level 0 is the quiet mode. Increasing the verbose level will increase the
+amount of diagnostic output.
+
+When an extract is invoked, it sets up a lock file in the extract destination
+root directory. The lock is normally removed at the end of the extract. While
+the lock file is in place, other extract commands invoked in the same
+destination root directory will fail. If you need to bypass this check for
+whatever reason, you can invoke the extract system with the --ignore-lock
+option.
+END_DESCRIPTION
+    # --------------------------------------------------------------------------
+    EXTRACT_CONFIG_COMPARATOR => <<'END_DESCRIPTION',
+Compares the extract configurations of two similar extract configuration files
+CFGFILE1 and CFGFILE2.
+
+In normal mode with verbosity level 2 or above, displays the change log of each
+revision.
+
+In wiki mode, print revision tables in wiki format. The argument to the --wiki
+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.
+END_DESCRIPTION
+    # --------------------------------------------------------------------------
+    GUI => <<'END_DESCRIPTION',
+The optional argument PATH modifies the initial working directory of the GUI.
+END_DESCRIPTION
+    # --------------------------------------------------------------------------
+    KEYWORD => <<'END_DESCRIPTION',
+If no argument is specified, prints registered location keywords. Otherwise,
+prints the implied location keywords and revision keywords for the specified
+target.
+END_DESCRIPTION
+);
+
+my %OPTION_OF = (
+    ARCHIVE => Fcm::CLI::Option->new({
+        name        => 'archive',
+        letter      => 'a',
+        description => 'archives sub-directories on success',
+    }),
+
+    BROWSER => Fcm::CLI::Option->new({
+        name        => 'browser',
+        letter      => 'b',
+        description => 'specifies the web browser command',
+        has_arg     => Fcm::CLI::Option->SCALAR_ARG,
+    }),
+
+    CLEAN => Fcm::CLI::Option->new({
+        name        => 'clean',
+        description => 'cleans the destination',
+    }),
+
+    FULL => Fcm::CLI::Option->new({
+        name        => 'full',
+        letter      => 'f',
+        description => 'runs in full mode',
+    }),
+
+    HELP => Fcm::CLI::Option->new({
+        name        => 'help',
+        letter      => 'h',
+        description => 'prints help',
+        is_help     => 1,
+    }),
+
+    IGNORE_LOCK => Fcm::CLI::Option->new({
+        name        => 'ignore-lock',
+        description => 'ignores lock file',
+    }),
+
+    JOBS => Fcm::CLI::Option->new({
+        name        => 'jobs',
+        letter      => 'j',
+        description => 'number of parallel jobs',
+        has_arg     => Fcm::CLI::Option->SCALAR_ARG,
+    }),
+
+    OUTPUT => Fcm::CLI::Option->new({
+        name        => 'output',
+        letter      => 'o',
+        description => 'sends output to the specified file',
+        has_arg     => Fcm::CLI::Option->SCALAR_ARG,
+    }),
+
+    STAGE => Fcm::CLI::Option->new({
+        name        => 'stage',
+        letter      => 's',
+        description => 'runs command up to a named stage',
+        has_arg     => Fcm::CLI::Option->SCALAR_ARG,
+    }),
+
+    TARGETS => Fcm::CLI::Option->new({
+        name        => 'targets',
+        letter      => 't',
+        delimiter   => ':',
+        description => 'list of build targets, delimited by (:)',
+        has_arg     => Fcm::CLI::Option->ARRAY_ARG,
+    }),
+
+    VERBOSITY => Fcm::CLI::Option->new({
+        name        => 'verbose',
+        letter      => 'v',
+        description => 'verbose level',
+        has_arg     => Fcm::CLI::Option->SCALAR_ARG,
+    }),
+
+    WIKI => Fcm::CLI::Option->new({
+        name        => 'wiki',
+        letter      => 'w',
+        description => 'print revision tables in wiki format',
+        has_arg     => Fcm::CLI::Option->SCALAR_ARG,
+    }),
+);
+
+my %SUBCOMMAND_OF = (
+    BRANCH => Fcm::CLI::Subcommand->new({
+        names         => ['branch', 'br'],
+        synopsis      => 'branch utilities',
+        invoker_class => 'Fcm::CLI::Invoker::CM',
+        is_vc         => 1,
+    }),
+
+    BROWSER => Fcm::CLI::Subcommand->new({
+        names         => ['trac', 'www'],
+        synopsis      => 'invokes the browser for a version controlled target',
+        usage         => '[OPTIONS...] [TARGET]',
+        description   => $DESCRIPTION_OF{BROWSER},
+        invoker_class => 'Fcm::CLI::Invoker::Browser',
+        options       => [
+            $OPTION_OF{BROWSER},
+            $OPTION_OF{HELP},
+        ],
+    }),
+
+    BUILD => Fcm::CLI::Subcommand->new({
+        names          => ['build', 'bld'],
+        synopsis       => 'invokes the build system',
+        usage          => '[OPTIONS...] [CFGFILE]',
+        description    => $DESCRIPTION_OF{BUILD},
+        invoker_class  => 'Fcm::CLI::Invoker::ConfigSystem',
+        invoker_config => {
+            impl_class         => 'Fcm::Build',
+            cli2invoke_key_map => {
+                'archive'     => 'ARCHIVE',
+                'clean'       => 'CLEAN',
+                'full'        => 'FULL',
+                'ignore-lock' => 'IGNORE_LOCK',
+                'jobs'        => 'JOBS',
+                'stage'       => 'STAGE',
+                'targets'     => 'TARGETS',
+            },
+        },
+        options        => [
+            $OPTION_OF{ARCHIVE},
+            $OPTION_OF{CLEAN},
+            $OPTION_OF{FULL},
+            $OPTION_OF{HELP},
+            $OPTION_OF{IGNORE_LOCK},
+            $OPTION_OF{JOBS},
+            $OPTION_OF{STAGE},
+            $OPTION_OF{TARGETS},
+            $OPTION_OF{VERBOSITY},
+        ],
+    }),
+
+    CFG_PRINTER => Fcm::CLI::Subcommand->new({
+        names         => ['cfg'],
+        synopsis      => 'invokes the CFG file pretty printer',
+        usage         => '[OPTIONS...] [CFGFILE]',
+        description   => $DESCRIPTION_OF{CFG_PRINTER},
+        invoker_class => 'Fcm::CLI::Invoker::CfgPrinter',
+        options       => [
+            $OPTION_OF{HELP},
+            $OPTION_OF{OUTPUT},
+        ],
+    }),
+
+    CM => Fcm::CLI::Subcommand->new({
+        names => [qw{
+            add
+            blame     praise annotate ann
+            cat
+            checkout  co
+            cleanup
+            commit    ci
+            copy      cp
+            delete    del    remove   rm
+            diff      di
+            export
+            import
+            info
+            list      ls
+            lock
+            log
+            merge
+            mkdir
+            move      mv     rename   ren
+            propdel   pdel   pd
+            propedit  pedit  pe
+            propget   pget   pg
+            proplist  plist  pl
+            propset   pset   ps
+            resolved
+            revert
+            status    stat   st
+            switch    sw
+            unlock
+            update    up
+        }],
+        invoker_class => 'Fcm::CLI::Invoker::CM',
+        is_vc         => 1,
+    }),
+
+    CONFLICTS => Fcm::CLI::Subcommand->new({
+        names         => ['conflicts', 'cf'],
+        synopsis      => 'resolves conflicts in your working copy',
+        usage         => '[PATH]',
+        invoker_class => 'Fcm::CLI::Invoker::CM',
+        is_vc         => 1,
+    }),
+
+    EXTRACT => Fcm::CLI::Subcommand->new({
+        names          => ['extract', 'ext'],
+        synopsis       => 'invokes the extract system',
+        usage          => '[OPTIONS...] [CFGFILE]',
+        description    => $DESCRIPTION_OF{EXTRACT},
+        invoker_class  => 'Fcm::CLI::Invoker::ConfigSystem',
+        invoker_config => {
+            impl_class         => 'Fcm::Extract',
+            cli2invoke_key_map => {
+                'clean'       => 'CLEAN',
+                'full'        => 'FULL',
+                'ignore-lock' => 'IGNORE_LOCK',
+            },
+        },
+        options        => [
+            $OPTION_OF{CLEAN},
+            $OPTION_OF{FULL},
+            $OPTION_OF{HELP},
+            $OPTION_OF{IGNORE_LOCK},
+            $OPTION_OF{VERBOSITY},
+        ],
+    }),
+
+    EXTRACT_CONFIG_COMPARATOR => Fcm::CLI::Subcommand->new({
+        names         => ['cmp-ext-cfg'],
+        synopsis      => 'invokes the extract configuration files comparator',
+        usage         => '[OPTIONS...] CFGFILE1 CFGFILE2',
+        description   => $DESCRIPTION_OF{EXTRACT_CONFIG_COMPARATOR},
+        invoker_class => 'Fcm::CLI::Invoker::ExtractConfigComparator',
+        options       => [
+            $OPTION_OF{HELP},
+            $OPTION_OF{VERBOSITY},
+            $OPTION_OF{WIKI},
+        ],
+    }),
+
+    GUI => Fcm::CLI::Subcommand->new({
+        names         => ['gui'],
+        synopsis      => 'invokes the GUI wrapper for code management commands',
+        usage         => '[PATH]',
+        description   => $DESCRIPTION_OF{GUI},
+        invoker_class => 'Fcm::CLI::Invoker::GUI',
+    }),
+
+    HELP => Fcm::CLI::Subcommand->new({
+        names         => ['help', q{?}, q{}],
+        synopsis      => 'displays the usage of this program or its subcommands',
+        usage         => '[SUBCOMMAND]',
+        description   => q{},
+        invoker_class => 'Fcm::CLI::Invoker::Help',
+        options       => [$OPTION_OF{HELP}],
+    }),
+
+    KEYWORD => Fcm::CLI::Subcommand->new({
+        names         => ['keyword-print', 'kp'],
+        synopsis      => 'prints registered location and/or revision keywords',
+        usage         => '[TARGET]',
+        description   => $DESCRIPTION_OF{KEYWORD},
+        invoker_class => 'Fcm::CLI::Invoker::KeywordPrinter',
+        options       => [$OPTION_OF{HELP}],
+    }),
+
+    MKPATCH => Fcm::CLI::Subcommand->new({
+        names         => ['mkpatch'],
+        synopsis      => 'creates patches from specified revisions of a URL',
+        usage         => '[OPTIONS] URL [OUTDIR]',
+        invoker_class => 'Fcm::CLI::Invoker::CM',
+        is_vc         => 1,
+    }),
+);
+
+our @CORE_SUBCOMMANDS = (
+    $SUBCOMMAND_OF{HELP},
+    $SUBCOMMAND_OF{BUILD},
+    $SUBCOMMAND_OF{CFG_PRINTER},
+);
+
+our @VC_SUBCOMMANDS = (
+    $SUBCOMMAND_OF{BRANCH},
+    $SUBCOMMAND_OF{BROWSER},
+    $SUBCOMMAND_OF{CONFLICTS},
+    $SUBCOMMAND_OF{EXTRACT},
+    $SUBCOMMAND_OF{EXTRACT_CONFIG_COMPARATOR},
+    $SUBCOMMAND_OF{GUI},
+    $SUBCOMMAND_OF{KEYWORD},
+    $SUBCOMMAND_OF{MKPATCH},
+    $SUBCOMMAND_OF{CM},
+);
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Config::Default
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Config::Default;
+    @core_subcommands = @Fcm::CLI::Config::Default::CORE_SUBCOMMANDS;
+    @vc_subcommands = @Fcm::CLI::Config::Default::VC_SUBCOMMANDS;
+
+=head1 DESCRIPTION
+
+This module stores the default configuration of the FCM command line interface.
+It should only be used by L<Fcm::CLI::Config|Fcm::CLI::Config>.
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Config|Fcm::CLI::Config>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>,
+L<Fcm::CLI::Option|Fcm::CLI::Option>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Exception.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Exception.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Exception.pm	(revision 1981)
@@ -0,0 +1,42 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Exception;
+use base qw{Fcm::Exception};
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Exception
+
+=head1 SYNOPSIS
+
+    use Carp qw{croak};
+    use Fcm::CLI::Exception;
+    croak(Fcm::CLI::Exception->new({message => 'something is wrong'}));
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::Exception|Fcm::Exception>. This exception is thrown
+on errors associated with the command line interface.
+
+=head1 METHODS
+
+See L<Fcm::Exception|Fcm::Exception> for a list of methods.
+
+=head1 SEE ALSO
+
+L<Fcm::Exception|Fcm::Exception>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker.pm	(revision 1981)
@@ -0,0 +1,136 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker;
+
+use Carp qw{croak};
+use Fcm::CLI::Exception;
+
+################################################################################
+# Constructor
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Returns the name of the (sub)command as given by the user
+sub get_command {
+    my ($self) = @_;
+    return $self->{command};
+}
+
+################################################################################
+# Returns a reference to a hash containing the options
+sub get_options {
+    my ($self) = @_;
+    return (wantarray() ? %{$self->{options}} : $self->{options});
+}
+
+################################################################################
+# Returns a reference to an array containing the arguments
+sub get_arguments {
+    my ($self) = @_;
+    return (wantarray() ? @{$self->{arguments}} : $self->{arguments});
+}
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my $message = "command not implemented\n";
+    $message .= sprintf("opts:");
+    for my $key (sort keys(%{$self->get_options()})) {
+        my $value = $self->get_options()->{$key};
+        $message .= sprintf(
+            " [%s=%s]",
+            $key,
+            ($value && ref($value) eq 'ARRAY' ? join(q{, }, @{$value}) : $value)
+        );
+    }
+    $message .= sprintf("\n");
+    $message .= sprintf("args: [%s]\n", join(q{] [}, $self->get_arguments()));
+    croak(Fcm::CLI::Exception->new({message => $message}));
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Invoker
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker;
+    $invoker = Fcm::CLI::Invoker->new({
+        command   => $command,
+        options   => \%options,
+        arguments => $arguments,
+    });
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This is the base class for an invoker of a FCM sub-system from the CLI.
+Sub-classes should override the invoke() method.
+
+=head1 METHODS
+
+=over 4
+
+=item new($args_ref)
+
+Constructor. It accepts a hash reference as an argument. The element I<command>
+should be set to the actual (sub)command as specified by the user. The element
+I<options> should be a reference to a hash containing the specified command line
+options. The element I<arguments> should be a reference to an array containing
+the remaining command line arguments.
+
+=item get_command()
+
+Returns the actual (sub)command as specified by the user.
+
+=item get_options()
+
+Returns a hash containing the specified command line options. In scalar context,
+returns a reference to the hash.
+
+=item get_arguments()
+
+Returns an array containing the (remaining) command line arguments. In scalar
+context, returns a reference to the array.
+
+=item invoke()
+
+Sub-classes should override this method. Calling the method in this base
+class causes the system to croak() with a
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::CLI::Exception|Fcm::CLI::Exception>
+
+The C<invoke()> croak() with this exception.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/Browser.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/Browser.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/Browser.pm	(revision 1981)
@@ -0,0 +1,119 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::Browser;
+use base qw{Fcm::CLI::Invoker};
+
+use Carp qw{croak};
+use Fcm::CLI::Exception;
+use Fcm::Config;
+use Fcm::Keyword;
+use Fcm::Util qw{expand_tilde get_url_of_wc is_wc run_command};
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my $config = Fcm::Config->instance();
+    my $browser
+        = $self->get_options()->{browser} ? $self->get_options()->{browser}
+        :                                   $config->setting(qw/WEB_BROWSER/)
+        ;
+    my ($target) = $self->get_arguments();
+    if (!$target) {
+        if (is_wc()) {
+            $target = q{.};
+        }
+        else {
+            croak(Fcm::CLI::Exception->new({
+                message => 'no TARGET specified and . not a working copy',
+            }));
+        }
+    }
+    $target = expand_tilde($target);
+    if (-e $target) {
+        $target = get_url_of_wc($target);
+    }
+
+    my $browser_url = Fcm::Keyword::get_browser_url($target);
+    my @command = (split(qr{\s+}xms, $browser), $browser_url);
+    run_command(\@command, METHOD => 'exec', PRINT => 1);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Invoker::Browser
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::Browser;
+    $invoker = Fcm::CLI::Invoker::Browser->new({
+        command   => $command,
+        options   => \%options,
+        arguments => $arguments,
+    });
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
+methods. An object of this class is used to invoke a web browser of a VC
+location.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item invoke()
+
+Invokes a web browser for a VC target, if it can be mapped to a browser URL. If
+a target is not specified in arguments, it uses the current working directory
+as the target.
+
+If the browser option is set, it is used as the browser command. Otherwise, the
+default browser is used.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::CLI::Exception|Fcm::CLI::Exception>
+
+The invoke() method can croak() with this exception if no target is specified
+and a target cannot be deduced from the current working directory.
+
+=item L<Fcm::Keyword::Exception|Fcm::Keyword::Exception>
+
+The invoke() method can croak() with this exception if the target cannot be
+mapped to a browser URL.
+
+=back
+
+=head1 TO DO
+
+Unit tests.
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>,
+L<Fcm::Keyword|Fcm::Keyword>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/CM.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/CM.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/CM.pm	(revision 1981)
@@ -0,0 +1,69 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::CM;
+use base qw{Fcm::CLI::Invoker};
+
+use Fcm::Cm qw{cli};
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    return cli($self->get_command(), @ARGV);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Invoker::CM
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::CM;
+    $invoker = Fcm::CLI::Invoker::CM->new();
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
+methods. An object of this class is used to invoke a command in the CM
+sub-system.
+
+It is worth noting that this is not yet a full implementation.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item invoke()
+
+Invokes a command in the CM sub-system.
+
+=back
+
+=head1 TO DO
+
+Bring the CM system into this framework.
+
+Unit tests.
+
+=head1 SEE ALSO
+
+L<Fcm::Cm|Fcm::Cm>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/CfgPrinter.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/CfgPrinter.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/CfgPrinter.pm	(revision 1981)
@@ -0,0 +1,105 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::CfgPrinter;
+use base qw{Fcm::CLI::Invoker};
+
+use Carp qw{croak};
+use Fcm::Exception;
+use Fcm::CfgFile;
+use Fcm::Config;
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my ($cfg_file) = $self->get_arguments();
+    if (!$cfg_file) {
+        croak(Fcm::CLI::Exception->new({message => 'no CFGFILE specified'}));
+    }
+    my $cfg = Fcm::CfgFile->new(SRC => $cfg_file);
+    Fcm::Config->instance()->verbose(0); # suppress message printing to STDOUT
+    my $read = $cfg->read_cfg();
+    if (!$read) {
+        croak(Fcm::Exception->new({message => sprintf(
+            "% :cannot read", $cfg_file,
+        )}));
+    }
+    $cfg->print_cfg($self->get_options()->{output});
+ }
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Invoker::CfgPrinter
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::CfgPrinter;
+    $invoker = Fcm::CLI::Invoker::CfgPrinter->new({
+        command   => $command,
+        options   => \%options,
+        arguments => $arguments,
+    });
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
+methods. An object of this class is used to invoke the pretty printer for FCM
+configuration files.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item invoke()
+
+Invokes the pretty printer for a FCM configuration file.
+
+If the I<output> option is set, output goes to the location specified by this
+value.  Otherwise, it prints to STDOUT.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::Exception|Fcm::Exception>
+
+The invoke() method can croak() with this exception if the configuration file
+cannot be read.
+
+=item L<Fcm::CLI::Exception|Fcm::CLI::Exception>
+
+The invoke() method can croak() with this exception if no configuration file is
+specified.
+
+=back
+
+=head1 TO DO
+
+Unit tests.
+
+=head1 SEE ALSO
+
+L<Fcm::CfgFile|Fcm::CfgFile>,
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/ConfigSystem.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/ConfigSystem.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/ConfigSystem.pm	(revision 1981)
@@ -0,0 +1,117 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::ConfigSystem;
+use base qw{Fcm::CLI::Invoker};
+
+use Cwd qw{cwd};
+use Fcm::CLI::Exception;
+use Fcm::Config;
+use Fcm::Util::ClassLoader;
+
+################################################################################
+# Returns a hash map to convert CLI options to system invoke options.
+sub get_cli2invoke_key_map {
+    my ($self) = @_;
+    return (
+          wantarray() ? %{$self->{cli2invoke_key_map}}
+        :               $self->{cli2invoke_key_map}
+    );
+}
+
+################################################################################
+# Returns the Fcm::ConfigSystem class for invoking the sub-system.
+sub get_impl_class {
+    my ($self) = @_;
+    return $self->{impl_class};
+}
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my $options_ref = $self->get_options();
+    if (exists($options_ref->{verbose})) {
+        Fcm::Config->instance()->verbose($options_ref->{verbose});
+    }
+
+    Fcm::Util::ClassLoader::load($self->get_impl_class());
+    my $system = $self->get_impl_class()->new();
+    my ($cfg_file) = $self->get_arguments();
+    $system->cfg()->src($cfg_file ? $cfg_file : cwd());
+
+    my %map = $self->get_cli2invoke_key_map();
+    my %invoke_args = map {($map{$_}, $options_ref->{$_})} keys(%map);
+    $system->invoke(%invoke_args);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Invoke::ConfigSystem
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::ConfigSystem;
+    $invoker = Fcm::CLI::Invoker::ConfigSystem->new({
+        command            => $command,
+        options            => \%options,
+        arguments          => $arguments,
+        impl_class         => $class_name,
+        cli2invoke_key_map => {
+            option => 'OPTION',
+            # ... more keys
+        },
+    });
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> and inherits all its
+methods. An object of this class is used to invoke a
+L<Fcm::ConfigSystem|Fcm::ConfigSystem>, e.g. extract and build.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item get_cli2invoke_key_map()
+
+Returns a hash containing a mapping table from the names of the relevant command
+line options to the names to be given to the invoke() method of the implementing
+L<Fcm::ConfigSystem|Fcm::ConfigSystem> object.
+
+=item get_impl_class()
+
+Returns the actual class that implements L<Fcm::ConfigSystem|Fcm::ConfigSystem>.
+An object of this implementation will be created and used by invoke().
+
+=item invoke()
+
+Invokes the L<Fcm::ConfigSystem|Fcm::ConfigSystem> sub-system. If a
+configuration file is not specified in the argument, it uses the current working
+directory.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::ConfigSystem|Fcm::ConfigSystem>,
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/ExtractConfigComparator.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/ExtractConfigComparator.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/ExtractConfigComparator.pm	(revision 1981)
@@ -0,0 +1,83 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::ExtractConfigComparator;
+use base qw{Fcm::CLI::Invoker};
+
+use Cwd qw{cwd};
+use Fcm::ExtractConfigComparator;
+use Fcm::Config;
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my ($cfg_file1, $cfg_file2) = $self->get_arguments();
+    if (exists($self->get_options()->{verbose})) {
+        Fcm::Config->instance()->verbose($self->get_options()->{verbose});
+    }
+
+    my $system = Fcm::ExtractConfigComparator->new({
+        files => [$cfg_file1, $cfg_file2], wiki => $self->get_options()->{wiki},
+    });
+    $system->invoke();
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::ExtractInvoker
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::ExtractConfigComparator;
+    $invoker = Fcm::CLI::Invoker::ExtractConfigComparator->new({
+        command   => $command,
+        options   => \%options,
+        arguments => $arguments,
+    });
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
+methods. An object of this class is used to invoke the extract configuration
+comparator.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item invoke()
+
+Invokes the extract configuration comparator.
+
+The I<wiki> option is mapped directly to that of the constructor of
+L<Fcm::ExtractConfigComparator|Fcm::ExtractConfigComparator> object.
+
+=back
+
+=head1 TO DO
+
+Unit tests.
+
+=head1 SEE ALSO
+
+L<Fcm::ExtractConfigComparator|Fcm::ExtractConfigComparator>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/GUI.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/GUI.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/GUI.pm	(revision 1981)
@@ -0,0 +1,70 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::GUI;
+use base qw{Fcm::CLI::Invoker};
+
+use Fcm::Util qw{run_command};
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my ($target) = $self->get_arguments();
+    run_command(['fcm_gui', ($target ? $target : ())], METHOD => 'exec');
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::GUIInvoker
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::GUI;
+    $invoker = Fcm::CLI::Invoker::GUI->new({
+        command   => $command,
+        options   => \%options,
+        arguments => $arguments,
+    });
+    $invoker->invoke($command, \%options, $target);
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
+methods. An object of this class is used to invoke the FCM GUI.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item invoke()
+
+Invokes the FCM GUI. If a target is specified as argument, it is the initial
+working directory of the GUI.
+
+=back
+
+=head1 TO DO
+
+Unit tests.
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/Help.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/Help.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/Help.pm	(revision 1981)
@@ -0,0 +1,220 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::Help;
+use base qw{Fcm::CLI::Invoker};
+
+use Carp qw{croak};
+use Fcm::CLI::Exception;
+use Fcm::CLI::Config;
+use Fcm::Config;
+use Fcm::Util qw{run_command};
+use IO::File;
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my @subcommand_names = $self->get_arguments();
+    if (@subcommand_names) {
+        for my $subcommand_name (@subcommand_names) {
+            my $help_string = $self->_get_help_for($subcommand_name);
+            if (!defined($help_string)) {
+                croak(Fcm::CLI::Exception->new({message => sprintf(
+                    "%s: unknown command", $subcommand_name,
+                )}));
+            }
+            print($help_string, "\n");
+        }
+    }
+    else {
+        print($self->_get_help());
+    }
+}
+
+################################################################################
+# Returns the help string for a subcommand matching $subcommand_name
+sub _get_help_for {
+    my ($self, $subcommand_name) = @_;
+    my $subcommand
+        = Fcm::CLI::Config->instance()->get_subcommand_of($subcommand_name);
+    if (!$subcommand) {
+        return;
+    }
+    if ($subcommand->is_vc()) {
+        my $invoker = $subcommand->get_invoker($subcommand_name);
+        local(@ARGV) = '--help';
+        $invoker->invoke();
+        return q{};
+    }
+    my $prog = Fcm::Config->instance()->setting('FCM_COMMAND');
+    # FIXME: can do with using Text::Template or Perl6::Form
+    my $help = sprintf(
+        "%s %s: %s\n",
+        $prog,
+        $subcommand->as_string(),
+        $subcommand->get_synopsis(),
+    );
+    $help .= sprintf(
+        "usage: %s %s %s\n",
+        $prog, $subcommand->get_names()->[0], $subcommand->get_usage(),
+    );
+    if ($subcommand->get_description()) {
+        my @lines = (q{}, split("\n", $subcommand->get_description()), q{});
+        $help .= join(qq{\n  }, @lines) . "\n";
+    }
+    if ($subcommand->get_options()) {
+        $help .= "Valid options:\n";
+        my $max_length_of_name = 0;
+        my @option_names;
+        for my $option ($subcommand->get_options()) {
+            if (length($option->get_name()) > $max_length_of_name) {
+                $max_length_of_name = length($option->get_name());
+            }
+        }
+        for my $option ($subcommand->get_options()) {
+            $help .= sprintf(
+                "  --%s%s%s%s : %s\n",
+                $option->get_name(),
+                q{ } x ($max_length_of_name - length($option->get_name())),
+                (
+                    $option->get_letter()
+                    ? q{ [-} . $option->get_letter() . q{]} : q{     }
+                ),
+                ($option->has_arg() ? q{ arg} : q{ } x 4),
+                $option->get_description(),
+            );
+        }
+    }
+    return $help;
+}
+
+################################################################################
+# Returns the general help string
+sub _get_help {
+    my ($self) = @_;
+    my $release = $self->_get_release();
+
+    # FIXME: can do with using Text::Template or Perl6::Form
+    my $prog = Fcm::Config->instance()->setting('FCM_COMMAND');
+    my $return = sprintf(
+          qq{usage: %s <subcommand> [options] [args]\n}
+        . qq{Flexible configuration management system, release %s.\n}
+        . qq{Type "%s help <subcommand>" for help on a specific subcommand\n}
+        . qq{\n}
+        . qq{Available subcommands:\n}
+        ,
+        $prog, $release, $prog,
+    );
+    for my $subcommand (Fcm::CLI::Config->instance()->get_core_subcommands()) {
+        $return .= sprintf(qq{  %s\n}, $subcommand->as_string());
+    }
+
+    my @lines = run_command(
+        [qw/svn help/], DEVNULL => 1, METHOD => 'qx', ERROR => 'ignore',
+    );
+    if (@lines) {
+        for my $subcommand (Fcm::CLI::Config->instance()->get_vc_subcommands()) {
+            if (defined($subcommand->get_synopsis())) {
+                $return .= sprintf(qq{  %s\n}, $subcommand->as_string());
+            }
+            else {
+                $return .= qq{  <version control system commands, see below>\n};
+            }
+        }
+        $return .= "\n=> svn help\n". join(q{}, @lines);
+    }
+    return $return;
+}
+
+################################################################################
+# Returns the release number of the current program
+sub _get_release {
+    my ($self) = @_;
+    my $release  = Fcm::Config->instance()->setting('FCM_RELEASE');
+    my $rev_file = Fcm::Config->instance()->setting('FCM_REV_FILE');
+    if (-r $rev_file) {
+        my $handle = IO::File->new($rev_file, 'r');
+        if ($handle) {
+            my $rev = $handle->getline();
+            $handle->close();
+            chomp($rev);
+            if ($rev) {
+                $release .= qq{ (r$rev)};
+            }
+        }
+    }
+    return $release;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Invoker::Help
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::Help;
+    $invoker = Fcm::CLI::Invoker::Help->new({
+        command   => $command,
+        options   => \%options,
+        arguments => $arguments,
+    });
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
+methods. An object of this class is used to provide help on the command line
+interface.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item invoke()
+
+Provides help. If a subcommand name is specified in the argument, provides help
+for the specified subcommand. If a subcommand name is not specified, provides
+general CLI help.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::CLI::Exception|Fcm::CLI::Exception>
+
+The invoke() method can croak() with this exception if the specified subcommand
+cannot be identified.
+
+=back
+
+=head1 TO DO
+
+Unit tests.
+
+Separate logic in this module with that of L<Fcm::CLI::Config|Fcm::CLI::Config>.
+
+Decouples help formatter with this invoker.
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Invoker/KeywordPrinter.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Invoker/KeywordPrinter.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Invoker/KeywordPrinter.pm	(revision 1981)
@@ -0,0 +1,124 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Invoker::KeywordPrinter;
+use base qw{Fcm::CLI::Invoker};
+
+use Carp qw{croak};
+use Fcm::CLI::Exception;
+use Fcm::Keyword;
+use Fcm::Keyword::Formatter::Entries;
+use Fcm::Keyword::Formatter::Entry::Location;
+use Fcm::Keyword::Exception;
+use Fcm::Util qw{get_url_of_wc};
+
+################################################################################
+# Invokes the sub-system
+sub invoke {
+    my ($self) = @_;
+    my @targets = $self->get_arguments();
+    if (@targets) {
+        for my $target (@targets) {
+            my $entry_list = Fcm::Keyword::get_location_entries_for($target);
+            my $loc = $target;
+            if (-e $target) {
+                $loc = get_url_of_wc($target);
+                if (!$loc) {
+                    croak(Fcm::Keyword::Exception->new({message => sprintf(
+                        "%s: unrecognised version control resource", $target,
+                    )}));
+                }
+            }
+            my @entry_list = Fcm::Keyword::get_location_entries_for($loc);
+            if (!@entry_list) {
+                croak(Fcm::Keyword::Exception->new({message => sprintf(
+                    "%s: no FCM location keyword found for this target", $target,
+                )}));
+            }
+            my $formatter = Fcm::Keyword::Formatter::Entry::Location->new();
+            for my $entry (
+                sort {$a->get_key() cmp $b->get_key()}
+                grep {!$_->is_implied()}
+                @entry_list
+            ) {
+                print($formatter->format($entry), "\n");
+            }
+        }
+    }
+    else {
+        my $formatter = Fcm::Keyword::Formatter::Entries->new();
+        print($formatter->format(Fcm::Keyword::get_entries()));
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Invoker::KeywordPrinter
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Invoker::KeywordPrinter;
+    $invoker = Fcm::CLI::Invoker::KeywordPrinter->new({
+        command   => $command,
+        options   => \%options,
+        arguments => $arguments,
+    });
+    $invoker->invoke();
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
+methods. An object of this class is used to invoke the location keyword printer.
+
+=head1 METHODS
+
+See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
+
+=over 4
+
+=item invoke()
+
+Invokes the location keyword printer. If a namespace is specified in the
+argument, prints revision keywords and browser mapping templates for the
+specified namespace. If a namespace is not specified, prints all registered
+location keywords.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::Keyword::Exception|Fcm::Keyword::Exception>
+
+The invoke() method can croak() with this exception if there is no matching
+namespace matching that of the specified.
+
+=back
+
+=head1 TO DO
+
+Unit tests.
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>,
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Formatter::Entries|Fcm::Keyword::Formatter::Entries>,
+L<Fcm::Keyword::Formatter::Entry::Location|Fcm::Keyword::Formatter::Entry::Location>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Option.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Option.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Option.pm	(revision 1981)
@@ -0,0 +1,183 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Option;
+
+use constant NO_ARG     => 0;
+use constant SCALAR_ARG => 1;
+use constant ARRAY_ARG  => 2;
+use constant HASH_ARG   => 3;
+use constant ARG_STRING_SUFFIX_FOR => (q{}, q{=s}, q{=s@}, q{=s%});
+
+################################################################################
+# Constructor
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Methods: get_*
+for my $key (
+    # Returns the delimiter of this option, if it is an array
+    'delimiter',
+    # Returns the description of this option
+    'description',
+    # Returns the (long) name of this option
+    'name',
+) {
+    no strict qw{refs};
+    my $getter = "get_$key";
+    *$getter = sub {
+        my ($self) = @_;
+        return $self->{$key};
+    }
+}
+
+################################################################################
+# Returns the letter of this option
+sub get_letter {
+    my ($self) = @_;
+    if (defined($self->{letter})) {
+        return substr($self->{letter}, 0, 1);
+    }
+    else {
+        return;
+    }
+}
+
+################################################################################
+# Returns whether the current option has no, scalar, array or hash arguments
+sub has_arg {
+    my ($self) = @_;
+    return (defined($self->{has_arg}) ? $self->{has_arg} : $self->NO_ARG);
+}
+
+################################################################################
+# Returns true if this option is associated with help
+sub is_help {
+    my ($self) = @_;
+    return $self->{is_help};
+}
+
+################################################################################
+# Returns an option string/reference pair for Getopt::Long::GetOptions
+sub get_arg_for_getopt_long {
+    my ($self) = @_;
+    my $option_string
+        = $self->get_name()
+          . ($self->get_letter() ? q{|} . $self->get_letter() : q{})
+          . (ARG_STRING_SUFFIX_FOR)[$self->has_arg()]
+          ;
+    return $option_string;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Option
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Option;
+    $option = Fcm::CLI::Option->new({
+        name        => 'name',
+        letter      => 'n',
+        has_arg     => Fcm::CLI::Option->SCALAR_ARG,
+        is_help     => 1,
+        description => 'an example option',
+    });
+
+    # time passes ...
+    use Getopt::Long qw{GetOptions};
+    $success = GetOptions(
+        \%hash,
+        $option->get_arg_for_getopt_long(), # ('name|n=s')
+        # and other options ...
+    );
+    $option_value = $option->get_value();
+
+=head1 DESCRIPTION
+
+An object of this class represents a CLI option.
+
+=head1 METHODS
+
+=over 4
+
+=item new($args_ref)
+
+Constructor.
+
+=item get_arg_for_getopt_long()
+
+Returns an option string for this option that is suitable for use as arguments
+to L<Getopt::Long|Getopt::Long>.
+
+=item get_description()
+
+Returns a description of this option.
+
+=item get_delimiter()
+
+Returns the delimiter of this option. This is only relevant if has_arg() is
+equal to C<ARRAY_ARG>. If set, the argument for this option should be re-grouped
+using this delimiter.
+
+=item get_name()
+
+Returns the (long) name of this option.
+
+=item get_letter()
+
+Returns the option letter of this option.
+
+=item has_arg()
+
+Returns whether this option has no, scalar, array or hash arguments. See
+L</CONSTANTS> for detail.
+
+=item is_help()
+
+Returns true if this option is associated with help.
+
+=back
+
+=head1 CONSTANTS
+
+=over 4
+
+=item NO_ARG
+
+An option has no argument. (Default)
+
+=item SCALAR_ARG
+
+An option has a single scalar argument.
+
+=item ARRAY_ARG
+
+An option has multiple arguments, which can be placed in an array.
+
+=item HASH_ARG
+
+An option has multiple arguments, which can be placed in an hash.
+
+=back
+
+=head1 SEE ALSO
+
+L<Getopt::Long|Getopt::Long>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/Subcommand.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/Subcommand.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/Subcommand.pm	(revision 1981)
@@ -0,0 +1,245 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::CLI::Subcommand;
+
+use Carp qw{croak};
+use Fcm::CLI::Exception;
+use Fcm::Util::ClassLoader;
+
+################################################################################
+# Constructor
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Methods: get_*
+for my $key (
+    # Returns the long description of this subcommand
+    'description',
+    # Returns the class of the invoker of this subcommand
+    'invoker_class',
+    # Returns the extra config to be given to the invoker of this subcommand
+    'invoker_config',
+    # Returns the names of this subcommand
+    'names',
+    # Returns the options of this subcommand
+    'options',
+    # Returns the synopsis of this subcommand
+    'synopsis',
+    # Returns the usage of this subcommand
+    'usage',
+) {
+    no strict qw{refs};
+    my $getter = "get_$key";
+    *$getter = sub {
+        my ($self) = @_;
+        if (defined($self->{$key})) {
+            if (ref($self->{$key}) eq 'ARRAY') {
+                return (wantarray() ? @{$self->{$key}} : $self->{$key});
+            }
+            else {
+                return $self->{$key};
+            }
+        }
+        else {
+            return;
+        }
+    }
+}
+
+################################################################################
+# Returns true if this subcommand represents a command in the CM sub-system
+sub is_vc {
+    my ($self) = @_;
+    return $self->{is_vc};
+}
+
+################################################################################
+# Returns true if $string matches a name of this subcommand
+sub has_a_name {
+    my ($self, $string) = @_;
+    if ($self->get_names() && ref($self->get_names()) eq 'ARRAY') {
+        my %name_of = map {$_, 1} @{$self->get_names()};
+        return exists($name_of{$string});
+    }
+    else {
+        return;
+    }
+}
+
+################################################################################
+# Invokes this subcommand based on current @ARGV
+sub get_invoker {
+    my ($self, $command) = @_;
+    my %options = ();
+    if (($self->get_options())) {
+        my $problem = q{};
+        local($SIG{__WARN__}) = sub {
+            ($problem) = @_;
+        };
+        my $success = GetOptions(
+            \%options,
+            (map {$_->get_arg_for_getopt_long()} ($self->get_options())),
+        );
+        if (!$success) {
+            croak(Fcm::CLI::Exception->new({message => sprintf(
+                "%s: option parse failed: %s", $command, $problem,
+            )}));
+        }
+    }
+    my $invoker_class
+        = $self->get_invoker_class() ? $self->get_invoker_class()
+        :                              'Fcm::CLI::Invoker'
+        ;
+    Fcm::Util::ClassLoader::load($invoker_class);
+    my $invoker = $invoker_class->new({
+        command   => $command,
+        options   => \%options,
+        arguments => [@ARGV],
+    });
+    return $invoker;
+}
+
+################################################################################
+# Returns a simple string representation of this subcommand
+sub as_string {
+    my ($self) = @_;
+    # FIXME: can do with using Text::Template or Perl6::Form
+    if (
+           $self->get_names()
+        && ref($self->get_names()) eq 'ARRAY'
+        && @{$self->get_names()}
+    ) {
+        my @names = $self->get_names();
+        my $return = $names[0];
+        for my $i (1 .. $#names) {
+            if ($names[$i]) {
+                $return
+                    .= $i == 1       ? q{ (} . $names[$i]
+                    :                  q{, } . $names[$i]
+                    ;
+            }
+            if ($i == $#names) {
+                $return .= q{)};
+            }
+        }
+        return $return;
+    }
+    else {
+        return q{};
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::CLI::Subcommand
+
+=head1 SYNOPSIS
+
+    use Fcm::CLI::Subcommand;
+    $subcommand = Fcm::CLI::Subcommand->new({
+        names          => ['build', 'bld'],
+        options        => [
+            Fcm::CLI::Option->new(
+                # ... some arguments ...
+            ),
+            # ... more options
+        ],
+        synopsis       => 'invokes the build system',
+        description    => $description,
+        usage          => '[OPTIONS] [CONFIG]',
+        invoker_class  => $invoker_class,
+        invoker_config => {
+            option1 => $option1,
+            # ... more options
+        },
+    });
+    $boolean = $subcommand->has_a_name($string);
+    $invoker_class = $subcommand->get_invoker_class();
+
+=head1 DESCRIPTION
+
+An object of this class is used to store the configuration of a subcommand of
+the FCM CLI.
+
+=head1 METHODS
+
+=over 4
+
+=item new($args_ref)
+
+Constructor.
+
+=item get_description()
+
+Returns the long description of this subcommand.
+
+=item get_invoker_class()
+
+Returns the invoker class of this subcommand, which should be a sub-class of
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>.
+
+=item get_invoker_cconfig()
+
+Returns a reference to a hash containing the extra configuration to be given to
+the constructor of the invoker of this subcommand.
+
+=item get_names()
+
+Returns an array containing the names of this subcommand.
+
+=item get_options()
+
+Returns an array containing the options of this subcommand. Each element of
+the array should be a L<Fcm::CLI::Option|Fcm::CLI::Option> object.
+
+=item get_synopsis()
+
+Returns a short synopsis of this subcommand.
+
+=item get_usage()
+
+Returns a short usage statement of this subcommand.
+
+=item is_vc()
+
+Returns true if this subcommand represents commands in the underlying VC system.
+
+=item has_a_name($string)
+
+Returns true if a name in C<$self-E<gt>get_names()> matches $string.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::CLI::Exception|Fcm::CLI::Exception>
+
+The invoke() method may croak() with this exception.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
+L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
+L<Fcm::CLI::Option|Fcm::CLI::Option>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/fcm-add.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-add.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-add.pod	(revision 1981)
@@ -0,0 +1,22 @@
+=head1 NAME
+
+fcm add
+
+=head1 SYNOPSIS
+
+    fcm add [options] [args]
+
+=head1 OPTIONS
+
+=over 4
+
+=item -c [--check]
+
+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: vendors/fcm/1-5/lib/Fcm/CLI/fcm-branch.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-branch.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-branch.pod	(revision 1981)
@@ -0,0 +1,173 @@
+=head1 NAME
+
+fcm branch (br)
+
+=head1 SYNOPSIS
+
+Create, delete or display information of a branch, or list branches in a
+project.
+
+    fcm branch [--info] [OPTIONS] [TARGET]
+    fcm branch --delete [OPTIONS] [TARGET]
+    fcm branch --create [OPTIONS] [SOURCE]
+    fcm branch --list   [OPTIONS] [SOURCE]
+
+=head1 ARGUMENTS
+
+TARGET (and SOURCE) can be an URL or a Subversion working copy. Otherwise,
+the current working directory must be a working copy. For --info and
+--delete, the specified URL (or the URL of the working copy) must be a URL
+under a valid branch in a standard FCM project. For --create and --list, it
+must be a URL under a standard FCM project.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --info or -i
+
+Display information about a branch. This is the default option if --create,
+--delete and --list are not specified.
+
+=item --delete or -d
+
+Delete a branch.
+
+=item --create or -c
+
+Create a new branch from SOURCE. The --name option must be used to specify a
+short name for the new branch.
+
+=item --list or -l
+
+List all the branches owned by the current user in SOURCE. If the --user option
+is specified with a list of users, list all the branches owned by these users
+instead of the current user.
+
+=back
+
+Valid options with --info and --delete:
+
+=over 4
+
+=item -v [--verbose]
+
+Print extra information.
+
+=item -a [--show-all]
+
+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
+
+Valid options with --delete and --create:
+
+=over 4
+
+=item --non-interactive
+
+Do no interactive prompting. This option implies --svn-non-interactive.
+
+=item --password arg
+
+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
+
+Valid options with --create and --list:
+
+=over 4
+
+=item -r [--revision] arg
+
+Specify the operative revision of the SOURCE for creating the branch.
+
+=back
+
+Valid options with --create:
+
+=over 4
+
+=item --branch-of-branch
+
+If this option is specified and the SOURCE is a branch, it will create a new
+branch from the SOURCE branch. Otherwise, the branch is created from the trunk.
+
+=item -k [--ticket] arg
+
+Specify one (or more) Trac ticket. If specified, the command will add to the
+commit log the line "Relates to ticket #<ticket>". Multiple tickets can be set
+by specifying this option multiple times, or by specifying the tickets in a
+comma-separated list.
+
+=item -n [--name] arg
+
+Specify a short name for the branch, which should contain only characters in the
+set [A-Za-z0-9_-.].
+
+=item --rev-flag arg
+
+Specify a flag for determining the prefix of the branch name. The flag can be
+the the string "NORMAL", "NUMBER" or "NONE".  "NORMAL" is the default behaviour,
+in which the branch name will be prefixed with a Subversion revision number if
+the revision is not associated with a registered FCM revision keyword. If the
+revision is registered with a FCM revision keyword, the keyword will be used in
+place of the number. If "NUMBER" is specified, the branch name will always be
+prefixed with a Subversion revision number. If "NONE" is specified, the branch
+name will not be prefixed by a revision number or keyword.
+
+=item -t [--type] arg
+
+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
+
+Valid options with --list:
+
+=over 4
+
+=item -a [--show-all]
+
+Print all branches.
+
+=item -u [--user] arg
+
+Specify a colon-separated list of users. List branches owned by these users
+instead of the current user.
+
+=item -v [--verbose]
+
+Print Subversion URL instead of FCM URL keywords.
+
+=back
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/fcm-commit.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-commit.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-commit.pod	(revision 1981)
@@ -0,0 +1,31 @@
+=head1 NAME
+
+fcm commit (ci)
+
+=head1 SYNOPSIS
+
+    fcm commit [OPTIONS] [PATH ...]
+
+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.
+
+=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 arg
+
+Specify a password ARG.
+
+=back
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/fcm-conflicts.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-conflicts.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-conflicts.pod	(revision 1981)
@@ -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: vendors/fcm/1-5/lib/Fcm/CLI/fcm-delete.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-delete.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-delete.pod	(revision 1981)
@@ -0,0 +1,22 @@
+=head1 NAME
+
+fcm delete (del, remove, rm)
+
+=head1 SYNOPSIS
+
+    fcm delete [options] [args]
+
+=head1 OPTIONS
+
+=over 4
+
+=item -c [--check]
+
+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: vendors/fcm/1-5/lib/Fcm/CLI/fcm-diff.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-diff.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-diff.pod	(revision 1981)
@@ -0,0 +1,79 @@
+=head1 NAME
+
+fcm diff (di)
+
+=head1 SYNOPSIS
+
+    1. fcm diff --branch [OPTIONS] [TARGET]
+    2. fcm diff [OPTIONS] [ARGS]
+
+=over 4
+
+=item 1.
+
+Show differences relative to the base of the target branch, i.e. the changes
+available for merging from the target branch into its parent. If TARGET is
+specified, it must either be a URL or a working copy.  Otherwise, the target is
+the the current directory which must be a working copy. The target URL must be a
+branch in a standard FCM project.
+
+=item 2.
+
+See description of "L<svn|svn> diff" below.
+
+=back
+
+=head1 OPTIONS
+
+Valid options with --branch:
+
+=over 4
+
+=item --diff-cmd arg
+
+As described below in the help for "L<svn|svn> diff".
+
+=item -g [--graphical]
+
+As described below.
+
+=item --summarise
+
+As described below
+
+=item --summarize
+
+As described below in the help for "L<svn|svn> diff".
+
+=item -t [--trac]
+
+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 -x [--extensions] arg
+
+As described below in the help for "L<svn|svn> diff".
+
+=back
+
+Other options:
+
+=over 4
+
+=item -g [--graphical]
+
+Use a graphical diff tool to display the differences. This option should not be
+used in combination with --diff-cmd.
+
+=item --summarise
+
+Same as --summarize as described below.
+
+=back
+
+For other options, see output of "L<svn|svn> help diff".
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/fcm-merge.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-merge.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-merge.pod	(revision 1981)
@@ -0,0 +1,71 @@
+=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 option. If a single revision is
+specified, the merge delta is N:(N - 1). Otherwise, the merge delta is M:N,
+where M > N. Note that you do not have to specify a SOURCE for a reverse merge,
+because the SOURCE should always be the branch your working copy is pointing to.
+  
+=back
+
+The command provide a commit log message template following the merge.
+
+=head1 OPTIONS
+
+=over 4
+
+=item --dry-run
+
+Try operation but make no changes.
+
+=item --non-interactive
+
+Do no interactive prompting.
+
+=item -r [--revision] arg
+
+Specify a (range of) revision number(s).
+
+=item --verbose
+
+Print extra information.
+
+=back
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/fcm-mkpatch.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-mkpatch.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-mkpatch.pod	(revision 1981)
@@ -0,0 +1,60 @@
+=head1 NAME
+
+fcm mkpatch
+
+=head1 SYNOPSIS
+
+mkpatch: Create patches from specified revisions of a URL
+
+    fcm mkpatch [OPTIONS] URL [OUTDIR]
+
+Create patches from specified revisions of the specified URL. If OUTDIR is
+specified, the output is sent to OUTDIR. Otherwise, the output will be sent to a
+default location in the current directory ($PWD/fcm-mkpatch-out). The output
+directory will contain the patch for each revision as well as a script for
+importing the patch.
+
+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 -r [--revision] 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: vendors/fcm/1-5/lib/Fcm/CLI/fcm-switch.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-switch.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-switch.pod	(revision 1981)
@@ -0,0 +1,14 @@
+=head1 NAME
+
+fcm switch (sw)
+
+=head1 SYNOPSIS
+
+    1. switch URL [PATH]
+    2. switch --relocate FROM TO [PATH...]
+
+Note: if --relocate is not specified, "fcm switch" will only support the options
+--non-interactive, -r [--revision] and -q [--quiet]. For detail, see the output
+of "L<svn|svn> help switch".
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CLI/fcm-update.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CLI/fcm-update.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CLI/fcm-update.pod	(revision 1981)
@@ -0,0 +1,14 @@
+=head1 NAME
+
+fcm update (up)
+
+=head1 SYNOPSIS
+
+Bring changes from the repository into the working copy.
+
+    usage: update [PATH...]
+
+Note: "fcm update" only supports --non-interactive, -r [--revision] arg and -q
+[--quiet]. For detail, see the output of "L<svn|svn> help update".
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CfgFile.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CfgFile.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CfgFile.pm	(revision 1981)
@@ -0,0 +1,679 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CfgFile
+#
+# DESCRIPTION
+#   This class is used for reading and writing FCM config files. A FCM config
+#   file is a line-based text file that provides information on how to perform
+#   a particular task using the FCM system.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CfgFile;
+@ISA = qw(Fcm::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Basename;
+use File::Path;
+use File::Spec;
+
+# FCM component modules
+use Fcm::Base;
+use Fcm::CfgLine;
+use Fcm::Config;
+use Fcm::Keyword;
+use Fcm::Util;
+
+# List of property methods for this class
+my @scalar_properties = (
+  'actual_src', # actual source of configuration file
+  'lines',      # list of lines, Fcm::CfgLine objects
+  'pegrev',     # peg revision of configuration file
+  'src',        # source of configuration file
+  'type',       # type of configuration file
+  'version',    # version of configuration file
+);
+
+# Local module variables
+my $expand_type   = 'bld|ext'; # config file type that needs variable expansions
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::CfgFile->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::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 = Fcm::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 ();
+#
+# DESCRIPTION
+#   This method reads the current configuration file. It returns the number of
+#   lines read from the config file, or "undef" if it fails. The result is
+#   placed in the LINES array of the current instance, and can be accessed via
+#   the "lines" method.
+# ------------------------------------------------------------------------------
+
+sub read_cfg {
+  my $self = shift;
+
+  my @lines = $self->_get_cfg_lines;
+
+  # List of CFG types that need INC declarations expansion
+  my %exp_inc    = ();
+  for (split (/$Fcm::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 (/$Fcm::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 (exists $exp_inc{uc ($self->type)} and
+        uc ($start ? $start->label : $label) eq $self->cfglabel ('INC') and
+        not defined $cont) {
+      # Current configuration file requires expansion of INC declarations
+      # The start/current line is an INC declaration
+      # The current line is not a continuation or is the end of the continuation
+
+      # Get lines from an "include" configuration file
+      my $src = ($start ? $start->value : $value);
+      $src   .= '@' . $self->pegrev if $here and $self->pegrev;
+
+      if ($src) {
+        # Invoke a new instance to read the source
+        my $cfg = Fcm::CfgFile->new (
+          SRC => expand_tilde ($src), TYPE => $self->type,
+        );
+
+        $cfg->read_cfg;
+
+        # Add lines to the lines array in the current configuration file
+        $comment = 'INC ' . $src . ' ';
+        push @{$self->lines}, Fcm::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}, Fcm::CfgLine->new (
+          comment => $comment . '# End',
+          src     => $self->actual_src,
+        );
+
+      } else {
+        push @{$self->lines}, Fcm::CfgLine->new (
+          number  => $line_num,
+          src     => $self->actual_src,
+          warning => 'empty INC declaration.'
+        );
+      }
+
+    } else {
+      # Push label:value pair into lines array
+      push @{$self->lines}, Fcm::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 /$Fcm::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 $old_select = select;
+
+    # Open file if necessary
+    if ($file) {
+      # Make sure the host directory exists and is writable
+      my $dirname = dirname $file;
+      if (not -d $dirname) {
+        print 'Make directory: ', $dirname, "\n" if $self->verbose;
+        mkpath $dirname;
+      }
+      croak $dirname, ': cannot write to config file directory, abort'
+        unless -d $dirname and -w $dirname;
+
+      if (-f $file and not $force) {
+        if (-r $file) {
+          # Read old config file to see if content has changed
+          open IN, '<', $file or croak $file, ': cannot open (', $!, '), abort';
+          my $in_lines = '';
+          while (my $line = <IN>) {
+            $in_lines .= $line;
+          }
+          close IN or croak $file, ': cannot close (', $!, '), abort';
+
+          # 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 (-w $file) {
+          if ($self->type) {
+            # Existing config file writable, rename it using its time stamp
+            my $mtime = (stat $file)[9];
+            my ($sec, $min, $hour, $mday, $mon, $year) = (gmtime $mtime)[0 .. 5];
+            my $timestamp = sprintf '%4d%2.2d%2.2d_%2.2d%2.2d%2.2d_',
+                            $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
+            my $oldfile   = File::Spec->catfile (
+              $dirname, $timestamp . basename ($file)
+            );
+            rename $file, $oldfile;
+            print 'Rename existing ', lc ($self->type), ' cfg: ',
+                  $oldfile, "\n" if $self->verbose > 1;
+          }
+
+        } else {
+          # Existing config file not writable, throw an error
+          croak $file, ': config file not writable, abort';
+        }
+      }
+
+      # Open file and select file handle
+      open OUT, '>', $file
+        or croak $file, ': cannot open config file (', $!, '), abort';
+      select OUT;
+    }
+
+    # Print output
+    print $out;
+
+    # Close file if necessary
+    if ($file) {
+      select $old_select;
+      close OUT or croak $file, ': cannot close config file (', $!, '), abort';
+
+      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 ();
+#
+# DESCRIPTION
+#   This internal method reads from a configuration file residing in a
+#   Subversion repository or in the normal file system.
+# ------------------------------------------------------------------------------
+
+sub _get_cfg_lines {
+  my $self  = shift;
+  my @lines = ();
+
+  my $verbose = $self->verbose;
+
+  my ($src) = $self->src();
+  if ($src =~ qr{\A([A-Za-z][\w\+-\.]*):}xms) { # is a URI
+    $src = Fcm::Keyword::expand($src);
+    # Config file resides in a SVN repository
+    # --------------------------------------------------------------------------
+    # Set URL source and version
+    my $rev = 'HEAD';
+
+    # Extract version from source if it exists
+    if ($src =~ s{\@ ([^\@]+) \z}{}xms) {
+      $rev = $1;
+    }
+
+    $src = Fcm::Util::tidy_url($src);
+
+    # Check whether URL is a config file
+    my $rc;
+    my @cmd = (qw/svn cat/, $src . '@' . $rev);
+    @lines = &run_command (
+      \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
+    );
+
+    # Error in "svn cat" command
+    if ($rc) {
+      # See whether specified config file is a known type
+      my %cfgname = %{ $self->setting ('CFG_NAME') };
+      my $key     = uc $self->type;
+      my $file    = exists $cfgname{$key} ? $cfgname{$key} : '';
+
+      # If config file is a known type, specified URL may be a directory
+      if ($file) {
+        # Check whether a config file with a default name exists in the URL
+        my $path = $src . '/' . $file;
+        my @cmd  = (qw/svn cat/, $path . '@' . $rev);
+
+        @lines = &run_command (
+          \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
+        );
+
+        # Check whether a config file with a default name exists under the "cfg"
+        # sub-directory of the URL
+        if ($rc) {
+          my $cfgdir = $self->setting (qw/DIR CFG/);
+          $path   = $src . '/' . $cfgdir . '/' . $file;
+          my @cmd = (qw/svn cat/, $path . '@' . $rev);
+
+          @lines  = &run_command (
+            \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
+          );
+        }
+
+        $src = $path unless $rc;
+      }
+    }
+
+    if ($rc) {
+      # Error in "svn cat"
+      croak 'Unable to locate config file from "', $self->src, '", abort';
+
+    } else {
+      # Print diagnostic, if necessary
+      if ($verbose and $self->type and $self->type =~ /$expand_type/) {
+        print 'Config file (', $self->type, '): ', $src;
+        print '@', $rev if $rev;
+        print "\n";
+      }
+    }
+
+    # Record the actual source location
+    $self->pegrev ($rev);
+    $self->actual_src ($src);
+
+  } else {
+    # Config file resides in the normal file system
+    # --------------------------------------------------------------------------
+    my $src = $self->src;
+
+    if (-d $src) { # Source is a directory
+      croak 'Config file "', $src, '" is a directory, abort' if not $self->type;
+
+      # Get name of the config file by looking at the type
+      my %cfgname = %{ $self->setting ('CFG_NAME') };
+      my $key     = uc $self->type;
+      my $file    = exists $cfgname{$key} ? $cfgname{$key} : '';
+
+      if ($file) {
+        my $cfgdir = $self->setting (qw/DIR CFG/);
+
+        # Check whether a config file with a default name exists in the
+        # specified path, then check whether a config file with a default name
+        # exists under the "cfg" sub-directory of the specified path
+        if (-f File::Spec->catfile ($self->src, $file)) {
+          $src = File::Spec->catfile ($self->src, $file);
+
+        } elsif (-f File::Spec->catfile ($self->src, $cfgdir, $file)) {
+          $src = File::Spec->catfile ($self->src, $cfgdir, $file);
+
+        } else {
+          croak 'Unable to locate config file from "', $self->src, '", abort';
+        }
+
+      } else {
+        croak 'Unknown config file type "', $self->type, '", abort';
+      }
+    }
+
+    if (-r $src) {
+      open FILE, '<', $src;
+      print 'Config file (', $self->type, '): ', $src, "\n"
+        if $verbose and $self->type and $self->type =~ /$expand_type/;
+
+      @lines = readline 'FILE';
+      close FILE;
+
+    } else {
+      croak 'Unable to read config file "', $src, '", abort';
+    }
+
+    # Record the actual source location
+    $self->actual_src ($src);
+  }
+
+  return @lines;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_expand_variable ($string, $env[, \%recursive]);
+#
+# DESCRIPTION
+#   This internal method expands variables in $string. If $env is true, it
+#   expands environment variables. Otherwise, it expands local variables. If
+#   %recursive is set, 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.
+# ------------------------------------------------------------------------------
+
+sub _expand_variable {
+  my ($self, $string, $env, $recursive) = @_;
+
+  # 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 $cyclic = 0;
+        if ($recursive) {
+          if (exists $recursive->{$var_label}) {
+            $cyclic = 1;
+
+          } else {
+            $recursive->{$var_label} = 1;
+          }
+
+        } else {
+          $recursive = {$var_label => 1};
+        }
+
+        if ($cyclic) {
+          $warning .= ', ' if $warning;
+          $warning .= $match . ': cyclic dependency, variable not expanded';
+          $ret .= $variable;
+
+        } else {
+          my ($r, $w) = $self->_expand_variable ($variable, $env, $recursive);
+          $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: vendors/fcm/1-5/lib/Fcm/CfgLine.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CfgLine.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CfgLine.pm	(revision 1981)
@@ -0,0 +1,333 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CfgLine
+#
+# DESCRIPTION
+#   This class is used for grouping the settings in each line of a FCM
+#   configuration file.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CfgLine;
+@ISA = qw(Fcm::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use File::Basename;
+
+# In-house modules
+use Fcm::Base;
+use Fcm::Config;
+use Fcm::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 = Fcm::CfgLine->comment_block (@comment);
+#
+# DESCRIPTION
+#   This method returns a list of Fcm::CfgLine objects representing a comment
+#   block with the comment string @comment.
+# ------------------------------------------------------------------------------
+
+sub comment_block {
+  my @return = (
+    Fcm::CfgLine->new (comment => $COMMENT_RULER),
+    (map {Fcm::CfgLine->new (comment => $_)} @_),
+    Fcm::CfgLine->new (comment => $COMMENT_RULER),
+    Fcm::CfgLine->new (),
+  );
+
+  return @return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::CfgLine->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::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 = Fcm::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 (/$Fcm::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 ($Fcm::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 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: vendors/fcm/1-5/lib/Fcm/Cm.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Cm.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Cm.pm	(revision 1981)
@@ -0,0 +1,2721 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Cm
+#
+# DESCRIPTION
+#   This module contains the FCM code management functionalities and wrappers
+#   to Subversion commands.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Cm;
+use base qw{Exporter};
+
+our @EXPORT_OK = qw(cli cm_check_missing cm_check_unknown cm_switch cm_update);
+
+use Cwd            qw{cwd};
+use Getopt::Long   qw{GetOptions :config bundling};
+use Fcm::CLI::Exception;
+use Fcm::Config;
+use Fcm::CmBranch;
+use Fcm::CmUrl;
+use Fcm::Keyword;
+use Fcm::Util      qw{
+    get_url_of_wc
+    get_url_peg_of_wc
+    get_wct
+    is_url
+    is_wc
+    run_command
+    tidy_url
+};
+use File::Basename qw{basename dirname};
+use File::Path     qw{mkpath rmtree};
+use File::Spec;
+use File::Temp     qw{tempfile};
+use Pod::Usage     qw{pod2usage};
+
+# ------------------------------------------------------------------------------
+
+# 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       => "Performing merge ...\n",
+    MERGE_CF    => "About to merge in changes from %s compared with %s\n",
+    MERGE_CI    => "The following is added to the commit message file:\n%s",
+    MERGE_DRY   => "This merge will result in the following change:\n",
+    MERGE_REVS  => "Merge(s) available 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      => "Status of the target working copy(ies):\n%s",
+);
+
+# 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 usage",
+    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_EXIST            => "%s: working copy already exists.\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{Are you sure you want to continue?},
+    MERGE             => qq{Would you like to go ahead with the merge?},
+    MERGE_REV         => qq{Please enter the revision you wish to merge from},
+    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 };
+
+# List of exception handlers [$class, CODE->($function, $e)]
+our @CLI_EXCEPTION_HANDLERS = (
+    ['Fcm::CLI::Exception', \&_cli_e_handler_of_cli_exception],
+    ['Fcm::Cm::Exception' , \&_cli_e_handler_of_cm_exception],
+    ['Fcm::Cm::Abort'     , \&_cli_e_handler_of_cm_abort],
+);
+
+# Event handlers
+our %CLI_HANDLER_OF = (
+    'WC_STATUS'      => \&_cli_handler_of_wc_status,
+    'WC_STATUS_PATH' => \&_cli_handler_of_wc_status_path,
+);
+
+# Handlers of sub-commands
+our %CLI_IMPL_OF = (
+    'add'       => \&_cli_command_add,
+    'branch'    => \&cm_branch,
+    'commit'    => \&cm_commit,
+    'conflicts' => \&cm_conflicts,
+    'checkout'  => \&_cli_command_checkout,
+    'delete'    => \&_cli_command_delete,
+    'diff'      => \&cm_diff,
+    'merge'     => \&cm_merge,
+    'mkpatch'   => \&cm_mkpatch,
+    'switch'    => \&_cli_command_switch,
+    'update'    => \&_cli_command_update,
+);
+
+# List of overridden subcommands that need to display "svn help"
+our %CLI_MORE_HELP_FOR = map {($_, 1)} qw{add diff delete switch update};
+
+# The preferred name of subcommand aliases
+our %CLI_PREFERRED_NAME_OF = (
+    'ann'      => 'blame',
+    'annotate' => 'blame',
+    'br'       => 'branch',
+    'ci'       => 'commit',
+    'cf'       => 'conflicts',
+    'co'       => 'checkout',
+    'cp'       => 'copy',
+    'del'      => 'delete',
+    'di'       => 'diff',
+    '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',
+    'remove'   => 'delete',
+    'ren'      => 'move',
+    'rename'   => 'move',
+    'rm'       => 'delete',
+    'sw'       => 'switch',
+    'up'       => 'update',
+);
+
+# List of subcommands that accept URL inputs
+our %CLI_SUBCOMMAND_URL = map {($_, 1)} qw{
+    blame
+    branch
+    cat
+    checkout
+    copy
+    delete
+    diff
+    export
+    import
+    info
+    list
+    lock
+    log
+    merge
+    mkdir
+    mkpatch
+    move
+    propdel
+    propedit
+    propget
+    proplist
+    propset
+    switch
+    unlock
+};
+
+# List of subcommands that accept revision inputs
+our %CLI_SUBCOMMAND_REV = map {($_, 1)} qw{
+    blame
+    branch
+    cat
+    checkout
+    copy
+    diff
+    export
+    info
+    list
+    log
+    merge
+    mkpatch
+    move
+    propdel
+    propedit
+    propget
+    proplist
+    propset
+    switch
+};
+
+# 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 = (
+    MISSING     => sub {substr($_[0], 0, 1) eq '!'},
+    MODIFIED    => sub {substr($_[0], 0, 6) =~ qr{\S}xms},
+    OUT_OF_DATE => sub {substr($_[0], 7, 1) eq '*'},
+    UNKNOWN     => sub {substr($_[0], 0, 1) eq '?'},
+);
+
+# ------------------------------------------------------------------------------
+# Entry function for the FCM code management CLI. Calls the relevant FCM code
+# management function or SVN command based on $function.
+sub cli {
+    my ($function, @args) = @_;
+    if (exists($CLI_PREFERRED_NAME_OF{$function})) {
+        $function = $CLI_PREFERRED_NAME_OF{$function};
+    }
+    if (grep {$_ eq '-h' || $_ eq '--help'} @args) {
+        return _cli_help($function, 'NOEXIT');
+    }
+    if (exists($CLI_SUBCOMMAND_URL{$function})) {
+        _cli_keyword_expand_url(\@args);
+    }
+    if (exists($CLI_SUBCOMMAND_REV{$function})) {
+        _cli_keyword_expand_rev(\@args);
+    }
+    if (exists($CLI_IMPL_OF{$function})) {
+        eval {
+            local(@ARGV) = @args;
+            return $CLI_IMPL_OF{$function}->(@args);
+        };
+        if ($@) {
+            my $e = $@;
+            for (@CLI_EXCEPTION_HANDLERS) {
+                my ($class, $handler) = @{$_};
+                if ($class->caught($e)) {
+                    return $handler->($function, $e);
+                }
+            }
+            die($e);
+        }
+    }
+    else {
+        return _svn($function, @args);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_branch ();
+#
+# DESCRIPTION
+#   This is a FCM command to check information, create or delete a branch in
+#   a Subversion repository.
+# ------------------------------------------------------------------------------
+
+sub cm_branch {
+  # Process command line options
+  # ----------------------------------------------------------------------------
+  my (
+    $info,
+    $delete,
+    $create,
+    $list,
+    $branch_of_branch,
+    $name,
+    $non_interactive,
+    $password,
+    $rev,
+    $rev_flag,
+    $show_all,
+    $show_children,
+    $show_other,
+    $show_siblings,
+    $svn_non_interactive,
+    @tickets,
+    $type,
+    @userlist,
+    $verbose,
+  );
+  my $rc = GetOptions(
+    'info|i'              => \$info,
+    'delete|d'            => \$delete,
+    'create|c'            => \$create,
+    'list|l'              => \$list,
+    'branch-of-branch'    => \$branch_of_branch,
+    'name|n=s'            => \$name,
+    'non-interactive'     => \$non_interactive,
+    'password=s'          => \$password,
+    'revision|r=s'        => \$rev,
+    'rev-flag=s'          => \$rev_flag,
+    'show-all|a'          => \$show_all,
+    'show-children'       => \$show_children,
+    'show-other'          => \$show_other,
+    'show-siblings'       => \$show_siblings,
+    'svn-non-interactive' => \$svn_non_interactive,
+    'ticket|k=s'          => \@tickets,
+    'type|t=s'            => \$type,
+    'user|u=s'            => \@userlist,
+    'verbose|v'           => \$verbose,
+  );
+  if (!$rc) {
+    _cli_err();
+  }
+
+  my $num_options = 0;
+  $num_options++ if defined $info;
+  $num_options++ if defined $delete;
+  $num_options++ if defined $create;
+  $num_options++ if defined $list;
+  if ($num_options > 1) {
+    _cli_err();
+  }
+
+  # Get URL of repository or branch
+  # ----------------------------------------------------------------------------
+  my $url;
+  if ($ARGV[0]) {
+    $url = Fcm::CmUrl->new (URL => $ARGV[0]);
+
+    if (not $url->is_url) {
+      # An argument is specified and is not a URL
+      # Assume that it is a path with a working copy
+      if (&is_wc ($ARGV[0])) {
+        $url = Fcm::CmUrl->new (URL => &get_url_of_wc ($ARGV[0]));
+
+      } else {
+        return _cm_err(Fcm::Cm::Exception->INVALID_WC, $ARGV[0]);
+      }
+    }
+
+  } else {
+    # An argument is not specified
+    # Assume that the current directory is a working copy
+    if (&is_wc ()) {
+      $url = Fcm::CmUrl->new (URL => &get_url_of_wc ());
+
+    } else {
+      return _cm_err(Fcm::Cm::Exception->INVALID_TARGET, '.');
+    }
+  }
+
+  # Ensure $url->url_peg is a URL of a standard FCM project
+  if (!$url->project_url()) {
+    return _cm_err(Fcm::Cm::Exception->INVALID_PROJECT, $url->url_peg());
+  }
+
+  if ($create) {
+    # The --create option is specified, create a branch
+    # --------------------------------------------------------------------------
+
+    # Check branch type flags
+    if ($type) {
+      $type = uc ($type);
+
+      if ($type =~ /^(USER|SHARE)$/) {
+        $type = 'DEV' . $Fcm::Config::DELIMITER . $1;
+
+      } elsif ($type =~ /^(CONFIG|REL)$/) {
+        $type = 'PKG' . $Fcm::Config::DELIMITER . $1;
+
+      } elsif ($type =~ /^(DEV|TEST|PKG)$/) {
+        $type = $1 . $Fcm::Config::DELIMITER . 'USER';
+
+      } elsif ($type !~ /^(?:DEV|TEST|PKG)$Fcm::Config::DELIMITER(?:USER|SHARE)$/
+               and $type !~ /^PKG$Fcm::Config::DELIMITER(?:CONFIG|REL)/) {
+        _cli_err('CLI_OPT_ARG', 'type', $type);
+      }
+
+    } else {
+      $type = 'DEV' . $Fcm::Config::DELIMITER . 'USER';
+    }
+
+    # Check branch name
+    if (!$name) {
+      _cli_err('CLI_OPT_WITH_OPT', 'name', 'create');
+    }
+
+    if ($name !~ qr{\A[\w.-]+\z}xms) {
+      _cli_err('CLI_OPT_ARG', 'name', $name);
+    }
+
+    # Check revision flag is valid
+    if ($rev_flag) {
+      $rev_flag = uc ($rev_flag);
+      if ($rev_flag !~ qr{\A (?:NORMAL|NUMBER|NONE) \z}xms) {
+        _cli_err('CLI_OPT_ARG', 'rev-flag', $rev_flag);
+      }
+
+    } else {
+      $rev_flag = 'NORMAL';
+    }
+
+    # Handle multiple tickets
+    @tickets = split (
+      /$Fcm::Config::DELIMITER_LIST/,
+      join ($Fcm::Config::DELIMITER_LIST, @tickets)
+    );
+    s/^#// for (@tickets);
+    @tickets = sort {$a <=> $b} @tickets;
+
+    # Determine whether to create a branch of a branch
+    $url->branch ('trunk') unless $branch_of_branch;
+
+    # Create the branch
+    my $branch = Fcm::CmBranch->new;
+    $branch->create (
+      SRC                 => $url,
+      TYPE                => $type,
+      NAME                => $name,
+      PASSWORD            => $password,
+      REV_FLAG            => $rev_flag,
+      TICKET              => \@tickets,
+      REV                 => $rev,
+      NON_INTERACTIVE     => $non_interactive,
+      SVN_NON_INTERACTIVE => $svn_non_interactive,
+    );
+
+  } elsif ($list) {
+    # The option --list is specified
+    # List branches owned by current or specified users
+    # --------------------------------------------------------------------------
+    # Get URL of the project "branches/" sub-directory
+    $url->subdir ('');
+    $url->branch ('');
+
+    my @branches = $url->branch_list($rev);
+    if (!$show_all) {
+      @userlist = split(qr{:}xms, join(q{:}, @userlist));
+      if (!@userlist) {
+        @userlist = (Fcm::Config->instance()->user_id());
+      }
+      my %filter = map {($_, 1)} @userlist;
+      @branches = grep {
+        $filter{Fcm::CmBranch->new(URL => $_)->branch_owner()}
+      } @branches
+    }
+
+    # Output, number of branches found
+    $CLI_MESSAGE->(
+      'BRANCH_LIST',
+      $url->project_url_peg(),
+      $rev ? "r$rev" : 'HEAD',
+      scalar(@branches),
+      ($show_all ? '[--show-all]' : join(q{, }, sort(@userlist))),
+    );
+
+    if (@branches) {
+      # Output the URL of each branch
+      if (not $verbose) {
+        my $project = $url->project_url;
+        @branches = map {Fcm::Keyword::unexpand($_)} @branches;
+      }
+      @branches = map {$_ . "\n"} sort @branches;
+      $CLI_MESSAGE->(q{}, join(q{}, @branches));
+
+    } else {
+      # No branch found, exit with an error code
+      return;
+    }
+
+  } else {
+    # The option --info or --delete is specified
+    # Report branch information (and/or delete a branch)
+    # --------------------------------------------------------------------------
+    # Set verbose level
+    Fcm::Config->instance()->verbose ($verbose ? 1 : 0);
+
+    # Set up the branch, report any error
+    my $branch = Fcm::CmBranch->new (URL => $url->url_peg);
+    if (!$branch->branch()) {
+      return _cm_err(Fcm::Cm::Exception->INVALID_BRANCH, $branch->url_peg());
+    }
+    if (!$branch->url_exists()) {
+      return _cm_err(Fcm::Cm::Exception->NOT_EXIST, $branch->url_peg());
+    }
+
+    # Remove the sub-directory part of the URL
+    $branch->subdir ('');
+
+    # Report branch info
+    $branch->display_info (
+      SHOW_CHILDREN => ($show_all || $show_children),
+      SHOW_OTHER    => ($show_all || $show_other   ),
+      SHOW_SIBLINGS => ($show_all || $show_siblings),
+    );
+
+    # Delete branch if --delete is specified
+    $branch->del (
+      PASSWORD            => $password,
+      NON_INTERACTIVE     => $non_interactive,
+      SVN_NON_INTERACTIVE => $svn_non_interactive,
+    ) if $delete;
+  }
+
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_commit ();
+#
+# DESCRIPTION
+#   This is a FCM wrapper to the "svn commit" command.
+# ------------------------------------------------------------------------------
+
+sub cm_commit {
+  my ($dry_run, $svn_non_interactive, $password);
+  my $rc = GetOptions(
+    'dry-run'             => \$dry_run,
+    'svn-non-interactive' => \$svn_non_interactive,
+    'password=s'          => \$password,
+  );
+  if (!$rc) {
+    _cli_err();
+  }
+
+  # The remaining argument is the path to a working copy
+  my ($path) = @ARGV;
+
+  if ($path) {
+    if (!-e $path) {
+      return _cm_err(Fcm::Cm::Exception->NOT_EXIST, $path);
+    }
+
+  } else {
+    # No argument specified, use current working directory
+    $path = cwd ();
+  }
+
+  # Make sure we are in a working copy
+  if (!is_wc($path)) {
+    return _cm_err(Fcm::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 = &get_wct ($path);
+
+  if ($dir ne cwd ()) {
+    chdir($dir) || return _cm_err(Fcm::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);
+  unless (defined $dry_run) {
+    my (@conflict, @missing, @outdate);
+
+    for (@status) {
+      if (/^C/) {
+        push @conflict, $_;
+        next;
+      }
+
+      if (/^!/) {
+        push @missing, $_;
+        next;
+      }
+
+      if (/^.{7}\*/) {
+        push @outdate, $_;
+        next;
+      }
+
+      # Check that all files which have been added have the svn:executable
+      # property set correctly (in case the developer adds a script before they
+      # remember to set the execute bit)
+      next unless /^A.{7} *\d+ +(.*)/;
+      my $file = $1;
+
+      next unless -f $file;
+      my ($command, @arguments)
+        = (-x $file && !-l $file) ? ('propset', '*') : ('propdel');
+      run_command(['svn', $command, qw{-q svn:executable}, @arguments, $file]);
+    }
+
+    # Abort commit if files are in conflict, missing, or out of date
+    if (@conflict or @missing or @outdate) {
+      for (
+        ['ST_CONFLICT'   , \@conflict],
+        ['ST_MISSING'    , \@missing ],
+        ['ST_OUT_OF_DATE', \@outdate ],
+      ) {
+        my ($key, $array_ref) = @{$_};
+        if (@{$array_ref}) {
+          $CLI_MESSAGE->($key, join(q{}, @{$array_ref}));
+        }
+      }
+      return _cm_abort(Fcm::Cm::Abort->FAIL);
+    }
+  }
+
+  # Read in any existing message
+  my $ci_mesg = Fcm::CmCommitMessage->new;
+  $ci_mesg->read_file;
+
+  # Execute "svn status" for a list of changed items
+  @status = grep !/^\?/, _svn_status_get();
+
+  # Abort if there is no change in the working copy
+  if (!@status) {
+    return _cm_abort(Fcm::Cm::Abort->NULL);
+  }
+
+  # Get associated URL of current working copy
+  my $url = Fcm::CmUrl->new (URL => &get_url_of_wc ());
+
+  # Include URL, or project, branch and sub-directory info in @status
+  unshift @status, "\n";
+
+  if ($url->project and $url->branch) {
+    unshift @status, (
+      '[Project: ' . $url->project                           . ']' . "\n",
+      '[Branch : ' . $url->branch                            . ']' . "\n",
+      '[Sub-dir: ' . ($url->subdir ? $url->subdir : '<top>') . ']' . "\n",
+    );
+
+  } else {
+    unshift @status, '[URL: ' . $url->url . ']' . "\n";
+  }
+
+  # Use a temporary file to store the final commit log message
+  $ci_mesg->ignore_mesg (\@status);
+  my $logfile = $ci_mesg->edit_file (TEMP => 1);
+
+  # Check with the user to see if he/she wants to go ahead
+  my $reply = 'n';
+  if (!defined($dry_run)) {
+    # Add extra warning for trunk commit
+    my @prompt_args;
+    my $user = Fcm::Config->instance()->user_id();
+
+    if ($url->is_trunk()) {
+      @prompt_args = ('CI_TRUNK');
+    }
+    elsif ($user && $url->is_branch() && $url->branch_owner() ne $user) {
+      if (exists $Fcm::CmUrl::owner_keywords{$url->branch_owner}) {
+        @prompt_args = (
+          'CI_BRANCH_SHARED',
+          uc($Fcm::CmUrl::owner_keywords{$url->branch_owner()}),
+        );
+      }
+      else {
+        @prompt_args = ('CI_BRANCH_USER');
+      }
+    }
+    else {
+      @prompt_args = ('CI');
+    }
+    $reply = $CLI_PROMPT->('commit', @prompt_args);
+  }
+
+  if ($reply eq 'y') {
+    # Commit the change if user replies "y" for "yes"
+    my @command = (
+      qw/svn commit -F/, $logfile,
+      ($svn_non_interactive  ? '--non-interactive'       : ()),
+      (defined $password     ? ('--password', $password) : ()),
+    );
+    my $rc;
+    &run_command (\@command, RC => \$rc, ERROR => 'warn');
+
+    if ($rc) {
+      # Commit failed
+      # Write temporary commit log content to commit log message file
+      $ci_mesg->write_file;
+
+      # Fail the command
+      return _cm_abort(Fcm::Cm::Abort->FAIL);
+    }
+
+    # Remove commit message file
+    unlink $ci_mesg->file;
+
+    # Update the working copy
+    $CLI_MESSAGE->(q{}, join(q{}, _svn_update()));
+
+  } else {
+    $ci_mesg->write_file;
+    if (!$dry_run) {
+      return _cm_abort();
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_conflicts ();
+#
+# DESCRIPTION
+#   This is a FCM command for resolving conflicts within working copy using a
+#   graphical merge tool.
+# ------------------------------------------------------------------------------
+
+sub cm_conflicts {
+  # Path to the working copy
+  my $path = $ARGV[0];
+  $path    = cwd () if not $path;
+
+  # Check for any files with conflicts
+  my @status = grep /^C.{4} *(.*)/, &run_command (
+    [qw/svn st/, ($path eq cwd () ? () : $path)], METHOD => 'qx',
+  );
+  my @files  = map {m/^C.{4} *(.*)/; $1} @status;
+
+  # Save current working directory
+  my $topdir = cwd ();
+
+  # Set up environment for graphical merge
+  # Use environment variable if set, otherwise use default setting
+  local(%ENV) = %ENV;
+  $ENV{FCM_GRAPHIC_MERGE}
+    ||= Fcm::Config->instance()->setting (qw/TOOL GRAPHIC_MERGE/);
+
+  FILE:
+  for my $file (@files) {
+    # Print name of file in conflicts
+    $CLI_MESSAGE->('CF', $file);
+
+    # Determine directory and base name of file in conflicts
+    my $base = basename $file;
+    my $dir  = dirname $file;
+
+    # Change to container directory of file in conflicts
+    chdir(File::Spec->catfile($topdir, $dir))
+      || return _cm_err(Fcm::Cm::Exception->CHDIR, $dir);
+
+    # Use "svn info" to determine conflict marker files
+    my @info = &run_command ([qw/svn info/, $base], METHOD => 'qx');
+
+    # Ignore if $base is a binary file
+    if (-B $base) {
+      $CLI_MESSAGE->('CF_BINARY', $base);
+      next FILE;
+    }
+
+    # Get conflicts markers files
+    my ($older, $mine, $yours);
+
+    for (@info) {
+      $older = $1 if (/^Conflict Previous Base File: (.*)/);
+      $mine  = $1 if (/^Conflict Previous Working File: (.*)/);
+      $yours = $1 if (/^Conflict Current Base File: (.*)/);
+    }
+
+    if (-f $base and (stat $base)[9] > (stat $mine)[9] + 1) {
+      # If $base is newer (by more than a second), it may contain saved changes
+      if ($CLI_PROMPT->('conflicts', 'CF_OVERWRITE', $base) ne 'y') {
+        next FILE;
+      }
+    }
+
+    # Launch graphic merge tool
+    my $rc;
+    my $command = [qw/fcm_graphic_merge/, $base, $mine, $older, $yours];
+    # $rc == 0: all conflicts resovled
+    # $rc == 1: some conflicts not resolved
+    # $rc == 2: trouble
+    eval {
+      run_command($command, RC => \$rc);
+    };
+    if ($@) {
+      if (!defined($rc) || $rc > 1) {
+        die($@);
+      }
+    }
+    next FILE if $rc;
+
+    # Prompt user to run "svn resolved" on the file
+    if ($CLI_PROMPT->('conflicts', 'RUN_SVN_COMMAND', 'resolved') eq 'y') {
+      run_command([qw{svn resolved}, $base]);
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_diff ();
+#
+# DESCRIPTION
+#   This is a wrapper to "svn diff". It adds two extra functionalities. The
+#   first one allows the command to show differences relative to the base of
+#   the branch. The second one allows differences to be displayed via a
+#   graphical tool.
+# ------------------------------------------------------------------------------
+
+sub cm_diff {
+  # Set up environment for graphical diff
+  # Use environment variable if set, otherwise use default setting
+  local(%ENV) = %ENV;
+  $ENV{FCM_GRAPHIC_DIFF}
+    ||= Fcm::Config->instance()->setting(qw/TOOL GRAPHIC_DIFF/);
+
+  # Check for the --branch options
+  # ----------------------------------------------------------------------------
+  my $branch = grep {$_ eq '-b' or $_ eq '--branch'} @ARGV;
+
+  if (not $branch) {
+    # The --branch option not specified, just call "svn diff"
+    # Convert the --graphical to qw/--diff-cmd fcm_graphical_diff/
+    # Convert the --summarise to --summarize
+    @ARGV = map {
+      my @return;
+      if ($_ eq '-g' or $_ eq '--graphical') {
+        @return = (qw/--diff-cmd fcm_graphic_diff/)
+
+      } elsif ($_ eq '--summarise') {
+        @return = ('--summarize');
+
+      } else {
+        @return = ($_);
+      }
+      @return;
+    } @ARGV;
+
+    # Execute the command
+    return _svn('diff', @ARGV);
+  }
+
+  # The --branch option is specified
+  # ----------------------------------------------------------------------------
+
+  # Determine whether the --graphical option is specified,
+  # if so set the appropriate command
+  # ----------------------------------------------------------------------------
+  my ($diff_cmd, $extensions, $graphical, $summarise, $trac, $wiki);
+  my $rc = GetOptions (
+    'b|branch'            => \$branch,
+    'diff-cmd=s'          => \$diff_cmd,
+    'x|extensions=s'      => \$extensions,
+    'g|graphical'         => \$graphical,
+    'summarise|summarize' => \$summarise,
+    't|trac'              => \$trac,
+    'wiki'                => \$wiki,
+  );
+  if (!$rc) {
+    _cli_err();
+  }
+
+  my @diff_cmd = ();
+
+  if ($graphical) {
+    @diff_cmd = (qw/--diff-cmd fcm_graphic_diff/);
+
+  } elsif ($diff_cmd) {
+    @diff_cmd = ('--diff-cmd', $diff_cmd);
+
+    push @diff_cmd, '--extensions', split (/\s+/, $extensions) if $extensions;
+  }
+
+  # The remaining argument should either be a URL or a PATH
+  my ($url_arg, $path_arg);
+
+  if (@ARGV) {
+    my $arg = Fcm::CmUrl->new (URL => $ARGV[0]);
+
+    if ($arg->is_url) {
+      $url_arg = $ARGV[0];
+
+    } else {
+      $path_arg = $ARGV[0];
+    }
+  }
+
+  # Get repository and branch information
+  # ----------------------------------------------------------------------------
+  my ($url, $path);
+  if (defined $url_arg) {
+    # If a URL is specified, get repository and branch information from it
+    $url = Fcm::CmBranch->new (URL => $url_arg);
+
+  } else {
+    # Get repository and branch information from the specified path or the
+    # current directory if it is a working copy
+    $path = $path_arg ? $path_arg : cwd ();
+    if (!is_wc($path)) {
+      return _cm_err(Fcm::Cm::Exception->INVALID_WC, $path);
+    }
+
+    $url  = Fcm::CmBranch->new (URL => &get_url_peg_of_wc ($path));
+  }
+
+  # Check that URL is a standard FCM branch
+  if (!$url->is_branch()) {
+    return _cm_err(Fcm::Cm::Exception->INVALID_BRANCH, $url->url_peg());
+  }
+
+  # Save and remove sub-directory part of the URL
+  my $subdir = $url->subdir ();
+  $url->subdir ('');
+
+  # Check that $url exists
+  if (!$url->url_exists()) {
+    return _cm_err(Fcm::Cm::Exception->INVALID_URL, $url->url_peg());
+  }
+
+  # Compare current branch with its parent
+  # ----------------------------------------------------------------------------
+  my $parent = Fcm::CmBranch->new (URL => $url->parent->url);
+  $parent->pegrev ($url->pegrev) if $url->pegrev;
+
+  if (!$parent->url_exists()) {
+    return _cm_err(
+      Fcm::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
+  $url->subdir ($subdir);
+  $base->subdir ($subdir);
+
+  # Ensure the branch URL has a peg revision
+  $url->pegrev ($url->svninfo (FLAG => 'Last Changed Rev')) if not $url->pegrev;
+
+  if ($trac or $wiki) {
+    # Trac/wiki
+    # --------------------------------------------------------------------------
+    if (!$url_arg && _svn_status_get([$path_arg ? $path_arg : q{.}])) {
+      $CLI_MESSAGE->('ST_IN_TRAC_DIFF', ($path_arg ? $path_arg : q{.}));
+    }
+
+    # Trac wiki syntax
+    my $wiki_syntax = 'diff:' . $base->path_peg . '//' . $url->path_peg;
+
+    if ($wiki) {
+      # Print Trac wiki syntax only
+      $CLI_MESSAGE->(q{}, "$wiki_syntax\n");
+
+    } else { # if $trac
+      # Use Trac to view "diff"
+      my $browser = Fcm::Config->instance()->setting(qw/WEB_BROWSER/);
+      $browser ||= 'firefox';
+
+      my $trac_url = Fcm::Keyword::get_browser_url($url->project_url());
+      $trac_url =~ s{/intertrac/.*$}{/intertrac/$wiki_syntax}xms;
+
+      &run_command ([$browser, $trac_url], METHOD => 'exec', PRINT => 1);
+    }
+
+  } else {
+    # Execute the "diff" command
+    # --------------------------------------------------------------------------
+    my @command = (
+      qw/svn diff/, @diff_cmd,
+      ($summarise ? ('--summarize') : ()),
+      '--old', $base->url_peg,
+      '--new', ($url_arg ? $url->url_peg : ($path_arg ? $path_arg : '.')),
+    );
+    &run_command (\@command, PRINT => 1);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_merge ();
+#
+# DESCRIPTION
+#   This is a wrapper to "svn merge".
+# ------------------------------------------------------------------------------
+
+sub cm_merge {
+  # Options
+  # ----------------------------------------------------------------------------
+  my ($custom, $dry_run, $non_interactive, $reverse, $rev, $verbose);
+  my $rc = GetOptions(
+    'custom'          => \$custom,
+    'dry-run'         => \$dry_run,
+    'non-interactive' => \$non_interactive,
+    'reverse'         => \$reverse,
+    'revision|r=s'    => \$rev,
+    'verbose|v'       => \$verbose,
+  );
+  if (!$rc) {
+    _cli_err();
+  }
+
+  # Find out the URL of the working copy
+  # ----------------------------------------------------------------------------
+  my ($target, $wct);
+  if (&is_wc ()) {
+    $wct = &get_wct ();
+
+    if ($wct ne cwd ()) {
+      chdir($wct) || return _cm_err(Fcm::Cm::Exception->CHDIR, $wct);
+      $CLI_MESSAGE->('CHDIR_WCT', $wct);
+    }
+
+    $target = Fcm::CmBranch->new (URL => &get_url_of_wc ($wct));
+
+  } else {
+    return _cm_err(Fcm::Cm::Exception->INVALID_WC, '.');
+  }
+
+  if (!$target->url_exists()) {
+    return _cm_err(Fcm::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;
+  $target->subdir ('') if $subdir;
+
+  # Check for any local modifications
+  # ----------------------------------------------------------------------------
+  if (!$dry_run && !$non_interactive) {
+    _svn_status_checker('merge', 'MODIFIED', $CLI_HANDLER_OF{WC_STATUS})->();
+  }
+
+  # Determine the SOURCE URL
+  # ----------------------------------------------------------------------------
+  my $source;
+
+  if ($reverse) {
+    # Reverse merge, the SOURCE is the the working copy URL
+    $source = Fcm::CmBranch->new (URL => $target->url);
+
+  } else {
+    # Automatic/custom merge, argument 1 is the SOURCE of the merge
+    my $source_url = shift (@ARGV);
+    if (!$source_url) {
+      _cli_err('CLI_MERGE_ARG1');
+    }
+
+    $source = _cm_get_source($source_url, $target);
+  }
+
+  # Parse the revision option
+  # ----------------------------------------------------------------------------
+  if ($reverse && !$rev) {
+    _cli_err('CLI_OPT_WITH_OPT', 'revision', 'reverse');
+  }
+  my @revs = (($reverse || $custom) && $rev ? split(qr{:}xms, $rev) : ());
+
+  # Determine the merge delta and the commit log message
+  # ----------------------------------------------------------------------------
+  my (@delta, $mesg);
+  my $separator = '-' x 80 . "\n";
+
+  if ($reverse) {
+    # Reverse merge
+    # --------------------------------------------------------------------------
+    if (@revs == 1) {
+      $revs[1] = ($revs[0] - 1);
+
+    } else {
+      @revs = sort {$b <=> $a} @revs;
+    }
+
+    $source->pegrev ($source->svninfo (FLAG => 'Last Changed Rev'))
+      unless $source->pegrev;
+    $source->subdir ($subdir);
+
+    # "Delta" of the "svn merge" command
+    @delta = ('-r' . $revs[0] . ':' . $revs[1], $source->url_peg);
+
+    # Template message
+    $mesg = 'Reversed r' . $revs[0] .
+            (($revs[1] < $revs[0] - 1) ? ':' . $revs[1] : '') . ' of ' .
+            $source->path . "\n";
+
+  } elsif ($custom) {
+    # Custom merge
+    # --------------------------------------------------------------------------
+    if (@revs) {
+      # Revision specified
+      # ------------------------------------------------------------------------
+      # Only one revision N specified, use (N - 1):N as the delta
+      unshift @revs, ($revs[0] - 1) if @revs == 1;
+
+      $source->pegrev ($source->svninfo (FLAG => 'Last Changed Rev'))
+        unless $source->pegrev;
+      $source->subdir ($subdir);
+      $target->subdir ($subdir);
+
+      # "Delta" of the "svn merge" command
+      @delta = ('-r' . $revs[0] . ':' . $revs[1], $source->url_peg);
+
+      # Template message
+      $mesg = 'Custom merge into ' . $target->path . ': r' . $revs[1] .
+              ' cf. r' . $revs[0] . ' of ' . $source->path_peg . "\n";
+
+    } else {
+      # Revision not specified
+      # ------------------------------------------------------------------------
+      # Get second source URL
+      my $source2_url = shift (@ARGV);
+      if (!$source2_url) {
+        _cli_err('CLI_MERGE_ARG2');
+      }
+
+      my $source2 = _cm_get_source($source2_url, $target);
+
+      $source->pegrev  ($source->svninfo  (FLAG => 'Last Changed Rev'))
+        unless $source->pegrev;
+      $source2->pegrev ($source2->svninfo (FLAG => 'Last Changed Rev'))
+        unless $source2->pegrev;
+      $source->subdir  ($subdir);
+      $source2->subdir ($subdir);
+      $target->subdir  ($subdir);
+
+      # "Delta" of the "svn merge" command
+      @delta = ($source->url_peg, $source2->url_peg);
+
+      # Template message
+      $mesg = 'Custom merge into ' . $target->path . ': ' . $source->path_peg .
+              ' cf. ' . $source2->path_peg . "\n";
+    }
+
+  } else {
+    # Automatic merge
+    # --------------------------------------------------------------------------
+    # Check to ensure source branch is not the same as the target branch
+    if (!$target->branch()) {
+      return _cm_err(Fcm::Cm::Exception->WC_INVALID_BRANCH, $wct);
+    }
+    if ($source->branch() eq $target->branch()) {
+      return _cm_err(Fcm::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(
+      Fcm::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 ($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(Fcm::Cm::Abort->NULL);
+    }
+
+    # If more than one merge available, prompt user to enter a revision number
+    # to merge from, default to $revs [0]
+    # --------------------------------------------------------------------------
+    if ($non_interactive || @revs == 1) {
+      $source->pegrev($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 = (Fcm::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(Fcm::Cm::Exception->MERGE_REV_INVALID, $reply)
+      }
+      $source->pegrev($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(Fcm::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 $s = Fcm::CmBranch->new (URL => $source->url_peg);
+    my $b = Fcm::CmBranch->new (URL => $base->url_peg);
+
+    $s->subdir ($subdir) if $subdir;
+    $b->subdir ($subdir) if $subdir;
+
+    # Diagnostic
+    $CLI_MESSAGE->('MERGE_CF', $s->path_peg(), $b->path_peg());
+
+    # Delta of the "svn merge" command
+    @delta = ($b->url_peg, $s->url_peg);
+
+    # Template message
+    $mesg = 'Merged into ' . $target->path . ': ' . $source->path_peg .
+            ' cf. ' . $base->path_peg . "\n";
+  }
+
+  # Run "svn merge" in "--dry-run" mode to see the result
+  # ----------------------------------------------------------------------------
+  my @out   = &run_command (
+    [qw/svn merge --dry-run/, @delta],
+    METHOD => 'qx', PRINT => ($dry_run and $verbose),
+  );
+
+  # Abort merge if it will result in no change
+  if (not @out) {
+    return _cm_abort(Fcm::Cm::Abort->NULL);
+  }
+
+  # Report result of "svn merge --dry-run"
+  if ($dry_run || !$non_interactive) {
+    $CLI_MESSAGE->('MERGE_DRY');
+    $CLI_MESSAGE->('SEPARATOR');
+    $CLI_MESSAGE->(q{}, join(q{}, @out));
+    $CLI_MESSAGE->('SEPARATOR');
+  }
+
+  return if $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 (!$non_interactive && $CLI_PROMPT->('merge', 'MERGE') ne 'y') {
+    return _cm_abort();
+  }
+  $CLI_MESSAGE->('MERGE');
+  run_command([qw/svn merge/, @delta], PRINT => $verbose);
+
+  # Prepare the commit log
+  # ----------------------------------------------------------------------------
+  # Read in any existing message
+  my $ci_mesg = Fcm::CmCommitMessage->new;
+  $ci_mesg->read_file;
+  $ci_mesg->auto_mesg ([$mesg, @{ $ci_mesg->auto_mesg }]);
+  $ci_mesg->write_file;
+
+  if ($verbose) {
+    $CLI_MESSAGE->('SEPARATOR');
+    $CLI_MESSAGE->('MERGE_CI', $mesg);
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_mkpatch ();
+#
+# DESCRIPTION
+#   This is a FCM command to create a patching script from particular revisions
+#   of a URL.
+# ------------------------------------------------------------------------------
+
+sub cm_mkpatch {
+  # Process command line options and arguments
+  # ----------------------------------------------------------------------------
+  my (@exclude, $organisation, $revision);
+  my $rc = GetOptions(
+    'exclude=s'      => \@exclude,
+    'organisation=s' => \$organisation,
+    'r|revision=s'   => \$revision,
+  );
+  if (!$rc) {
+    _cli_err();
+  }
+
+  # Excluded paths, convert glob into regular patterns
+  @exclude = split (/:/, join (':', @exclude));
+  for (@exclude) {
+    s#\*#[^/]*#; # match any number of non-slash character
+    s#\?#[^/]#;  # match a non-slash character
+    s#/*$##;     # remove trailing slash
+  }
+
+  # Organisation prefix
+  $organisation = $organisation ? $organisation : 'original';
+
+  # Make sure revision option is set correctly
+  my @revs = $revision ? split (/:/, $revision) : ();
+  @revs    = @revs [0, 1] if @revs > 2;
+
+  # Arguments
+  my ($u, $outdir) = @ARGV;
+
+  if (!$u) {
+    _cli_err();
+  }
+
+  my $url = Fcm::CmUrl->new (URL => $u);
+  if (!$url->is_url()) {
+    return _cm_err(Fcm::Cm::Exception->INVALID_URL, $u);
+  }
+  if (!$url->url_exists()) {
+    return _cm_err(Fcm::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') ne 'y') {
+      return _cm_abort();
+    }
+
+    rmtree($outdir) || return _cm_err(Fcm::Cm::Exception->RMTREE, $outdir);
+  }
+
+  # (Re-)create output directory
+  mkpath($outdir) || return _cm_err(Fcm::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 @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';
+
+      push @paths, $path;
+    }
+
+    # If a patch is being used, make sure it isn't just property changes
+    if ($use_patch) {
+      my @changedpaths;
+      for my $path (@paths) {
+        (my $file = $path) =~ s#^$url_path/*##;
+        if ($log{$rev}{paths}{$path}{action} eq 'M') {
+          my ($diff) = &run_command (
+                         [qw/svn diff --no-diff-deleted --summarize -c/,
+                          $rev, $url->url . '/' . $file. '@' . $rev],
+                         METHOD => 'qx');
+          next unless $diff =~ /^[A-Z]/;
+        }
+        push @changedpaths, $path;
+      }
+      @paths = @changedpaths;
+    }
+
+    next unless @paths;
+
+    # Create the patch using "svn diff"
+    my @patch = ();
+    if ($use_patch) {
+      @patch = &run_command ([qw/svn diff --no-diff-deleted -c/, $rev,
+                              $url->url], METHOD => 'qx');
+      if (@patch) {
+        # Don't use the patch if it may contain subversion keywords
+        for (@patch) {
+          $use_patch = 0 if /\$[a-zA-Z:]+ *\$/;
+        }
+      } 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(Fcm::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(?:/*|$)#;
+      }
+
+      if ($log{$rev}{paths}{$path}{action} eq 'D') {
+        # Script to delete file
+        push @after_script, 'svn delete ' . $file;
+
+      } else {
+        my $export_required = 0;
+        my $recursive_add   = 0;
+        my $is_newfile      = 0;
+
+        # Skip property changes
+        if ($log{$rev}{paths}{$path}{action} eq 'M') {
+          my ($diff) = &run_command (
+                         [qw/svn diff --no-diff-deleted --summarize -c/,
+                          $rev, $url->url . '/' . $file. '@' . $rev],
+                         METHOD => 'qx');
+          next CHANGED unless $diff =~ /^[A-Z]/;
+        }
+
+        # Determine if the file is a directory
+        my $is_dir = 0;
+        if ($log{$rev}{paths}{$path}{action} ne 'M') {
+          my @info = &run_command ([qw/svn info/, $url_file], METHOD => 'qx');
+          for (@info) {
+            if (/^Node Kind: (\w+)/) {
+              $is_dir = 1 if $1 eq 'directory';
+              last;
+            }
+          }
+        }
+
+        # Decide how to treat added files
+        if ($log{$rev}{paths}{$path}{action} eq 'A') {
+          # Determine if the file is copied
+          if (exists $log{$rev}{paths}{$path}{'copyfrom-path'}) {
+            if ($is_dir) {
+              # A copied directory needs to be treated as a new file, exported
+              # and added recursively
+              $is_newfile      = 1;
+              $export_required = 1;
+              $recursive_add   = 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 = Fcm::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 = Fcm::CmUrl->new (
+                      URL => $url->root . $attrib{'copyfrom-path'} . '@' .
+                             $attrib{'copyfrom-rev'},
+                    );
+                    $cp_cp_url->branch ($url->branch);
+                    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 path
+              if (not $is_newfile) {
+                for my $exclude (@exclude) {
+                  if ($copyfrom_path =~ m#^$exclude(?:/*|$)#) {
+                    $is_newfile = 1;
+                    last;
+                  }
+                }
+              }
+
+              # Script to copy 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
+            $is_newfile = 1;
+            # If it's a directory then create it (in case patch doesn't)
+            push @before_script, 'mkdir ' . $file if $is_dir;
+          }
+        }
+
+        if ($log{$rev}{paths}{$path}{action} eq 'R') {
+          # Script to delete file
+          push @before_script, 'svn delete ' . $file;
+
+          # Now treat as new file
+          $is_newfile = 1;
+        }
+
+        # Script to add the file, if required
+        if ($is_newfile) {
+          if ($recursive_add) {
+            push @after_script, 'svn add ' . $file;
+          } else {
+            push @after_script, 'svn add --non-recursive ' . $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 @mime_type = &run_command
+             ([qw/svn propget svn:mime-type/, $url_file], METHOD => 'qx');
+            for (@mime_type) {
+              $export_required = 1 if not /^text\//;
+            }
+            # 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);
+          &run_command ([qw/svn 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
+    my $shell = Fcm::Config->instance()->setting(qw/TOOL SHELL/);
+    print FILE <<EOF;
+#!$shell
+# ------------------------------------------------------------------------------
+# 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
+EOF
+
+    # Script content
+    print FILE @before_script if @before_script;
+    print FILE "patch -p0 <\${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';
+    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
+    my $shell = Fcm::Config->instance()->setting(qw/TOOL SHELL/);
+    print FILE <<EOF;
+#!$shell
+# ------------------------------------------------------------------------------
+# 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
+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
+export LANG=en_GB
+
+# Commands to apply patches
+EOF
+
+    # Script content
+    print FILE @script;
+
+    # Script footer
+    print FILE <<EOF;
+# Remove temporary working copy, if necessary
+if [[ -d \$fcm_tmp_dir && -w \$fcm_tmp_dir ]]; then
+  rm -rf \$fcm_tmp_dir
+fi
+
+echo "\$this: finished normally."
+#EOF
+EOF
+
+    close FILE or die $out, ': cannot close (', $!, ')';
+
+    # Add executable permission
+    chmod 0755, $out;
+
+    # Diagnostic
+    $CLI_MESSAGE->('PATCH_DONE', $outdir);
+
+  } else {
+    # Remove output directory
+    rmtree $outdir or die $outdir, ': cannot remove';
+
+    # Diagnostic
+    return _cm_abort(Fcm::Cm::Abort->NULL);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# CLI: fcm add.
+sub _cli_command_add {
+    my @args = map {($_ eq '--check' || $_ eq '-c' ? () : $_)} @_;
+    my %option = (st_check_handler => $CLI_HANDLER_OF{'WC_STATUS_PATH'});
+    return (
+        @args == @_ ? _svn("add", @args) : cm_check_unknown(\%option, @args)
+    );
+}
+
+# ------------------------------------------------------------------------------
+# CLI: fcm checkout.
+sub _cli_command_checkout {
+    if (@ARGV) {
+        my $target = is_url($ARGV[-1]) ? cwd() : $ARGV[-1];
+        if (-d $target && is_wc($target)) {
+            return _cm_err(Fcm::Cm::Exception->WC_EXIST, $target);
+        }
+    }
+    return _svn('checkout', @ARGV);
+}
+
+# ------------------------------------------------------------------------------
+# CLI: fcm delete.
+sub _cli_command_delete {
+    my @args = map {($_ eq '--check' || $_ eq '-c' ? () : $_)} @_;
+    my %option = (st_check_handler => $CLI_HANDLER_OF{'WC_STATUS_PATH'});
+    return (
+        @args == @_ ? _svn("delete", @args) : cm_check_missing(\%option, @args)
+    );
+}
+
+# ------------------------------------------------------------------------------
+# CLI: fcm switch.
+sub _cli_command_switch {
+    local(@ARGV) = @_;
+    if (grep {$_ eq '--relocate'} @ARGV) {
+        return _svn('switch', @ARGV);
+    }
+    my %option;
+    if (!GetOptions(\%option, 'non-interactive', 'revision|r=s', 'quiet|q')) {
+        _cli_err();
+    }
+    if (!$option{'non-interactive'}) {
+        $option{st_check_handler} = $CLI_HANDLER_OF{WC_STATUS};
+    }
+    if (!@ARGV) {
+        _cli_err();
+    }
+    $CLI_MESSAGE->(q{}, join(q{}, cm_switch(\%option, @ARGV)));
+}
+
+# ------------------------------------------------------------------------------
+# CLI: fcm update.
+sub _cli_command_update {
+    local(@ARGV) = @_;
+    my %option;
+    if (!GetOptions(\%option, 'non-interactive', 'revision|r=s', 'quiet|q')) {
+        _cli_err();
+    }
+    if (!$option{'non-interactive'}) {
+        $option{st_check_handler} = $CLI_HANDLER_OF{WC_STATUS};
+    }
+    $CLI_MESSAGE->(q{}, join(q{}, cm_update(\%option, @ARGV)));
+}
+
+# ------------------------------------------------------------------------------
+# CLI error.
+sub _cli_err {
+    my ($key, @args) = @_;
+    $key ||= 'CLI_USAGE';
+    my $message = sprintf($CLI_MESSAGE_FOR_ERROR{$key}, @args);
+    die(Fcm::CLI::Exception->new({message => $message}));
+}
+
+# ------------------------------------------------------------------------------
+# Handles abort exception.
+sub _cli_e_handler_of_cm_abort {
+    my ($function, $e) = @_;
+    if ($e->get_code() eq $e->FAIL) {
+        die(sprintf($CLI_MESSAGE_FOR_ABORT{FAIL}, $function));
+    }
+    else {
+        $CLI_MESSAGE->($e->get_code(), $function);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Handles CM exception.
+sub _cli_e_handler_of_cm_exception {
+    my ($function, $e) = @_;
+    die(sprintf($CLI_MESSAGE_FOR_ERROR{$e->get_code()}, $e->get_targets()));
+}
+
+# ------------------------------------------------------------------------------
+# Handles CLI exception.
+sub _cli_e_handler_of_cli_exception {
+    my ($function, $e) = @_;
+    $CLI_MESSAGE->('CLI', $e);
+    $CLI_MESSAGE->('CLI_HELP', $function);
+}
+
+# ------------------------------------------------------------------------------
+# The default handler of the "WC_STATUS" event.
+sub _cli_handler_of_wc_status {
+    my ($name, $target_list_ref, $status_list_ref) = @_;
+    if (@{$status_list_ref}) {
+        $CLI_MESSAGE->('STATUS', join(q{}, @{$status_list_ref}));
+        if ($CLI_PROMPT->($name, 'CONTINUE') 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) = @_;
+    $CLI_MESSAGE->(q{}, join(q{}, @{$status_list_ref}));
+    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;
+}
+
+# ------------------------------------------------------------------------------
+# Prints help for a given $subcommand.
+sub _cli_help {
+    my ($key, $exit_val) = @_;
+    my $pod
+        = File::Spec->catfile(dirname($INC{'Fcm/Cm.pm'}), 'CLI', "fcm-$key.pod");
+    my $has_pod = -f $pod;
+    if ($has_pod) {
+        pod2usage({
+            '-exitval' => defined($exit_val) ? $exit_val : 2,
+            '-input'   => $pod,
+            '-verbose' => 1,
+        });
+    }
+    if (!$has_pod || exists($CLI_MORE_HELP_FOR{$key})) {
+        local(@ARGV) = ($key);
+        return _svn('help', $key);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# 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 = Fcm::Util::tidy_url(Fcm::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 = (Fcm::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 Fcm::Interactive::get_input.
+sub _cli_prompt {
+    my %option
+        = (type => 'yn', default => 'n', (ref($_[0]) ? %{shift(@_)} : ()));
+    my ($name, $key, @args) = @_;
+    return Fcm::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) {
+        run_command([qw{svn 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) {
+        run_command([qw{svn add}, @paths]);
+    }
+}
+
+# ------------------------------------------------------------------------------
+# FCM wrapper to SVN switch.
+sub cm_switch {
+    my %option = %{shift()};
+    my ($target, $path) = @_;
+    $path ||= cwd();
+    if (!-e $path) {
+        return _cm_err(Fcm::Cm::Exception->NOT_EXIST, $path);
+    }
+    if (!is_wc($path)) {
+        return _cm_err(Fcm::Cm::Exception->INVALID_WC, $path);
+    }
+
+    # Check for merge template in the commit log file in the working copy
+    my $path_of_wc = get_wct($path);
+    my $ci_mesg = Fcm::CmCommitMessage->new();
+    $ci_mesg->dir($path_of_wc);
+    $ci_mesg->read_file();
+    if (@{$ci_mesg->auto_mesg()}) {
+        return _cm_err(
+            Fcm::Cm::Exception->SWITCH_UNSAFE,
+            $path eq $path_of_wc ? $ci_mesg->base() : $ci_mesg->file(),
+        );
+    }
+
+    # Check for any local modifications
+    if (defined($option{st_check_handler})) {
+        my $handler = $CLI_HANDLER_OF{WC_STATUS};
+        _svn_status_checker('switch', 'MODIFIED', $handler)->([$path_of_wc]);
+    }
+
+    # Invokes "svn switch"
+    _svn(
+        {METHOD => 'qx', PRINT => !$option{quiet}},
+        'switch',
+        ($option{'non-interactive'} ? '--non-interactive'       : ()),
+        ($option{revision}          ? ('-r', $option{revision}) : ()),
+        ($option{quiet}             ? '--quiet'                 : ()),
+        _cm_get_source(
+            $target,
+            Fcm::CmBranch->new(URL => get_url_of_wc($path_of_wc)),
+        )->url_peg(),
+        ($path_of_wc eq cwd() ? () : $path_of_wc),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# 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(Fcm::Cm::Exception->NOT_EXIST, $target);
+        }
+        if (!is_wc($target)) {
+            return _cm_err(Fcm::Cm::Exception->INVALID_WC, $target);
+        }
+        $target = get_wct($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', 'OUT_OF_DATE'], 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} = (
+            Fcm::Keyword::expand(get_url_of_wc($targets[0]), $option{revision})
+        )[1];
+    }
+    return _svn_update(\@targets, \%option);
+}
+
+# ------------------------------------------------------------------------------
+# Raises an abort exception.
+sub _cm_abort {
+    my ($code) = @_;
+    $code ||= Fcm::Cm::Abort->USER;
+    die(bless({code => $code, message => 'abort'}, 'Fcm::Cm::Abort'));
+}
+
+# ------------------------------------------------------------------------------
+# Raises a failure.
+sub _cm_err {
+    my ($code, @targets) = @_;
+    die(bless(
+        {code => $code, message => "ERROR: $code", targets => \@targets},
+        'Fcm::Cm::Exception',
+    ));
+}
+
+# ------------------------------------------------------------------------------
+# Returns the corresponding Fcm::CmBranch instance for $src_url w.r.t. $target.
+sub _cm_get_source {
+    my ($src_url, $target) = @_;
+    my $source = Fcm::CmBranch->new(URL => $src_url);
+    if (!$source->is_url()) {
+        # Not a full URL, construct full URL based on current URL
+        $source->url_peg($target->url_peg());
+        my $project = $target->project();
+        my ($path) = $src_url =~ qr{\A/*(.*)\z}xms;
+        if (index($path, $project) == 0) {
+            # Argument contains the full path under the repository root
+            $path = substr($path, length($project));
+        }
+        if ($path =~ $PATTERN_OF{FCM_BRANCH_PATH}) {
+            # Argument contains the full branch name
+            $path = join(q{/}, $target->project_path(), $path);
+        }
+        else {
+            # Argument contains the shorter branch name
+            $path = join(q{/}, $target->project_path(), 'branches', $path);
+        }
+        $source->path_peg($path);
+    }
+    # Replace source sub-directory with the target sub-directory
+    $source->subdir($target->subdir());
+    # Ensure that the branch name exists
+    if (!$source->url_exists()) {
+        return _cm_err(Fcm::Cm::Exception->INVALID_URL, $src_url);
+    }
+    # Ensure that the branch name is valid
+    if (!$source->branch()) {
+        return _cm_err(Fcm::Cm::Exception->INVALID_BRANCH, $src_url);
+    }
+    # Ensure that the source and target URLs are in the same project
+    if ($source->project_url() ne $target->project_url()) {
+        return _cm_err(
+            Fcm::Cm::Exception->DIFF_PROJECTS,
+            $target->url_peg(),
+            $source->url_peg(),
+        );
+    }
+    return $source;
+}
+
+# ------------------------------------------------------------------------------
+# Runs "svn".
+sub _svn {
+    my @args = @_;
+    my %option;
+    if (@args && ref($args[0])) {
+        %option = %{shift(@args)};
+    }
+    return run_command(
+        ['svn', @args],
+        PRINT => ($args[0] ne 'cat' && !grep {$_ eq '--xml'} @args),
+        %option,
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Returns the results of "svn status".
+sub _svn_status_get {
+    my ($target_list_ref, $show_updates) = @_;
+    my @targets = (defined($target_list_ref) ? @{$target_list_ref} : ());
+    for my $target (@targets) {
+        if ($target eq cwd()) {
+            $target = q{.};
+        }
+    }
+    my @options = ($show_updates ? qw{--show-updates} : ());
+    return _svn({METHOD => 'qx', PRINT => 0}, 'status', @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};
+    }
+    return sub {
+        my ($target_list_ref) = @_;
+        my @status = _svn_status_get($target_list_ref, $show_updates);
+        if ($show_updates) {
+            @status = map {$_ =~ $PATTERN_OF{ST_AGAINST_REV} ? () : $_} @status;
+        }
+        my @status_of_interest = grep {$matcher->($_)} @status;
+        if (defined($handler)) {
+            return $handler->($name, $target_list_ref, \@status_of_interest);
+        }
+        return @status_of_interest;
+    }
+}
+
+# ------------------------------------------------------------------------------
+# Runs "svn update".
+sub _svn_update {
+    my ($target_list_ref, $option_hash_ref) = @_;
+    my %option = (defined($option_hash_ref) ? %{$option_hash_ref} : ());
+    _svn(
+        {METHOD => 'qx', PRINT => !$option{quiet}},
+        'update',
+        ($option{'non-interactive'} ? '--non-interactive'       : ()),
+        ($option{revision}          ? ('-r', $option{revision}) : ()),
+        ($option{quiet}             ? '--quiet'                 : ()),
+        (defined($target_list_ref) ? @{$target_list_ref} : ()),
+    );
+}
+
+# ------------------------------------------------------------------------------
+# Abort exception.
+package Fcm::Cm::Abort;
+use base qw{Fcm::Exception};
+use constant {FAIL => 'FAIL', NULL => 'NULL', USER => 'USER'};
+
+sub get_code {
+    return $_[0]->{code};
+}
+
+# ------------------------------------------------------------------------------
+# Resource exception.
+package Fcm::Cm::Exception;
+our @ISA = qw{Fcm::Cm::Abort};
+use constant {
+    CHDIR             => 'CHDIR',
+    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_EXIST          => 'WC_EXIST',
+    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
+
+Fcm::Cm
+
+=head1 SYNOPSIS
+
+    use Fcm::Cm qw{cli};
+
+    # Use as a wrapper to Subversion, and other FCM code management commands
+    cli('info', '--revision', 'HEAD', $url);
+
+    use Fcm::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, $target_list_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, $target_list_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, $target_list_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 cli($function,@args)
+
+Implements the FCM code management CLI. If --help or -h is specified in @args,
+it displays help and returns.  Otherwise, it attempts to expand any FCM location
+and revision keywords in @args. Calls the relevant FCM code management function
+according to $function, or a SVN command if $function is not modified by FCM.
+
+=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, $target_list_ref, $status_list_ref) where
+$name is "delete", $target_list_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, $target_list_ref, $status_list_ref), where $name is
+'update', $target_list_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 Fcm::Cm::Abort
+
+This exception @ISA L<Fcm::Exception|Fcm::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 Fcm::Cm::Exception
+
+This exception @ISA L<Fcm::Abort|Fcm::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_EXIST
+
+The target working copy 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<Fcm::CmUrl|Fcm::CmUrl> and L<Fcm::CmBranch|Fcm::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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/CmBranch.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CmBranch.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CmBranch.pm	(revision 1981)
@@ -0,0 +1,1217 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CmBranch
+#
+# DESCRIPTION
+#   This class contains methods for manipulating a branch. It is a sub-class of
+#   Fcm::CmUrl, and inherits all methods from that class.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CmBranch;
+@ISA = qw(Fcm::CmUrl);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Spec;
+
+# FCM component modules
+use Fcm::CmCommitMessage;
+use Fcm::CmUrl;
+use Fcm::Config;
+use Fcm::Interactive;
+use Fcm::Keyword;
+use Fcm::Util qw/run_command 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 Fcm::CmBranch
+  'ANCESTOR',    # list of common ancestors with other branches
+                 # key = URL, value = ancestor Fcm::CmBranch
+  'LAST_MERGE',  # list of last merges from branches
+                 # key = URL@REV, value = [TARGET, UPPER, LOWER]
+  'AVAIL_MERGE', # list of available revisions for merging
+                 # key = URL@REV, value = [REV ...]
+  'CHILDREN',    # list of children of this branch
+  'SIBLINGS',    # list of siblings of this branch
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch = Fcm::CmBranch->new (URL => $url,);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::CmBranch class.
+#
+# ARGUMENTS
+#   URL    - URL of a branch
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::CmUrl->new (%args);
+
+  $self->{$_} = undef for (@properties);
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_branch->url_peg;
+#   $cm_branch->url_peg ($url);
+#
+# DESCRIPTION
+#   This method returns/sets the current URL.
+# ------------------------------------------------------------------------------
+
+sub url_peg {
+  my $self = shift;
+
+  if (@_) {
+    if (! $self->{URL} or $_[0] ne $self->{URL}) {
+      # Re-set URL and other essential variables in the SUPER-class
+      $self->SUPER::url_peg (@_);
+
+      # Re-set essential variables
+      $self->{$_} = undef for (@properties);
+    }
+  }
+
+  return $self->{URL};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rev = $cm_branch->create_rev;
+#
+# DESCRIPTION
+#   This method returns the revision at which the branch was created.
+# ------------------------------------------------------------------------------
+
+sub create_rev {
+  my $self = shift;
+
+  if (not $self->{CREATE_REV}) {
+    return unless $self->url_exists ($self->pegrev);
+
+    # Use "svn log" to find out the first revision of the branch
+    my %log = $self->svnlog (STOP_ON_COPY => 1);
+
+    # Look at log in ascending order
+    my $rev   = (sort {$a <=> $b} keys %log) [0];
+    my $paths = $log{$rev}{paths};
+
+    # Get revision when URL is first added to the repository
+    if (exists $paths->{$self->branch_path}) {
+      $self->{CREATE_REV} = $rev if $paths->{$self->branch_path}{action} eq 'A';
+    }
+  }
+
+  return $self->{CREATE_REV};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $parent = $cm_branch->parent;
+#
+# DESCRIPTION
+#   This method returns the parent (a Fcm::CmBranch object) of the current
+#   branch.
+# ------------------------------------------------------------------------------
+
+sub parent {
+  my $self = shift;
+
+  if (not $self->{PARENT}) {
+    # Use the log to find out the parent revision
+    my %log = $self->svnlog (REV => $self->create_rev);
+
+    if (exists $log{paths}{$self->branch_path}) {
+      my $path = $log{paths}{$self->branch_path};
+
+      if ($path->{action} eq 'A') {
+        if (exists $path->{'copyfrom-path'}) {
+          # Current branch is copied from somewhere, set the source as the parent
+          my $url = $self->root .  $path->{'copyfrom-path'};
+          my $rev = $path->{'copyfrom-rev'};
+          $self->{PARENT} = Fcm::CmBranch->new (URL => $url . '@' . $rev);
+
+        } else {
+          # Current branch is not copied from somewhere
+          $self->{PARENT} = $self;
+        }
+      }
+    }
+  }
+
+  return $self->{PARENT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rev = $cm_branch->delete_rev;
+#
+# DESCRIPTION
+#   This method returns the revision at which the branch was deleted.
+# ------------------------------------------------------------------------------
+
+sub delete_rev {
+  my $self = shift;
+
+  if (not $self->{DELETE_REV}) {
+    return if $self->url_exists ('HEAD');
+
+    # Container of the current URL
+    (my $dir_url = $self->branch_url) =~ s#/+[^/]+/*$##;
+
+    # Use "svn log" on the container between a revision where the branch exists
+    # and the HEAD
+    my $dir = Fcm::CmUrl->new (URL => $dir_url);
+    my %log = $dir->svnlog (
+      REV => ['HEAD', ($self->pegrev ? $self->pegrev : $self->create_rev)],
+    );
+
+    # Go through the log to see when branch no longer exists
+    for my $rev (sort {$a <=> $b} keys %log) {
+      next unless exists $log{$rev}{paths}{$self->branch_path} and
+                  $log{$rev}{paths}{$self->branch_path}{action} eq 'D';
+
+      $self->{DELETE_REV} = $rev;
+      last;
+    }
+  }
+
+  return $self->{DELETE_REV};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_branch->is_child_of ($branch);
+#
+# DESCRIPTION
+#   This method returns true if the current branch is a child of $branch.
+# ------------------------------------------------------------------------------
+
+sub is_child_of {
+  my ($self, $branch) = @_;
+
+  # The trunk cannot be a child branch
+  return if $self->is_trunk;
+
+  # If $branch is a branch, use name of $self to see when it is created
+  if ($branch->is_branch and $self->url =~ m#/r(\d+)_[^/]+/*$#) {
+    my $rev = $1;
+
+    # $self can only be a child if it is copied from a revision > the create
+    # revision of $branch
+    return if $rev < $branch->create_rev;
+  }
+
+  return if $self->parent->url ne $branch->url;
+
+  # If $branch is a branch, ensure that it is created before $self
+  return if $branch->is_branch and $self->create_rev <= $branch->create_rev;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_branch->is_sibling_of ($branch);
+#
+# DESCRIPTION
+#   This method returns true if the current branch is a sibling of $branch.
+# ------------------------------------------------------------------------------
+
+sub is_sibling_of {
+  my ($self, $branch) = @_;
+
+  # The trunk cannot be a sibling branch
+  return if $branch->is_trunk;
+
+  return if $self->parent->url ne $branch->parent->url;
+
+  # If the parent is a branch, ensure they are actually the same branch
+  return if $branch->parent->is_branch and
+            $self->parent->create_rev != $branch->parent->create_rev;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_get_relatives ($relation);
+#
+# DESCRIPTION
+#   This method sets the $self->{$relation} variable by inspecting the list of
+#   branches at the current revision of the current branch. $relation can be
+#   either "CHILDREN" or "SIBLINGS".
+# ------------------------------------------------------------------------------
+
+sub _get_relatives {
+  my ($self, $relation) = @_;
+
+  my @branch_list = $self->branch_list;
+
+  $self->{$relation} = [];
+
+  # If we are searching for CHILDREN, get list of SIBLINGS, and vice versa
+  my $other = ($relation eq 'CHILDREN' ? 'SIBLINGS' : 'CHILDREN');
+  my %other_list;
+  if ($self->{$other}) {
+    %other_list = map {$_->url, 1} @{ $self->{$other} };
+  }
+
+  for my $u (@branch_list) {
+    # Ignore URL of current branch and its parent
+    next if $u eq $self->url;
+    next if $self->is_branch and $u eq $self->parent->url;
+
+    # Ignore if URL is a branch detected to be another type of relative
+    next if exists $other_list{$u};
+
+    # Construct new Fcm::CmBranch object from branch URL
+    my $url = $u . ($self->pegrev ? '@' . $self->pegrev : '');
+    my $branch = Fcm::CmBranch->new (URL => $url);
+
+    # Test whether $branch is a relative we are looking for
+    if ($relation eq 'CHILDREN') {
+      push @{ $self->{$relation} }, $branch if $branch->is_child_of ($self);
+
+    } else {
+      push @{ $self->{$relation} }, $branch if $branch->is_sibling_of ($self);
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @children = $cm_branch->children;
+#
+# DESCRIPTION
+#   This method returns a list of children (Fcm::CmBranch objects) of the
+#   current branch that exists in the current revision.
+# ------------------------------------------------------------------------------
+
+sub children {
+  my $self = shift;
+
+  $self->_get_relatives ('CHILDREN') if not $self->{CHILDREN};
+
+  return @{ $self->{CHILDREN} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @siblings = $cm_branch->siblings;
+#
+# DESCRIPTION
+#   This method returns a list of siblings (Fcm::CmBranch objects) of the
+#   current branch that exists in the current revision.
+# ------------------------------------------------------------------------------
+
+sub siblings {
+  my $self = shift;
+
+  $self->_get_relatives ('SIBLINGS') if not $self->{SIBLINGS};
+
+  return @{ $self->{SIBLINGS} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ancestor = $cm_branch->ancestor ($branch);
+#
+# DESCRIPTION
+#   This method returns the common ancestor (a Fcm::CmBranch object) of a
+#   specified $branch and the current branch. The argument $branch must be a
+#   Fcm::CmBranch object. Both the current branch and $branch are assumed to be
+#   in the same project.
+# ------------------------------------------------------------------------------
+
+sub ancestor {
+  my ($self, $branch) = @_;
+
+  if (not exists $self->{ANCESTOR}{$branch->url_peg}) {
+    if ($self->url_peg eq $branch->url_peg) {
+      $self->{ANCESTOR}{$branch->url_peg} = $self;
+
+    } else {
+      # Get family tree of current branch, from trunk to current branch
+      my @this_family = ($self);
+      while (not $this_family [0]->is_trunk) {
+        unshift @this_family, $this_family [0]->parent;
+      }
+
+      # Get family tree of $branch, from trunk to $branch
+      my @that_family = ($branch);
+      while (not $that_family [0]->is_trunk) {
+        unshift @that_family, $that_family [0]->parent;
+      }
+
+      # Find common ancestor from list of parents
+      my $ancestor = undef;
+
+      while (not $ancestor) {
+        # $this and $that should both start as some revisions on the trunk.
+        # Walk down a generation each time it loops around.
+        my $this = shift @this_family;
+        my $that = shift @that_family;
+
+        if ($this->url eq $that->url) {
+          if ($this->is_trunk or $this->create_rev eq $that->create_rev) {
+            # $this and $that are the same branch
+            if (@this_family and @that_family) {
+              # More generations in both branches, try comparing the next
+              # generations.
+              next;
+
+            } else {
+              # End of lineage in one of the branches, ancestor is at the lower
+              # revision of the current URL.
+              if ($this->pegrev and $that->pegrev) {
+                $ancestor = $this->pegrev < $that->pegrev ? $this : $that;
+
+              } else {
+                $ancestor = $this->pegrev ? $this : $that;
+              }
+            }
+
+          } else {
+            # Despite the same URL, $this and $that are different branches as
+            # they are created at different revisions. The ancestor must be the
+            # parent with the lower revision. (This should not occur at the
+            # start.)
+            $ancestor = $this->parent->pegrev < $that->parent->pegrev
+                        ? $this->parent : $that->parent;
+          }
+
+        } else {
+          # Different URLs, ancestor must be the parent with the lower revision.
+          # (This should not occur at the start.)
+          $ancestor = $this->parent->pegrev < $that->parent->pegrev
+                      ? $this->parent : $that->parent;
+        }
+      }
+
+      $self->{ANCESTOR}{$branch->url_peg} = $ancestor;
+    }
+  }
+
+  return $self->{ANCESTOR}{$branch->url_peg};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($target, $upper, $lower) = $cm_branch->last_merge_from (
+#     $branch, $stop_on_copy,
+#   );
+#
+# DESCRIPTION
+#   This method returns a 3-element list with information of the last merge
+#   into the current branch from a specified $branch. The first element in the
+#   list $target (a Fcm::CmBranch object) is the target at which the merge was
+#   performed. (This can be the current branch or a parent branch up to the
+#   common ancestor with the specified $branch.) The second and third elements,
+#   $upper and $lower, (both Fcm::CmBranch objects), are the upper and lower
+#   ends of the source delta. If there is no merge from $branch into the
+#   current branch from their common ancestor to the current revision, this
+#   method will return an empty list. If $stop_on_copy is specified, it ignores
+#   merges from parents of $branch, and merges into parents of the current
+#   branch.
+# ------------------------------------------------------------------------------
+
+sub last_merge_from {
+  my ($self, $branch, $stop_on_copy) = @_;
+
+  if (not exists $self->{LAST_MERGE}{$branch->url_peg}) {
+    # Get "log" of current branch down to the common ancestor
+    my %log = $self->svnlog (
+      REV => [
+       ($self->pegrev ? $self->pegrev : 'HEAD'),
+       $self->ancestor ($branch)->pegrev,
+      ],
+
+      STOP_ON_COPY => $stop_on_copy,
+    );
+
+    my $cr = $self;
+
+    # Go down the revision log, checking for merge template messages
+    REV: for my $rev (sort {$b <=> $a} keys %log) {
+      # Loop each line of the log message at each revision
+      my @msg = split /\n/, $log{$rev}{msg};
+
+      # Also consider merges into parents of current branch
+      $cr = $cr->parent if ($cr->is_branch and $rev < $cr->create_rev);
+
+      for (@msg) {
+        # Ignore unless log message matches a merge template
+        next unless /Merged into \S+: (\S+) cf\. (\S+)/;
+
+        # Upper $1 and lower $2 ends of the source delta
+        my $u_path = $1;
+        my $l_path = $2;
+
+        # Add the root directory to the paths if necessary
+        $u_path = '/' . $u_path if substr ($u_path, 0, 1) ne '/';
+        $l_path = '/' . $l_path if substr ($l_path, 0, 1) ne '/';
+
+        # Only consider merges with specified branch (and its parent)
+        (my $path = $u_path) =~ s/@(\d+)$//;
+        my $u_rev = $1;
+
+        my $br = $branch;
+        $br    = $br->parent while (
+          $br->is_branch and $u_rev < $br->create_rev and not $stop_on_copy
+        );
+
+        next unless $br->branch_path eq $path;
+
+        # If $br is a parent of branch, ignore those merges with the parent
+        # above the branch point of the current branch
+        next if $br->pegrev and $br->pegrev < $u_rev;
+
+        # Set the return values
+        $self->{LAST_MERGE}{$branch->url_peg} = [
+          Fcm::CmBranch->new (URL => $cr->url . '@' . $rev), # target
+          Fcm::CmBranch->new (URL => $self->root . $u_path), # delta upper
+          Fcm::CmBranch->new (URL => $self->root . $l_path), # delta lower
+        ];
+
+        last REV;
+      }
+    }
+  }
+
+  return (exists $self->{LAST_MERGE}{$branch->url_peg}
+          ? @{ $self->{LAST_MERGE}{$branch->url_peg} } : ());
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @revs = $cm_branch->avail_merge_from ($branch[, $stop_on_copy]);
+#
+# DESCRIPTION
+#   This method returns a list of revisions of a specified $branch, which are
+#   available for merging into the current branch. If $stop_on_copy is
+#   specified, it will not list available merges from the parents of $branch.
+# ------------------------------------------------------------------------------
+
+sub avail_merge_from {
+  my ($self, $branch, $stop_on_copy) = @_;
+
+  if (not exists $self->{AVAIL_MERGE}{$branch->url_peg}) {
+    # Find out the revision of the upper delta at the last merge from $branch
+    # If no merge is found, use revision of common ancestor with $branch
+    my @last_merge = $self->last_merge_from ($branch);
+    my $rev        = $self->ancestor ($branch)->pegrev;
+    $rev           = $last_merge [1]->pegrev
+      if @last_merge and $last_merge [1]->pegrev > $rev;
+
+    # Get the "log" of the $branch down to $rev
+    my %log = $branch->svnlog (
+      REV          => [($branch->pegrev ? $branch->pegrev : 'HEAD'), $rev],
+      STOP_ON_COPY => $stop_on_copy,
+    );
+
+    # No need to include $rev itself, as it has already been merged
+    delete $log{$rev};
+
+    # No need to include the branch create revision
+    delete $log{$branch->create_rev}
+      if $branch->is_branch and exists $log{$branch->create_rev};
+
+    if (keys %log) {
+      # Check whether there is a latest merge from $self into $branch, if so,
+      # all revisions of $branch below that merge should become unavailable
+      my @last_merge_into = $branch->last_merge_from ($self);
+
+      if (@last_merge_into) {
+        for my $rev (keys %log) {
+          delete $log{$rev} if $rev < $last_merge_into [0]->pegrev;
+        }
+      }
+    }
+
+    # Available merges include all revisions above the branch creation revision
+    # or the revision of the last merge
+    $self->{AVAIL_MERGE}{$branch->url_peg} = [sort {$b <=> $a} keys %log];
+  }
+
+  return @{ $self->{AVAIL_MERGE}{$branch->url_peg} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $lower = $cm_branch->base_of_merge_from ($branch);
+#
+# DESCRIPTION
+#   This method returns the lower delta (a Fcm::CmBranch object) for the next
+#   merge from $branch.
+# ------------------------------------------------------------------------------
+
+sub base_of_merge_from {
+  my ($self, $branch) = @_;
+
+  # Base is the ancestor if there is no merge between $self and $branch
+  my $return = $self->ancestor ($branch);
+
+  # Get configuration for the last merge from $branch to $self
+  my @merge_from = $self->last_merge_from ($branch);
+
+  # Use the upper delta of the last merge from $branch, as all revisions below
+  # that have already been merged into the $self
+  $return = $merge_from [1]
+    if @merge_from and $merge_from [1]->pegrev > $return->pegrev;
+
+  # Get configuration for the last merge from $self to $branch
+  my @merge_into = $branch->last_merge_from ($self);
+
+  # Use the upper delta of the last merge from $self, as the current revision
+  # of $branch already contains changes of $self up to the peg revision of the
+  # upper delta
+  $return = $merge_into [1]
+    if @merge_into and $merge_into [0]->pegrev > $return->pegrev;
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_branch->allow_subdir_merge_from ($branch, $subdir);
+#
+# DESCRIPTION
+#   This method returns true if a merge from the sub-directory $subdir in
+#   $branch  is allowed - i.e. it does not result in losing changes made in
+#   $branch outside of $subdir.
+# ------------------------------------------------------------------------------
+
+sub allow_subdir_merge_from {
+  my ($self, $branch, $subdir) = @_;
+
+  # Get revision at last merge from $branch or ancestor
+  my @merge_from = $self->last_merge_from ($branch);
+  my $last       = @merge_from ? $merge_from [1] : $self->ancestor ($branch);
+  my $rev        = $last->pegrev;
+
+  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 = &run_command (
+      [qw/svn diff --summarize -r/, $range, $branch->url_peg], METHOD => 'qx', 
+    );
+
+    # 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->create (
+#     SRC                  => $src,
+#     TYPE                 => $type,
+#     NAME                 => $name,
+#     [PASSWORD            => $password,]
+#     [REV_FLAG            => $rev_flag,]
+#     [TICKET              => \@tickets,]
+#     [REV                 => $rev,]
+#     [NON_INTERACTIVE     => 1,]
+#     [SVN_NON_INTERACTIVE => 1,]
+#   );
+#
+# DESCRIPTION
+#   This method creates a branch in a Subversion repository.
+#
+# OPTIONS
+#   SRC                 - reference to a Fcm::CmUrl object.
+#   TYPE                - Specify the branch type. See help in "fcm branch" for
+#                         further information.
+#   NAME                - specify the name of the branch.
+#   NON_INTERACTIVE     - Do no interactive prompting, set SVN_NON_INTERACTIVE
+#                         to true automatically.
+#   PASSWORD            - specify the password for commit access.
+#   REV                 - specify the operative revision of the source.
+#   REV_FLAG            - A flag to specify the behaviour of the prefix to the
+#                         branch name. See help in "fcm branch" for further
+#                         information.
+#   SVN_NON_INTERACTIVE - Do no interactive prompting when running svn commit,
+#                         etc. This option is implied by NON_INTERACTIVE.
+#   TICKET              - Specify one or more related tickets for the branch.
+# ------------------------------------------------------------------------------
+
+sub create {
+  my $self = shift;
+  my %args = @_;
+
+  # Options
+  # ----------------------------------------------------------------------------
+  # Compulsory options
+  my $src  = $args{SRC};
+  my $type = $args{TYPE};
+  my $name = $args{NAME};
+
+  # Other options
+  my $rev_flag        = $args{REV_FLAG}        ? $args{REV_FLAG}    : 'NORMAL';
+  my @tickets         = exists $args{TICKET}   ? @{ $args{TICKET} } : ();
+  my $password        = exists $args{PASSWORD} ? $args{PASSWORD}    : undef;
+  my $orev            = exists $args{REV}      ? $args{REV}         : 'HEAD';
+
+  my $non_interactive     = exists $args{NON_INTERACTIVE}
+                            ? $args{NON_INTERACTIVE} : 0;
+  my $svn_non_interactive = exists $args{SVN_NON_INTERACTIVE}
+                            ? $args{SVN_NON_INTERACTIVE} : 0;
+  $svn_non_interactive    = $non_interactive ? 1 : $svn_non_interactive;
+
+  # Analyse the source URL
+  # ----------------------------------------------------------------------------
+  # Create branch from the trunk by default
+  $src->branch ('trunk') if not $src->branch;
+
+  # Remove "sub-directory" part from source URL
+  $src->subdir ('')      if $src->subdir;
+
+  # Remove "peg revision" part because it does not work with "svn copy"
+  $src->pegrev ('')      if $src->pegrev;
+
+  # Find out the URL and the last changed revision of the specified URL at the
+  # specified operative revision
+  my $url = $src->svninfo (FLAG => 'URL', REV => $orev);
+  e_report $src->url, ': cannot determine the operative URL at revision ',
+           $orev, ', abort.' if not $url;
+
+  $src->url ($url) if $url ne $src->url;
+
+  my $rev = $src->svninfo (FLAG => 'Last Changed Rev', REV => $orev);
+  e_report $src->url, ': cannot determine the last changed rev at revision',
+           $orev, ', abort.' if not $rev;
+
+  # Warn user if last changed revision is not the specified revision
+  w_report 'Warning: branch will be created from revision ', $rev,
+           ', i.e. the last changed rev.'
+    unless $orev and $orev eq $rev;
+
+  # Determine the sub-directory names of the branch
+  # ----------------------------------------------------------------------------
+  my @branch_dirs = ('branches');
+
+  # Split branch type flags into a hash table
+  my %type_flags = ();
+  $type_flags{$_} = 1 for ((split /$Fcm::Config::DELIMITER/, $type));
+
+  # Branch sub-directory 1, development, test or package
+  for my $flag (qw/DEV TEST PKG/) {
+    if (exists $type_flags{$flag}) {
+      push @branch_dirs, lc ($flag);
+      last;
+    }
+  }
+
+  # Branch sub-directory 2, user, share, configuration or release
+  if (exists $type_flags{USER}) {
+    die 'Unable to determine your user ID, abort' unless $self->config->user_id;
+
+    push @branch_dirs, $self->config->user_id;
+
+  } else {
+    for my $flag (keys %Fcm::CmUrl::owner_keywords) {
+      if (exists $type_flags{uc ($flag)}) {
+        push @branch_dirs, $flag;
+        last;
+      }
+    }
+  }
+
+  # Branch sub-directory 3, branch name
+  # Prefix branch name with revision number/keyword if necessary
+  my $prefix = '';
+  if ($rev_flag ne 'NONE') {
+    $prefix = $rev;
+
+    # Attempt to replace revision number with a revision keyword if necessary
+    if ($rev_flag eq 'NORMAL') {
+      $prefix = (Fcm::Keyword::unexpand($src->url_peg(), $rev))[1];
+    }
+
+    # $prefix is still a revision number, add "r" in front of it
+    $prefix = 'r' . $prefix if $prefix eq $rev;
+
+    # Add an underscore before the branch name
+    $prefix.= '_';
+  }
+
+  # Branch name
+  push @branch_dirs, $prefix . $name;
+
+  # Check whether the branch already exists, fail if so
+  # ----------------------------------------------------------------------------
+  # Construct the URL of the branch
+  $self->project_url ($src->project_url);
+  $self->branch  (join ('/', @branch_dirs));
+
+  # Check that branch does not already exists
+  e_report $self->url, ': branch already exists, abort.' if $self->url_exists;
+
+  # Message for the commit log
+  # ----------------------------------------------------------------------------
+  my @message = ('Created ' . $self->branch_path .  ' from ' .
+                 $src->branch_path . '@' . $rev . '.' . "\n");
+
+  # Add related Trac ticket links to commit log if set
+  if (@tickets) {
+    my $ticket_mesg = 'Relates to ticket' . (@tickets > 1 ? 's' : '');
+
+    while (my $ticket = shift @tickets) {
+      $ticket_mesg .= ' #' . $ticket;
+      $ticket_mesg .= (@tickets > 1 ? ',' : ' and') if @tickets >= 1;
+    }
+
+    push @message, $ticket_mesg . ".\n";
+  }
+
+  # Create a temporary file for the commit log message
+  my $ci_mesg = Fcm::CmCommitMessage->new;
+  $ci_mesg->auto_mesg (\@message);
+  $ci_mesg->ignore_mesg (['A' . ' ' x 4 . $self->url . "\n"]);
+  my $logfile = $ci_mesg->edit_file (TEMP => 1, BATCH => $non_interactive);
+
+  # Check with the user to see if he/she wants to go ahead
+  # ----------------------------------------------------------------------------
+  if (not $non_interactive) {
+    my $reply = Fcm::Interactive::get_input(
+      title   => 'fcm branch',
+      message => 'Would you like to go ahead and create this branch?',
+      type    => 'yn',
+      default => 'n',
+    );
+
+    return unless $reply eq 'y';
+  }
+
+  # Ensure existence of container sub-directories of the branch
+  # ----------------------------------------------------------------------------
+  for my $i (0 .. $#branch_dirs - 1) {
+    my $subdir     = join ('/', @branch_dirs[0 .. $i]);
+    my $subdir_url = Fcm::CmUrl->new (URL => $src->project_url . '/' . $subdir);
+
+    # Check whether each sub-directory of the branch already exists,
+    # if sub-directory does not exist, create it
+    next if $subdir_url->url_exists;
+
+    print 'Creating sub-directory: ', $subdir, "\n";
+
+    my @command = (
+      qw/svn mkdir/,
+      '-m', 'Created ' . $subdir . ' directory.',
+      ($svn_non_interactive  ? '--non-interactive'       : ()),
+      (defined $password     ? ('--password', $password) : ()),
+
+      $subdir_url->url,
+    );
+    &run_command (\@command);
+  }
+
+  # Create the branch
+  # ----------------------------------------------------------------------------
+  {
+    print 'Creating branch ', $self->url, ' ...', "\n";
+    my @command = (
+      qw/svn copy/,
+      '-r', $rev,
+      '-F', $logfile,
+      ($svn_non_interactive  ? '--non-interactive'       : ()),
+      (defined $password     ? ('--password', $password) : ()),
+
+      $src->url, $self->url,
+    );
+    &run_command (\@command);
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch->delete (
+#     [NON_INTERACTIVE     => 1,]
+#     [PASSWORD            => $password,]
+#     [SVN_NON_INTERACTIVE => 1,]
+#   );
+#
+# DESCRIPTION
+#   This method deletes the current branch from the Subversion repository.
+#
+# OPTIONS
+#   NON_INTERACTIVE     - Do no interactive prompting, set SVN_NON_INTERACTIVE
+#                         to true automatically.
+#   PASSWORD            - specify the password for commit access.
+#   SVN_NON_INTERACTIVE - Do no interactive prompting when running svn commit,
+#                         etc. This option is implied by NON_INTERACTIVE.
+# ------------------------------------------------------------------------------
+
+sub del {
+  my $self = shift;
+  my %args = @_;
+
+  # Options
+  # ----------------------------------------------------------------------------
+  my $password            = exists $args{PASSWORD} ? $args{PASSWORD} : undef;
+  my $non_interactive     = exists $args{NON_INTERACTIVE}
+                            ? $args{NON_INTERACTIVE} : 0;
+  my $svn_non_interactive = exists $args{SVN_NON_INTERACTIVE}
+                            ? $args{SVN_NON_INTERACTIVE} : 0;
+  $svn_non_interactive    = $non_interactive ? 1 : $svn_non_interactive;
+
+  # Ensure URL is a branch
+  # ----------------------------------------------------------------------------
+  e_report $self->url_peg, ': not a branch, abort.' if not $self->is_branch;
+
+  # Message for the commit log
+  # ----------------------------------------------------------------------------
+  my @message = ('Deleted ' . $self->branch_path . '.' . "\n");
+
+  # Create a temporary file for the commit log message
+  my $ci_mesg = Fcm::CmCommitMessage->new;
+  $ci_mesg->auto_mesg (\@message);
+  $ci_mesg->ignore_mesg (['D' . ' ' x 4 . $self->url . "\n"]);
+  my $logfile = $ci_mesg->edit_file (TEMP => 1, BATCH => $non_interactive);
+
+  # Check with the user to see if he/she wants to go ahead
+  # ----------------------------------------------------------------------------
+  if (not $non_interactive) {
+    my $mesg = '';
+    my $user = $self->config->user_id;
+
+    if ($user and $self->branch_owner ne $user) {
+      $mesg .= "\n";
+
+      if (exists $Fcm::CmUrl::owner_keywords{$self->branch_owner}) {
+        my $type = $Fcm::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 = Fcm::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";
+  my @command = (
+    qw/svn delete/,
+    '-F', $logfile,
+    (defined $password    ? ('--password', $password) : ()),
+    ($svn_non_interactive ? '--non-interactive'       : ()),
+
+    $self->url,
+  );
+  &run_command (\@command);
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch->display_info (
+#     [SHOW_CHILDREN => 1],
+#     [SHOW_OTHER    => 1]
+#     [SHOW_SIBLINGS => 1]
+#   );
+#
+# DESCRIPTION
+#   This method displays information of the current branch. If SHOW_CHILDREN is
+#   set, it shows information of all current children branches of the current
+#   branch. If SHOW_SIBLINGS is set, it shows information of siblings that have
+#   been merged recently with the current branch. If SHOW_OTHER is set, it shows
+#   information of custom/reverse merges.
+# ------------------------------------------------------------------------------
+
+sub display_info {
+  my $self = shift;
+  my %args = @_;
+
+  # Arguments
+  # ----------------------------------------------------------------------------
+  my $show_children = exists $args{SHOW_CHILDREN} ? $args{SHOW_CHILDREN} : 0;
+  my $show_other    = exists $args{SHOW_OTHER   } ? $args{SHOW_OTHER}    : 0;
+  my $show_siblings = exists $args{SHOW_SIBLINGS} ? $args{SHOW_SIBLINGS} : 0;
+
+  # Useful variables
+  # ----------------------------------------------------------------------------
+  my $separator  = '-' x 80 . "\n";
+  my $separator2 = '  ' . '-' x 78 . "\n";
+
+  # Print "info" as returned by "svn info"
+  # ----------------------------------------------------------------------------
+  for my $key ('URL', 'Repository Root', 'Revision', 'Last Changed Author',
+               'Last Changed Rev', 'Last Changed Date') {
+    print $key, ': ', $self->svninfo (FLAG => $key), "\n"
+      if $self->svninfo (FLAG => $key);
+  }
+
+  if ($self->config->verbose) {
+    # Verbose mode, print log message at last changed revision
+    my %log = $self->svnlog (REV => $self->svninfo (FLAG => 'Last Changed Rev'));
+    my @log = split /\n/, $log{msg};
+    print 'Last Changed Log:', "\n\n", map ({'  ' . $_ . "\n"} @log), "\n";
+  }
+
+  if ($self->is_branch) {
+    # Print create information
+    # --------------------------------------------------------------------------
+    my %log = $self->svnlog (REV => $self->create_rev);
+
+    print $separator;
+    print 'Branch Create Author: ', $log{author}, "\n" if $log{author};
+    print 'Branch Create Rev: ', $self->create_rev, "\n";
+    print 'Branch Create Date: ', &svn_date ($log{date}), "\n";
+
+    if ($self->config->verbose) {
+      # Verbose mode, print log message at last create revision
+      my @log = split /\n/, $log{msg};
+      print 'Branch Create Log:', "\n\n", map ({'  ' . $_ . "\n"} @log), "\n";
+    }
+
+    # Print delete information if branch no longer exists
+    # --------------------------------------------------------------------------
+    print 'Branch Delete Rev: ', $self->delete_rev, "\n" if $self->delete_rev;
+
+    # Report merges into/from the parent
+    # --------------------------------------------------------------------------
+    # Print the URL@REV of the parent branch
+    print $separator, 'Branch Parent: ', $self->parent->url_peg, "\n";
+
+    # Set up a new object for the parent at the current revision
+    # --------------------------------------------------------------------------
+    my $p_url  = $self->parent->url;
+    $p_url    .= '@' . $self->pegrev if $self->pegrev;
+    my $parent = Fcm::CmBranch->new (URL => $p_url);
+
+    if (not $parent->url_exists) {
+      print 'Branch parent deleted.', "\n";
+      return;
+    }
+
+    # Report merges into/from the parent
+    # --------------------------------------------------------------------------
+    print $self->_report_merges ($parent, 'Parent');
+  }
+
+  # Report merges with siblings
+  # ----------------------------------------------------------------------------
+  if ($show_siblings) {
+    # Report number of sibling branches found
+    print $separator, '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 (keys %avail) {
+      next unless @{ $avail{$key} };
+
+      $return .= $indent . $key . "\n";
+
+      my $s = ($key =~ /From/) ? $branch: $self;
+
+      for my $rev (@{ $avail{$key} }) {
+        $return .= $separator . $s->display_svnlog ($rev);
+      }
+    }
+
+  } else {
+    # Normal mode, print only the revisions
+    for my $key (keys %avail) {
+      next unless @{ $avail{$key} };
+
+      $return .= $indent . $key . ' ' . join (' ', @{ $avail{$key} }) . "\n";
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/CmCommitMessage.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CmCommitMessage.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CmCommitMessage.pm	(revision 1981)
@@ -0,0 +1,319 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CmCommitMessage
+#
+# DESCRIPTION
+#   This class contains methods to read, write and edit the commit message file
+#   in a working copy.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CmCommitMessage;
+@ISA = qw(Fcm::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use Cwd;
+use File::Spec;
+use File::Temp qw/tempfile/;
+
+# FCM component modules
+use Fcm::Base;
+use Fcm::Util qw/e_report run_command/;
+
+# List of property methods for this class
+my @scalar_properties = (
+  'auto_mesg',   # the automatically inserted part of a commit message
+  'base',        # the base name of the commit message file
+  'dir',         # the directory container of the commit message file
+  'ignore_mesg', # the ignored part of a commit message
+  'user_mesg',   # the user defined part of a commit message
+);
+
+# Commit log delimiter messages
+my $log_delimiter        = '--Add your commit message ABOVE - ' .
+                           'do not alter this line or those below--';
+my $auto_delimiter       = '--FCM message (will be inserted automatically)--';
+my $auto_delimiter_old   = '--This line will be ignored and those below ' .
+                           'will be inserted automatically--';
+my $status_delimiter     = '--Change summary ' .
+                           '(not part of commit message)--';
+my $status_delimiter_old = '--This line, and those below, will be ignored--';
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::CmCommitMessage->new ();
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::CmCommitMessage class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::Base->new (%args);
+
+  $self->{$_} = undef for (@scalar_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];
+    }
+
+    # Default value for property
+    if (not defined $self->{$name}) {
+      if ($name eq 'base') {
+        # Reference to an array
+        $self->{$name} = '#commit_message#';
+
+      } elsif ($name eq 'dir') {
+        # Current working directory
+        $self->{$name} = &cwd ();
+
+      } elsif ($name =~ /_mesg$/) {
+        # Reference to an array
+        $self->{$name} = [];
+      }
+    }
+
+    return $self->{$name};
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $file = $obj->file;
+#   $obj->file ($file);
+#
+# DESCRIPTION
+#   This method returns the full name of the commit message file. If an
+#   argument is specified, the file is reset using the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub file {
+  my ($self, $file) = @_;
+
+  if ($file) {
+    $self->dir  (dirname  ($file));
+    $self->base (basename ($file));
+  }
+
+  return File::Spec->catfile ($self->dir, $self->base);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($user, $auto) = $obj->read_file ();
+#
+# DESCRIPTION
+#   This function reads from the commit log message file. It resets the user
+#   and the automatic messages after reading the file. It returns the message
+#   back in two array references.
+# ------------------------------------------------------------------------------
+
+sub read_file {
+  my $self = shift;
+
+  my @user = ();
+  my @auto = ();
+  my $file = $self->file;
+
+  if (-r $file) {
+    open FILE, '<', $file or croak 'Cannot open ', $file, '(', $!, '), abort';
+
+    my $in_auto = 0;
+    while (<FILE>) {
+
+      next if (index ($_, $log_delimiter) == 0); 
+
+      if (index ($_, $status_delimiter)     == 0 ||
+          index ($_, $status_delimiter_old) == 0) {
+        # Ignore after the ignore delimiter
+        last;
+      }
+
+      if (index ($_, $auto_delimiter)     == 0 ||
+          index ($_, $auto_delimiter_old) == 0) {
+        # Beginning of the automatically inserted message
+        $in_auto = 1;
+        next;
+      }
+
+      if ($in_auto) {
+        push @auto, $_;
+
+      } else {
+        push @user, $_;
+      }
+    }
+
+    close FILE;
+
+    $self->user_mesg (\@user);
+    $self->auto_mesg (\@auto);
+  }
+
+  return (\@user, \@auto);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj->write_file ();
+#
+# DESCRIPTION
+#   This function writes to the commit log message file based on the content of
+#   the user defined message, and the automatically inserted message.
+# ------------------------------------------------------------------------------
+
+sub write_file {
+  my $self = shift;
+  my %args = @_;
+
+  my @user = @{ $self->user_mesg };
+  my @auto = @{ $self->auto_mesg };
+  my $file = $self->file;
+
+  open FILE, '>', $file or die 'Cannot open ', $file, '(', $!, '), abort';
+  print FILE @user;
+  print FILE $log_delimiter, "\n", $auto_delimiter, "\n", @auto if @auto;
+  close FILE or croak 'Cannot close ', $file, '(', $!, '), abort';
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $file = $obj->edit_file ([TEMP => 1,] [BATCH => 1,]);
+#
+# DESCRIPTION
+#   This function normally triggers an editor for editing the commit message.
+#   If TEMP is set, it edits a temporary file. Otherwise, it edits the current
+#   commit message file. It resets the user defined message on success. Returns
+#   the name of the commit log file. Do not start the editor if BATCH is set.
+# ------------------------------------------------------------------------------
+
+sub edit_file {
+  my $self  = shift;
+  my %args  = @_;
+  my $temp  = exists $args{TEMP}  ? $args{TEMP}  : 0;
+  my $batch = exists $args{BATCH} ? $args{BATCH} : 0;
+
+  my @user   = @{ $self->user_mesg };
+  my @auto   = @{ $self->auto_mesg };
+  my @ignore = @{ $self->ignore_mesg };
+  my $file   = $self->file;
+
+  if ($temp) {
+    my $fh;
+    ($fh, $file) = tempfile (SUFFIX => ".fcm", UNLINK => 1);
+    close $fh;
+  }
+
+  # Add original or code driven message and status information to the file
+  my $select = select;
+  open FILE, '>', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
+  select FILE;
+
+  print @user;
+  print (@auto || @user ? '' : "\n");
+  print $log_delimiter, "\n";
+  print $auto_delimiter, "\n", @auto, "\n" if @auto;
+  print $status_delimiter, "\n\n";
+  print @ignore if @ignore;
+
+  close FILE or die 'Cannot close ', $file, ' (', $!, '), abort';
+  select $select;
+
+  if (not $batch) {
+    # Select editor
+    my $editor = 'nedit';
+    
+    if ($ENV{'SVN_EDITOR'}) {
+      $editor = $ENV{'SVN_EDITOR'};
+
+    } elsif ($ENV{'VISUAL'}) {
+      $editor = $ENV{'VISUAL'};
+
+    } elsif ($ENV{'EDITOR'}) {
+      $editor = $ENV{'EDITOR'};
+    }
+
+    # Execute command to start the editor
+    print 'Starting ', $editor, ' to edit commit message ...', "\n";
+    &run_command ([split (/\s+/, $editor), $file]);
+  }
+
+  # Read the edited file, and extract user log message from it
+  open FILE, '<', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
+  my (@log);
+  my $delimiter_found = 0;
+
+  while (<FILE>) {
+    if (index ($_, $log_delimiter) == 0) {
+      $delimiter_found = 1;
+      last;
+    }
+    push @log, $_;
+  }
+
+  close FILE;
+
+  # Ensure log delimiter line was not altered
+  e_report 'Error: the line "', $log_delimiter, '" has been altered, abort.'
+    if not $delimiter_found;
+
+  # Check for empty commit log
+  e_report 'Error: log message unchanged or not specified, abort.'
+    if join (' ', (@log, @auto)) =~ /^\s*$/;
+
+  # Echo the commit message to standard output
+  my $separator = '-' x 80 . "\n";
+  print 'Change summary:', "\n";
+  print $separator, @ignore, $separator;
+  print 'Commit message is as follows:', "\n";
+  print $separator, @log, @auto, $separator;
+
+  open FILE, '>', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
+  print FILE @log, @auto;
+  close FILE or croak 'Cannot close ', $file, ' (', $!, '), abort';
+
+  # Reset the array for the user specified log message
+  $self->user_mesg (\@log);
+
+  return $file;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/CmUrl.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/CmUrl.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/CmUrl.pm	(revision 1981)
@@ -0,0 +1,1149 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CmUrl
+#
+# DESCRIPTION
+#   This class contains methods for manipulating a Subversion URL in a standard
+#   FCM project.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CmUrl;
+@ISA = qw(Fcm::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use HTTP::Date;
+use XML::DOM;
+
+# FCM component modules
+use Fcm::Base;
+use Fcm::Keyword;
+use Fcm::Util qw/run_command svn_date/;
+
+# Special branches
+our %owner_keywords = (Share => 'shared', Config => 'config', Rel => 'release');
+
+# Revision pattern
+my $rev_pattern = '\d+|HEAD|BASE|COMMITTED|PREV|\{.+\}';
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_url = Fcm::CmUrl->new ([URL => $url,]);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::CmUrl class.
+#
+# ARGUMENTS
+#   URL - URL of a branch
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::Base->new (%args);
+
+  $self->{URL} = (exists $args{URL} ? $args{URL} : '');
+
+  for (qw/ANALYSED BRANCH BRANCH_LIST INFO LIST LOG LOG_RANGE PEGREV RLIST
+          PROJECT SUBDIR/) {
+    $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/ANALYSED RLIST LIST INFO LOG LOG_RANGE/);
+    }
+  }
+
+  return $self->{URL};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_url ();
+#
+# DESCRIPTION
+#   Returns true if current url is a valid Subversion URL.
+# ------------------------------------------------------------------------------
+
+sub is_url {
+  my $self = shift;
+
+  # This should handle URL beginning with svn://, http:// and svn+ssh://
+  return ($self->url_peg =~ m#^[\+\w]+://#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->url_exists ([$rev]);
+#
+# DESCRIPTION
+#   Returns true if current url exists (at operative revision $rev) in a
+#   Subversion repository.
+# ------------------------------------------------------------------------------
+
+sub url_exists {
+  my ($self, $rev) = @_;
+
+  my $exists = $self->svnlist (REV => $rev);
+
+  return defined ($exists);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $cm_url->svninfo ([FLAG => $flag], [REV => $rev]);
+#
+# DESCRIPTION
+#   Returns the value of $flag, where $flag is a field returned by "svn info".
+#   (If $flag is not set, default to "URL".) Otherwise returns an empty string.
+#   If REV is specified, it will be used as the operative revision.
+# ------------------------------------------------------------------------------
+
+sub svninfo {
+  my $self = shift;
+  my %args = @_;
+
+  my $flag = exists $args{FLAG} ? $args{FLAG} : 'URL';
+  my $rev  = exists $args{REV}  ? $args{REV}  : undef;
+
+  $rev = ($self->pegrev ? $self->pegrev : 'HEAD') if not $rev;
+
+  return if not $self->is_url;
+
+  # Get "info" for the specified revision if necessary
+  if (not exists $self->{INFO}{$rev}) {
+    # Invoke "svn info" command
+    my @info = &run_command (
+      [qw/svn info -r/, $rev, $self->url_peg],
+      PRINT   => $self->config->verbose > 2,
+      METHOD  => 'qx',
+      DEVNULL => 1,
+      ERROR   => 'ignore',
+    );
+
+    # Store selected information
+    for (@info) {
+      chomp;
+
+      if (/^(.+?):\s*(.+)$/) {
+        $self->{INFO}{$rev}{$1} = $2;
+      }
+    }
+  }
+
+  my $return = exists $self->{INFO}{$rev}{$flag}
+               ? $self->{INFO}{$rev}{$flag} : undef;
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %logs = $cm_url->svnlog (
+#     [REV          => $rev,]
+#     [REV          => \@revs,] # reference to a 2-element array
+#     [STOP_ON_COPY => 1,]
+#   );
+#
+# DESCRIPTION
+#   Returns the logs for the current URL. If REV is a range of revisions or not
+#   specified, return a hash where the keys are revision numbers and the values
+#   are the entries (which are hash references). If a single REV is specified,
+#   return the entry (a hash reference) at the specified REV. Each entry in the
+#   returned list is a hash reference, with the following structure:
+#
+#   $entry = {
+#     author => $author,              # the commit author
+#     date   => $date,                # the commit date (in seconds since epoch)
+#     msg    => $msg,                 # the log message
+#     paths  => {                     # list of changed paths
+#       $path1  => {                  # a changed path
+#         copyfrom-path => $frompath, # copy-from-path
+#         copyfrom-rev  => $fromrev,  # copy-from-revision
+#         action        => $action,   # action status code
+#       },
+#       ...     => { ... },           # ... more changed paths ...
+#     },
+#   }
+# ------------------------------------------------------------------------------
+
+sub svnlog {
+  my $self = shift;
+  my %args = @_;
+
+  my $stop_on_copy  = exists $args{STOP_ON_COPY} ? $args{STOP_ON_COPY} : 0;
+  my $rev_arg       = exists $args{REV}          ? $args{REV}          : 0;
+
+  my @revs;
+
+  # Get revision options
+  # ----------------------------------------------------------------------------
+  if ($rev_arg) {
+    if (ref ($rev_arg)) {
+      # Revsion option is an array, a range of revisions specified?
+      ($revs [0], $revs [1]) = @$rev_arg;
+
+    } else {
+      # A single revision specified
+      $revs [0] = $rev_arg;
+    }
+
+    # Expand 'HEAD' revision
+    for my $rev (@revs) {
+      next unless uc ($rev) eq 'HEAD';
+      $rev = $self->svninfo (FLAG => 'Revision', REV => 'HEAD');
+    }
+
+  } else {
+    # No revision option specified, get log for all revisions
+    $revs [0] = $self->svninfo (FLAG => 'Revision');
+    $revs [1] = 1;
+  }
+
+  $revs [1] = $revs [0] if not $revs [1];
+  @revs     = sort {$b <=> $a} @revs;
+
+  # Check whether a "svn log" run is necessary
+  # ----------------------------------------------------------------------------
+  my $need_update = ! ($revs [0] == $revs [1] and exists $self->{LOG}{$revs [0]});
+  my @ranges      = @revs;
+  if ($need_update and $self->{LOG_RANGE}) {
+    my %log_range = %{ $self->{LOG_RANGE} };
+
+    if ($stop_on_copy) {
+      $ranges [1] = $log_range{UPPER} if $ranges [1] >= $log_range{LOWER_SOC};
+
+    } else {
+      $ranges [1] = $log_range{UPPER} if $ranges [1] >= $log_range{LOWER};
+    }
+  }
+
+  $need_update = 0 if $ranges [0] < $ranges [1];
+
+  if ($need_update) {
+    # Invoke "svn log" command for all revisions of the current branch
+    # --------------------------------------------------------------------------
+    my @command = (
+      qw/svn log --xml -v/, ($stop_on_copy ? '--stop-on-copy' : ()),
+      '-r' . join (':', @ranges),
+      $self->url_peg,
+    );
+
+    my $rc;
+    my @xml = &run_command (
+      \@command,
+      PRINT   => $self->config->verbose > 2,
+      METHOD  => 'qx',
+      DEVNULL => 1,
+      ERROR   => 'ignore',
+      RC      => \$rc,
+    );
+
+    # Parse the XML
+    # --------------------------------------------------------------------------
+    if (not $rc) {
+      my $parser = XML::DOM::Parser->new;
+      my $doc    = $parser->parse (join ('', @xml));
+
+      my $entry_list = $doc->getElementsByTagName ('logentry');
+
+      # Record the author, date, message and path change for each revision
+      for my $i (0 .. $entry_list->getLength - 1) {
+        # Select current entry from node list
+        my $entry = $entry_list->item ($i);
+        my %this = ();
+
+        # Revision is an attribute of the entry node
+        my $rev   = $entry->getAttributeNode ('revision')->getValue;
+
+        # Author, date and log message are children elements of the entry node
+        for my $key (qw/author date msg/) {
+          # Get data of each node, also convert date to seconds since epoch
+          my $node    = $entry->getElementsByTagName ($key)->item (0);
+          my $data    = ($node and $node->getFirstChild)
+                        ? $node->getFirstChild->getData : '';
+          $this{$key} = ($key eq 'date' ? str2time ($data) : $data);
+        }
+
+        # Path nodes are grand children elements of the entry node
+        my $paths = $entry->getElementsByTagName ('path');
+
+        for my $p (0 .. $paths->getLength - 1) {
+          # Select current path node from node list
+          my $node = $paths->item ($p);
+
+          # Get data from the path node
+          my $path = $node->getFirstChild->getData;
+          $this{paths}{$path} = {};
+
+          # Action, copyfrom-path and copyfrom-rev are attributes of path nodes
+          for my $key (qw/action copyfrom-path copyfrom-rev/) {
+            next unless $node->getAttributeNode ($key); # ensure attribute exists
+
+            $this{paths}{$path}{$key} = $node->getAttributeNode ($key)->getValue;
+          }
+        }
+
+        $self->{LOG}{$rev} = \%this;
+      }
+    }
+
+    # Update the range cache
+    # --------------------------------------------------------------------------
+    # Upper end of the range
+    $self->{LOG_RANGE}{UPPER} = $ranges [0]
+      if ! $self->{LOG_RANGE}{UPPER} or $ranges [0] > $self->{LOG_RANGE}{UPPER};
+
+    # Lower end of the range, need to take into account the stop-on-copy option
+    if ($stop_on_copy) {
+      # Lower end of the range with stop-on-copy option
+      $self->{LOG_RANGE}{LOWER_SOC} = $ranges [1]
+        if ! $self->{LOG_RANGE}{LOWER_SOC} or
+           $ranges [1] < $self->{LOG_RANGE}{LOWER_SOC};
+
+      my $low = (sort {$a <=> $b} keys %{ $self->{LOG} }) [0];
+      $self->{LOG_RANGE}{LOWER} = $low
+        if ! $self->{LOG_RANGE}{LOWER} or $low < $self->{LOG_RANGE}{LOWER};
+
+    } else {
+      # Lower end of the range without the stop-on-copy option
+      $self->{LOG_RANGE}{LOWER} = $ranges [1]
+        if ! $self->{LOG_RANGE}{LOWER} or
+           $ranges [1] < $self->{LOG_RANGE}{LOWER};
+
+      $self->{LOG_RANGE}{LOWER_SOC} = $ranges [1]
+        if ! $self->{LOG_RANGE}{LOWER_SOC} or
+           $ranges [1] < $self->{LOG_RANGE}{LOWER_SOC};
+    }
+  }
+
+  my %return = ();
+
+  if (! $rev_arg or ref ($rev_arg)) {
+    # REV is an array, return log entries if they are within range
+    for my $rev (sort {$b <=> $a} keys %{ $self->{LOG} }) {
+      next if $rev > $revs [0] or $revs [1] > $rev;
+
+      $return{$rev} = $self->{LOG}{$rev};
+
+      if ($stop_on_copy) {
+        last if exists $self->{LOG}{$rev}{paths}{$self->branch_path} and
+           $self->{LOG}{$rev}{paths}{$self->branch_path}{action} eq 'A';
+      }
+    }
+
+  } else {
+    # REV is a scalar, return log of the specified revision if it exists
+    %return = %{ $self->{LOG}{$revs [0]} } if exists $self->{LOG}{$revs [0]};
+  }
+
+  return %return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $cm_branch->display_svnlog ($rev, [$wiki]);
+#
+# DESCRIPTION
+#   This method returns a string for displaying the log of the current branch
+#   at a $rev. If $wiki is set, returns a string for displaying in a Trac wiki
+#   table.  The value of $wiki should be the Subversion URL of a FCM project
+#   associated with the intended Trac system.
+# ------------------------------------------------------------------------------
+
+sub display_svnlog {
+  my ($self, $rev, $wiki) = @_;
+  my $return = '';
+
+  my %log = $self->svnlog (REV => $rev);
+
+  if ($wiki) {
+    # Output in Trac wiki format
+    # --------------------------------------------------------------------------
+    $return .= '|| ' . &svn_date ($log{date}) . ' || ' . $log{author} . ' || ';
+
+    my $trac_url = Fcm::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 =~ m#^$wiki(?:/*|$)#) {
+      # 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->svnlist ([REV => $rev], [RECURSIVE => 1]);
+#
+# DESCRIPTION
+#   The method returns a list of paths as returned by "svn list". If RECURSIVE
+#   is set, "svn list" is invoked with the "-R" option.
+# ------------------------------------------------------------------------------
+
+sub svnlist {
+  my $self = shift;
+  my %args = @_;
+
+  my $recursive = exists $args{RECURSIVE} ? $args{RECURSIVE} : 0;
+  my $rev       = exists $args{REV}       ? $args{REV}       : undef;
+  my $key       = $recursive ? 'RLIST' : 'LIST';
+
+  # Find out last changed revision of the current URL
+  $rev = $self->svninfo (FLAG => 'Last Changed Rev', REV => $rev);
+  return () if not $rev;
+
+  # Get directory listing for the current URL at the last changed revision
+  if (not exists $self->{$key}{$rev}) {
+    my $rc;
+
+    my @list = map {chomp; $_} &run_command (
+      [qw/svn list -r/, $rev, ($recursive ? '-R' : ()), $self->url_peg],
+      METHOD => 'qx', ERROR => 'ignore', DEVNULL => 1, RC => \$rc,
+    );
+
+    $self->{$key}{$rev} = $rc ? undef : \@list;
+  }
+
+  return (defined ($self->{$key}{$rev}) ? @{ $self->{$key}{$rev} } : undef);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @list = $cm_url->branch_list ($rev);
+#
+# DESCRIPTION
+#   The method returns a list of branches in the current project, assuming the
+#   FCM naming convention. If $rev if specified, it returns the list of
+#   branches at $rev.
+# ------------------------------------------------------------------------------
+
+sub branch_list {
+  my ($self, $rev) = @_;
+
+  # Current URL must be a valid FCM project
+  return if not $self->project;
+
+  # Find out last changed revision of the current URL
+  $rev = $self->svninfo (FLAG => 'Revision', REV => $rev);
+  return () if not $rev;
+
+  if (not exists $self->{BRANCH_LIST}{$rev}) {
+    $self->{BRANCH_LIST}{$rev} = [];
+
+    # Get URL of the project "branches/" sub-directory
+    my $url = Fcm::CmUrl->new (URL => $self->project_url . '/branches');
+
+    # List three levels underneath "branches/"
+    # First level, i.e. dev, test, pkg, etc
+    my @list1 = map {$url->url . '/' . $_} $url->svnlist (REV => $rev);
+    @list1    = grep m#/$#, @list1;
+
+    # Second level, i.e. user name, Shared, Rel or Config
+    my @list2;
+    for (@list1) {
+      my $u    = Fcm::CmUrl->new (URL => $_);
+      my @list = $u->svnlist (REV => $rev);
+
+      push @list2, map {$u->url . $_} @list;
+    }
+
+    # Third level, branch name
+    for (@list2) {
+      my $u    = Fcm::CmUrl->new (URL => $_);
+      my @list = map {s#/*$##; $_} $u->svnlist (REV => $rev);
+
+      push @{ $self->{BRANCH_LIST}{$rev} }, map {$u->url . $_} @list;
+    }
+  }
+
+  return @{ $self->{BRANCH_LIST}{$rev} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_analyse_url ();
+#
+# DESCRIPTION
+#   The method analyses the current URL, breaking it up into the project
+#   (substring of URL up to the slash before "trunk", "branches" or "tags"),
+#   branch name ("trunk", "branches/<type>/<id>/<name>" or "tags/<name>") and
+#   the sub-directory below the top of the project sub-tree. It re-sets the
+#   corresponding interal variables.
+# ------------------------------------------------------------------------------
+
+sub _analyse_url {
+  my $self = shift;
+  my ($url, $project, $branch, $subdir, $pegrev);
+
+  # Check that URL is set
+  $url    = $self->url_peg;
+  return if not $url;
+  return if not $self->is_url;
+
+  # Extract from URL the peg revision
+  $pegrev = $1 if $url =~ s/@($rev_pattern)$//i;
+
+  if ($url =~ m#^(.*?)/+(trunk|branches|tags)(?:/+(.*))?/*$#) {
+    # URL is under the "trunk", a branch or a tag
+    $project                 = $1;
+    my ($branch_id, $remain) = ($2, $3);
+
+    $remain = '' if not defined $remain;
+
+    if ($branch_id eq 'trunk') {
+      # URL under the "trunk"
+      $branch = 'trunk';
+
+    } else {
+      # URL under a branch or a tag
+      $branch = $branch_id;
+
+      # Assume "3 sub-directories", FCM branch naming convention
+      for (1 .. 3) {
+        if ($remain =~ s#^([^/]+)(?:/+|$)##) {
+          $branch .=  '/' . $1;
+
+        } else {
+          $branch = undef;
+          last;
+        }
+      }
+    }
+
+    $subdir = $remain ? $remain : '' if $branch;
+
+  } else {
+    # URL is at some level above the "trunk", a branch or a tag
+    # Use "svn ls" to determine whether it is a project URL
+    my @list = $self->svnlist (REV => ($pegrev ? $pegrev : 'HEAD'));
+    my %lines = map {chomp $_; ($_, 1)} @list;
+
+    # A project URL should have the "trunk", "branches" and "tags" directories
+    ($project = $url) =~ s#/*$##
+      if $lines{'trunk/'} and $lines{'branches/'} and $lines{'tags/'};
+  }
+
+  $self->{PROJECT}  = $project;
+  $self->{BRANCH}   = $branch;
+  $self->{SUBDIR}   = $subdir;
+  $self->{PEGREV}   = $pegrev;
+  $self->{ANALYSED} = 1;
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->root ();
+#
+# DESCRIPTION
+#   The method returns the repository root of the current URL.
+# ------------------------------------------------------------------------------
+
+sub root {
+  my $self = shift;
+
+  return $self->svninfo (FLAG => 'Repository Root');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->project_url_peg ();
+#   $cm_url->project_url_peg ($url);
+#
+# DESCRIPTION
+#   The method returns the URL@PEG of the "project" part of the current URL. If
+#   an argument is specified, the URL of the "project" part and the peg
+#   revision of the current URL are re-set.
+# ------------------------------------------------------------------------------
+
+sub project_url_peg {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->project_url_peg or $url ne $self->project_url_peg) {
+      my $pegrev = ($url =~ s/@($rev_pattern)$//i) ? $1 : '';
+
+      $url .= '/' . $self->branch if $self->branch;
+      $url .= '/' . $self->subdir if $self->subdir;
+      $url .= '@' . $pegrev       if $pegrev;
+
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{PROJECT} . ($self->pegrev ? '@' . $self->pegrev : '');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->project_url ();
+#   $cm_url->project_url ($url);
+#
+# DESCRIPTION
+#   The method returns the URL of the "project" part of the current URL. If an
+#   argument is specified, the URL of the "project" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub project_url {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+    $url =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL is necessary
+    if (! $self->project_url or $url ne $self->project_url) {
+      $url .= '/' . $self->branch if $self->branch;
+      $url .= '/' . $self->subdir if $self->subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{PROJECT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->project_path ();
+#   $cm_url->project_path ($path);
+#
+# DESCRIPTION
+#   The method returns the path of the "project" part of the current URL. If an
+#   argument is specified, the path of the "project" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub project_path {
+  my $self = shift;
+
+  # Repository root
+  my $root = $self->root;
+  $root    = substr (
+    $self->project_url,
+    0,
+    length ($self->project_url) - length ($self->project) - 1
+  ) if not $root;
+
+  if (@_) {
+    my $path = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->project_path or $path ne $self->project_path) {
+      $path .= '/' . $self->branch if $self->branch;
+      $path .= '/' . $self->subdir if $self->subdir;
+
+      $self->path ($path);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return substr ($self->{PROJECT}, length ($root));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $name = $cm_url->project ();
+#   $cm_url->project ($name);
+#
+# DESCRIPTION
+#   The method returns the basename of the "project" part of the current URL.
+#   If an argument is specified, the basename of the "project" part of the
+#   current URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub project {
+  my $self = shift;
+
+  if (@_) {
+    my $name = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->project or $name ne $self->project) {
+      my $url = '';
+      if ($self->project) {
+        $url =  $self->project;
+        $url =~ s#/[^/]+$##;
+
+      } else {
+        $url =  $self->root;
+      }
+
+      $url .=  '/' . $name;
+      $url .=  '/' . $self->branch if $self->branch;
+      $url .=  '/' . $self->subdir if $self->subdir;
+      $url .=  '@' . $self->pegrev if $self->pegrev;
+
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  my $name = $self->{PROJECT};
+  $name =~ s#^.*/([^/]+)$#$1# if $name;
+
+  return $name;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->branch_url_peg ();
+#   $cm_url->branch_url_peg ($url);
+#
+# DESCRIPTION
+#   The method returns the URL@PEG of the "branch" part of the current URL. If
+#   an argument is specified, the URL@PEG of the "branch" part of the current
+#   URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub branch_url_peg {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->branch_url_peg or $url ne $self->branch_url_peg) {
+      my $pegrev = ($url =~ s/@($rev_pattern)$//i) ? $1 : '';
+
+      $url .= '/' . $self->subdir if $self->subdir;
+      $url .= '@' . $pegrev       if $pegrev;
+
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->project_url . '/' . $self->branch .
+         ($self->pegrev ? '@' . $self->pegrev : '');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->branch_url ();
+#   $cm_url->branch_url ($url);
+#
+# DESCRIPTION
+#   The method returns the URL of the "branch" part of the current URL. If an
+#   argument is specified, the URL of the "branch" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub branch_url {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+    $url =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL is necessary
+    if (! $self->branch_url or $url ne $self->branch_url) {
+      $url .= '/' . $self->subdir if $self->subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->project_url . '/' . $self->branch;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->branch_path ();
+#   $cm_url->branch_path ($path);
+#
+# DESCRIPTION
+#   The method returns the path of the "branch" part of the current URL. If an
+#   argument is specified, the path of the "branch" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub branch_path {
+  my $self = shift;
+
+  if (@_) {
+    my $path = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->branch_path or $path ne $self->branch_path) {
+      $path .= '/' . $self->subdir if $self->subdir;
+
+      $self->path ($path);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return ($self->branch ? $self->project_path . '/' . $self->branch : undef);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $branch = $cm_url->branch ();
+#   $cm_url->branch ($branch);
+#
+# DESCRIPTION
+#   The method returns the "branch" part of the current URL. If an argument is
+#   specified, the "branch" part of the current URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub branch {
+  my $self = shift;
+
+  if (@_) {
+    my $branch = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->branch or $branch ne $self->branch) {
+      my $url = $self->project_url;
+      $url   .= '/' . $branch;
+      $url   .= '/' . $self->subdir if $self->subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{BRANCH};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $obj->branch_owner;
+#
+# DESCRIPTION
+#   This method returns the owner of the branch.
+# ------------------------------------------------------------------------------
+
+sub branch_owner {
+  my $self = shift;
+  my $return;
+
+  if ($self->is_branch and $self->branch_url =~ m#/([^/]+)/[^/]+/*$#) {
+    my $user = $1;
+    $return = $user;
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_trunk ();
+#
+# DESCRIPTION
+#   The method returns true if the the current URL is (a sub-tree of) the trunk.
+# ------------------------------------------------------------------------------
+
+sub is_trunk {
+  my $self = shift;
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return ($self->branch and $self->branch eq 'trunk');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_branch ();
+#
+# DESCRIPTION
+#   The method returns true if the the current URL is (a sub-tree of) a branch.
+# ------------------------------------------------------------------------------
+
+sub is_branch {
+  my $self = shift;
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return ($self->branch and $self->branch =~ m#^branches/#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_tag ();
+#
+# DESCRIPTION
+#   The method returns true if the the current URL is (a sub-tree of) a tag.
+# ------------------------------------------------------------------------------
+
+sub is_tag {
+  my $self = shift;
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return ($self->branch and $self->branch =~ m#^tags/#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $subdir = $cm_url->subdir ();
+#   $cm_url->subdir ($subdir);
+#
+# DESCRIPTION
+#   The method returns the "subdir" part of the current URL. If an argument is
+#   specified, the "subdir" part of the current URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub subdir {
+  my $self = shift;
+
+  if (@_) {
+    my $subdir = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->subdir or $subdir ne $self->subdir) {
+      my $url = $self->project_url;
+      $url   .= '/' . $self->branch if $self->branch;
+      $url   .= '/' . $subdir if $subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{SUBDIR};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->url ();
+#   $cm_url->url ($url);
+#
+# DESCRIPTION
+#   The method returns the URL without the "peg revision" part. If an argument
+#   is specified, the URL is re-set without modifying the "peg revision" part.
+# ------------------------------------------------------------------------------
+
+sub url {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+    $url    =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL if necessary
+    if (! $self->url or $url ne $self->url) {
+      $self->url_peg ($url . ($self->pegrev ? '@' . $self->pegrev : ''));
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  (my $url = $self->url_peg) =~ s/@($rev_pattern)$//i;
+
+  return $url;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->path ();
+#   $cm_url->path ($path);
+#
+# DESCRIPTION
+#   The method returns the "path" part of the URL (i.e. URL without the
+#   "root" part). If an argument is specified, the "path" part of the URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub path {
+  my $self = shift;
+
+  # Repository root
+  my $root = $self->root;
+  $root    = substr (
+    $self->project_url,
+    0,
+    length ($self->project_url) - length ($self->project) - 1
+  ) if not $root;
+
+  if (@_) {
+    my $path = shift;
+    $path    =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL is necessary
+    if (! $self->path or $path ne $self->path) {
+      my $url = ($root . (substr ($path, 0, 1) eq '/' ? '' : '/') . $path);
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return substr ($self->url, length ($root));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->path_peg ();
+#   $cm_url->path_peg ($path);
+#
+# DESCRIPTION
+#   The method returns the PATH@PEG part of the URL (i.e. URL without the
+#   "root" part). If an argument is specified, the PATH@PEG part of the URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub path_peg {
+  my $self = shift;
+
+  # Repository root
+  my $root = $self->root;
+  $root    = substr (
+    $self->project_url,
+    0,
+    length ($self->project_url) - length ($self->project) - 1
+  ) if not $root;
+
+  if (@_) {
+    my $path = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->path_peg or $path ne $self->path_peg) {
+      my $url = ($root . (substr ($path, 0, 1) eq '/' ? '' : '/') . $path);
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return substr ($self->url_peg, length ($root));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rev = $cm_url->pegrev ();
+#   $cm_url->pegrev ($rev);
+#
+# DESCRIPTION
+#   The method returns the "peg revision" part of the current URL. If an
+#   argument is specified, the "peg revision" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub pegrev {
+  my $self = shift;
+
+  if (@_) {
+    my $pegrev = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->pegrev or $pegrev ne $self->pegrev) {
+      $self->url_peg ($self->url . ($pegrev ? '@' . $pegrev : ''));
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{PEGREV};
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/Config.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Config.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Config.pm	(revision 1981)
@@ -0,0 +1,894 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Config
+#
+# DESCRIPTION
+#   This is a class for reading and processing central and user configuration
+#   settings for FCM.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Config;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use File::Basename;
+use File::Spec::Functions;
+use FindBin;
+use POSIX qw/setlocale LC_ALL/;
+
+# FCM component modules
+use Fcm::CfgFile;
+
+# Other declarations:
+sub _get_hash_value;
+
+# Delimiter for setting and for list
+our $DELIMITER         = '::';
+our $DELIMITER_PATTERN = qr{::|/};
+our $DELIMITER_LIST    = ',';
+
+my $INSTANCE;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = Fcm::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 = Fcm::Config->new (VERBOSE => $verbose);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::Config class.
+#
+# ARGUMENTS
+#   VERBOSE - Set the verbose level of diagnostic output
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  # Ensure that all subsequent Subversion output is in UK English
+  if (setlocale (LC_ALL, 'en_GB')) {
+    $ENV{LANG} = 'en_GB';
+  }
+
+  my $self = {
+    initialising   => 1,
+    central_config => undef,
+    user_config    => undef,
+    user_id        => undef,
+    verbose        => exists $args{VERBOSE} ? $args{VERBOSE} : undef,
+    variable       => {},
+
+    # Primary settings
+    setting => {
+      # Current command
+      FCM_COMMAND => &basename ($0),
+
+      # Current FCM release identifier
+      FCM_RELEASE => '1-5',
+
+      # Location of file with the last changed revision of the FCM trunk
+      FCM_REV_FILE => catfile (dirname ($FindBin::Bin), 'etc', 'fcm_rev'),
+
+      # 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 (-r catfile (dirname ($FindBin::Bin), 'etc', 'fcm.cfg')) {
+          $self->{$name} = catfile (
+            dirname ($FindBin::Bin), 'etc', 'fcm.cfg'
+          );
+
+        } elsif (-r 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 -r 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 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 || !-r $config_file) {
+    return;
+  }
+
+  my $cfgfile = Fcm::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: vendors/fcm/1-5/lib/Fcm/ConfigSystem.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/ConfigSystem.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/ConfigSystem.pm	(revision 1981)
@@ -0,0 +1,735 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::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.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::ConfigSystem;
+use base qw{Fcm::Base};
+
+use strict;
+use warnings;
+
+use Fcm::CfgFile;
+use Fcm::CfgLine;
+use Fcm::Dest;
+use Fcm::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 = Fcm::ConfigSystem->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::ConfigSystem class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::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} = Fcm::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} = Fcm::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 (@Fcm::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
+#   Fcm::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 Fcm::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 (-r $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 -r $use_cache;
+    }
+  }
+
+  my $old_lines = undef;
+  for my $in_cache (@in_caches) {
+    next unless -r $in_cache;
+    my $cfg = Fcm::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 = Fcm::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 Fcm::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 Fcm::Config to be compared
+#   @old_lines - a list of Fcm::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, Fcm::CfgLine->new(
+      LABEL => $prefix . $Fcm::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/^Fcm:://;
+
+  # 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 ();
+#
+# DESCRIPTION
+#   This method calls other methods to parse the configuration file.
+# ------------------------------------------------------------------------------
+
+sub parse_cfg {
+  my $self = shift;
+
+  return unless $self->cfg->src;
+
+  # Read config file
+  # ----------------------------------------------------------------------------
+  return unless $self->cfg->read_cfg;
+
+  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;
+    $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
+  # ----------------------------------------------------------------------------
+  for my $use (@{ $self->inherit }) {
+    push @{ $self->dest->inherit }, (@{ $use->dest->inherit }, $use->dest);
+  }
+
+  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;
+
+    # 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 {
+    Fcm::CfgLine->new (
+      label => $self->cfglabel ('USE'), value => $_->dest->rootdir
+    );
+  } @{ $self->inherit };
+
+  return (
+    Fcm::CfgLine::comment_block ('File header'),
+    Fcm::CfgLine->new (
+      label => $self->cfglabel ('CFGFILE') . $Fcm::Config::DELIMITER . 'TYPE',
+      value => $self->type,
+    ),
+    Fcm::CfgLine->new (
+      label => $self->cfglabel ('CFGFILE') . $Fcm::Config::DELIMITER . 'VERSION',
+      value => '1.0',
+    ),
+    Fcm::CfgLine->new (),
+
+    @inherited_dests,
+
+    Fcm::CfgLine::comment_block ('Destination'),
+    ($self->dest->to_cfglines()),
+  );
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/Dest.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Dest.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Dest.pm	(revision 1981)
@@ -0,0 +1,887 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Dest
+#
+# DESCRIPTION
+#   This class contains methods to set up a destination location of an FCM
+#   extract/build.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use warnings;
+use strict;
+
+package Fcm::Dest;
+use base qw{Fcm::Base};
+
+use Carp             qw{croak}                          ;
+use Cwd              qw{cwd}                            ;
+use Fcm::CfgLine                                        ;
+use Fcm::Timer       qw{timestamp_command}              ;
+use Fcm::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 Fcm::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 = Fcm::Dest->new(%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::Dest class. See above for
+#   allowed list of properties. (KEYS should be in uppercase.)
+# ------------------------------------------------------------------------------
+
+sub new {
+  my ($class, %args) = @_;
+  my $self = bless(Fcm::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 Fcm::Dest object is
+#   destroyed.
+# ------------------------------------------------------------------------------
+
+sub DESTROY {
+  my $self = shift;
+
+  # Remove the lockfile if it is set
+  unlink $self->lockfile if $self->lockfile and -w $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
+    unless (-d $dir and -w $dir) {
+      w_report 'ERROR: ', $dir, ': cannot write to 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
+      return if not -r $File::Find::name; # ignore unreadable files
+
+      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 Fcm::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 ? $Fcm::Config::DELIMITER . $index : q{});
+
+  my @return = (
+    Fcm::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,
+          Fcm::CfgLine->new(
+            label => $PREFIX . $Fcm::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: vendors/fcm/1-5/lib/Fcm/Exception.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Exception.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Exception.pm	(revision 1981)
@@ -0,0 +1,95 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::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
+
+Fcm::Exception
+
+=head1 SYNOPSIS
+
+    use Fcm::Exception;
+    eval {
+        croak(Fcm::Exception->new({message => $message}));
+    };
+    if ($@) {
+        if (Fcm::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
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Extract.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Extract.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Extract.pm	(revision 1981)
@@ -0,0 +1,1118 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Extract
+#
+# DESCRIPTION
+#   This is the top level class for the FCM extract system.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Extract;
+@ISA = qw(Fcm::ConfigSystem);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use File::Path;
+use File::Spec;
+
+# FCM component modules
+use Fcm::CfgFile;
+use Fcm::CfgLine;
+use Fcm::Config;
+use Fcm::ConfigSystem;
+use Fcm::Dest;
+use Fcm::ExtractFile;
+use Fcm::ExtractSrc;
+use Fcm::Keyword;
+use Fcm::ReposBranch;
+use Fcm::SrcDirLayer;
+use Fcm::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=Fcm::ExtractFile
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $obj = Fcm::Extract->new;
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::Extract class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::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} = Fcm::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 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 }, Fcm::CfgLine->new (
+      LABEL  => join ($Fcm::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 ($Fcm::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 = Fcm::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 (
+            $Fcm::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 = Fcm::Util::tidy_url(Fcm::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 (/$Fcm::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 (/$Fcm::Config::DELIMITER/, $dir)
+          ),
+          STACK => [],
+          FILES => {},
+        });
+      }
+
+      my $stack = $self->srcdirs ($dir)->{STACK}; # copy reference
+
+      # Create a new layer in the input stack
+      my $layer = Fcm::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 ($Fcm::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 (/$Fcm::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, Fcm::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
+#   Fcm::ExtractFile object. It then compares its file's sources to determine
+#   if they have changed. If so, it will allow the Fcm::ExtractFile to
+#   "re-extract" the file to the destination. Otherwise, it will set
+#   Fcm::ExtractFile->dest_status to a null string to denote an "unchanged"
+#   dest_status.
+#
+# SEE ALSO
+#   Fcm::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 Fcm::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 (/$Fcm::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, Fcm::ExtractFile->new (
+            conflict => $self->conflict,
+            dest     => $self->dest->srcpath,
+            pkgname  => $pkgname,
+          ));
+
+          # Base is empty
+          $self->files ($pkgname)->src->[0] = Fcm::ExtractSrc->new (
+            id      => $layer->tag,
+            pkgname => $pkgname,
+          ) if $i > 0;
+        }
+        my $cache = File::Spec->catfile ($layer->localdir, $file);
+        push @{ $self->files ($pkgname)->src }, Fcm::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 }, Fcm::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 ($Fcm::Config::DELIMITER, @sources);
+
+    # Set Fcm::ExtractFile->dest_status to "unchanged" if value is unchanged
+    $self->files ($key)->dest_status ('')
+      if exists $old{$key} and $old{$key} eq $value;
+
+    # 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, Fcm::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 %Fcm::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 $Fcm::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 %Fcm::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 $Fcm::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, Fcm::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 (/$Fcm::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 Fcm::ConfigSystem->to_cfglines for further information.
+# ------------------------------------------------------------------------------
+
+sub to_cfglines {
+  my ($self) = @_;
+
+  return (
+    Fcm::ConfigSystem::to_cfglines($self),
+
+    $self->rdest->to_cfglines (),
+    Fcm::CfgLine->new (),
+
+    @{ $self->bdeclare } ? (
+      Fcm::CfgLine::comment_block ('Build declarations'),
+      map {
+        Fcm::CfgLine->new (label => $_->label, value => $_->value)
+      } ($self->sort_bdeclare),
+      Fcm::CfgLine->new (),
+    ) : (),
+
+    Fcm::CfgLine::comment_block ('Project and branches'),
+    (map {($_->to_cfglines ())} @{ $self->branches }),
+
+    ($self->conflict ne 'merge') ? (
+      Fcm::CfgLine->new (
+        label => $self->cfglabel ('CONFLICT'), value => $self->conflict,
+      ),
+      Fcm::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, Fcm::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, Fcm::CfgLine->new (label => $label, value => 'false');
+    }
+  }
+
+  return (
+    Fcm::CfgLine::comment_block ('File header'),
+    (map
+      {my ($lbl, $val) = @{$_}; Fcm::CfgLine->new(label => $lbl, value => $val)}
+      (
+        [$self->cfglabel('CFGFILE') . $Fcm::Config::DELIMITER . 'TYPE'   , 'bld'],
+        [$self->cfglabel('CFGFILE') . $Fcm::Config::DELIMITER . 'VERSION', '1.0'],
+        [],
+      )
+    ),
+
+    @{ $self->inherit } ? (
+      @inherits,
+      @no_inherits,
+      Fcm::CfgLine->new (),
+    ) : (),
+
+    Fcm::CfgLine::comment_block ('Destination'),
+    Fcm::CfgLine->new (label => $self->cfglabel ('DEST'), value => $root),
+    Fcm::CfgLine->new (),
+
+    @{ $self->bdeclare } ? (
+      Fcm::CfgLine::comment_block ('Build declarations'),
+      map {
+        Fcm::CfgLine->new (label => $_->slabel, value => $_->value)
+      } ($self->sort_bdeclare),
+      Fcm::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 = Fcm::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 = Fcm::CfgFile->new (TYPE => 'bld');
+  $cfg->lines ([$self->to_cfglines_bld()]);
+  $cfg->print_cfg ($self->dest->bldcfg);
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/ExtractConfigComparator.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/ExtractConfigComparator.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/ExtractConfigComparator.pm	(revision 1981)
@@ -0,0 +1,358 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+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 Fcm::CmUrl;
+    use Fcm::Keyword;
+    use Fcm::Util qw{tidy_url};
+
+    ############################################################################
+    # Reports the comparator's result
+    sub report_impl {
+        my ($self, $comparator) = @_;
+        # Output in wiki format
+        my $wiki_url = Fcm::CmUrl->new(
+            URL => tidy_url(Fcm::Keyword::expand($comparator->get_wiki()))
+        );
+        my $base_trac
+            = $comparator->get_wiki()
+            ? Fcm::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 = Fcm::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 Fcm::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 (Fcm::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 Fcm::ExtractConfigComparator;
+
+use Fcm::CmUrl;
+use Fcm::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] = Fcm::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 Fcm::CmUrl object for each SRC
+                my $cm_url = Fcm::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 => 'Last Changed Rev');
+                        my $that_rev
+                            = $that_url->svninfo(FLAG => 'Last Changed Rev');
+
+                        # 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
+                                    = Fcm::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
+
+Fcm::ExtractConfigComparator
+
+=head1 SYNOPSIS
+
+    use Fcm::ExtractConfigComparator;
+    my $comparator = Fcm::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<Fcm::Extract|Fcm::Extract>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/ExtractFile.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/ExtractFile.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/ExtractFile.pm	(revision 1981)
@@ -0,0 +1,410 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::ExtractFile
+#
+# DESCRIPTION
+#   Select/combine a file in different branches and extract it to destination.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use warnings;
+use strict;
+
+package Fcm::ExtractFile;
+use base qw{Fcm::Base};
+
+use Fcm::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 Fcm::ExtractSrc, specified for this file
+  'src_actual',  # list of Fcm::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 = Fcm::ExtractFile->new ();
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::ExtractFile class.
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::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
+#   Fcm::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: vendors/fcm/1-5/lib/Fcm/ExtractSrc.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/ExtractSrc.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/ExtractSrc.pm	(revision 1981)
@@ -0,0 +1,87 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::ExtractSrc
+#
+# DESCRIPTION
+#   This class is used by the extract system to define the functionalities of a
+#   source file (or directory) in a branch.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::ExtractSrc;
+@ISA = qw(Fcm::Base);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# FCM component modules
+use Fcm::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 = Fcm::ExtractSrc->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::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 = Fcm::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: vendors/fcm/1-5/lib/Fcm/Interactive.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Interactive.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Interactive.pm	(revision 1981)
@@ -0,0 +1,131 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Interactive;
+use base qw{Exporter};
+
+our @EXPORT_OK = qw{get_input};
+
+use Fcm::Util::ClassLoader;
+
+my $DEFAULT_IMPL_CLASS = 'Fcm::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();
+    Fcm::Util::ClassLoader::load($class_name);
+    %options = map {lc($_), $options{$_}} keys(%options);
+    return $class_name->new({%{$class_options_ref}, %options})->invoke();
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Interactive
+
+=head1 SYNOPSIS
+
+    use Fcm::Interactive;
+    Fcm::Interactive::set_impl('My::InputGetter', {option1 => 'value1', ...});
+    $answer = Fcm::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<Fcm::Interactive::InputGetter::CLI|Fcm::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<Fcm::Interactive::InputGetter|Fcm::Interactive::InputGetter>,
+L<Fcm::Interactive::InputGetter::CLI|Fcm::Interactive::InputGetter::CLI>,
+L<Fcm::Interactive::InputGetter::GUI|Fcm::Interactive::InputGetter::GUI>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter.pm	(revision 1981)
@@ -0,0 +1,122 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::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("Fcm::Interactive::InputGetter->invoke() not implemented.");
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Interactive::TxtInputGetter
+
+=head1 SYNOPSIS
+
+    use Fcm::Interactive::TxtInputGetter;
+    $answer = Fcm::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<Fcm::Interactive|Fcm::Interactive> to get a user reply.
+
+=head1 METHODS
+
+=over 4
+
+=item new($args_ref)
+
+Constructor, normally invoked via L<Fcm::Interactive|Fcm::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<Fcm::Interactive|Fcm::Interactive>,
+L<Fcm::Interactive::TxtInputGetter|Fcm::Interactive::TxtInputGetter>,
+L<Fcm::Interactive::GUIInputGetter|Fcm::Interactive::GUIInputGetter>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter/CLI.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter/CLI.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter/CLI.pm	(revision 1981)
@@ -0,0 +1,87 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Interactive::InputGetter::CLI;
+use base qw{Fcm::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
+
+Fcm::Interactive::InputGetter::CLI
+
+=head1 SYNOPSIS
+
+    use Fcm::Interactive;
+    $answer = Fcm::Interactive::get_input(
+        title   => 'My title',
+        message => 'Would you like to ...?',
+        type    => 'yn',
+        default => 'n',
+    );
+
+=head1 DESCRIPTION
+
+This is a solid implementation of
+L<Fcm::Interactive::InputGetter|Fcm::Interactive::InputGetter>. It gets a user
+reply from STDIN using a prompt on STDOUT.
+
+=head1 METHODS
+
+See L<Fcm::Interactive::InputGetter|Fcm::Interactive::InputGetter> for a list of
+methods.
+
+=head1 TO DO
+
+Use IO::Prompt.
+
+=head1 SEE ALSO
+
+L<Fcm::Interactive|Fcm::Interactive>,
+L<Fcm::Interactive::InputGetter|Fcm::Interactive::InputGetter>,
+L<Fcm::Interactive::InputGetter::GUI|Fcm::Interactive::InputGetter::GUI>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter/GUI.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter/GUI.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Interactive/InputGetter/GUI.pm	(revision 1981)
@@ -0,0 +1,248 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Interactive::InputGetter::GUI;
+use base qw{Fcm::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
+
+Fcm::Interactive::InputGetter::GUI
+
+=head1 SYNOPSIS
+
+    use Fcm::Interactive;
+    $answer = Fcm::Interactive::get_input(
+        title   => 'My title',
+        message => 'Would you like to ...?',
+        type    => 'yn',
+        default => 'n',
+    );
+
+=head1 DESCRIPTION
+
+This is a solid implementation of
+L<Fcm::Interactive::InputGetter|Fcm::Interactive::InputGetter>. It gets a user
+reply from a TK pop up message box.
+
+=head1 METHODS
+
+See L<Fcm::Interactive::InputGetter|Fcm::Interactive::InputGetter> for a list of
+inherited methods.
+
+=over 4
+
+=item new($args_ref)
+
+As in L<Fcm::Interactive::InputGetter|Fcm::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<Fcm::Interactive|Fcm::Interactive>,
+L<Fcm::Interactive::InputGetter|Fcm::Interactive::InputGetter>,
+L<Fcm::Interactive::InputGetter::CLI|Fcm::Interactive::InputGetter::CLI>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword.pm	(revision 1981)
@@ -0,0 +1,376 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword;
+
+use Carp qw{croak};
+use Fcm::Config;
+use Fcm::Exception;
+use Fcm::Keyword::Config;
+use Fcm::Keyword::Exception;
+use URI;
+
+my $ENTRIES;
+
+my $PREFIX_OF_LOCATION_KEYWORD = 'fcm';
+my $PATTERN_OF_RESERVED_REVISION_KEYWORDS
+    = qr{\A (?:\d+|HEAD|BASE|COMMITTED|PREV|\{[^\}]+\}) \z}ixms;
+
+################################################################################
+# Returns the Fcm::Keyword::Entries object for storing the location entries
+sub get_entries {
+    my ($reset) = @_;
+    if ($reset || !$ENTRIES) {
+        $ENTRIES = Fcm::Keyword::Config::get_entries('LOCATION_ENTRIES');
+    }
+    return $ENTRIES;
+}
+
+################################################################################
+# Returns a list of Fcm::Keyword::Entry::Location objects matching $in_loc
+sub get_location_entries_for {
+    my ($in_loc) = @_;
+    my ($loc, $rev, @entry_trail_refs) = _parse_loc($in_loc);
+    return (map {$_->[0]} @entry_trail_refs);
+}
+
+################################################################################
+# Returns the prefix of location keyword (with or without the delimiter).
+sub get_prefix_of_location_keyword {
+    my ($with_delimiter) = @_;
+    return $PREFIX_OF_LOCATION_KEYWORD . ($with_delimiter ? ':' : '');
+}
+
+################################################################################
+# Expands (the keywords in) the specfied location (and REV), and returns them
+sub expand {
+    my ($in_loc, $in_rev) = @_;
+    my ($loc, $rev) = _expand($in_loc, $in_rev);
+    return _unparse_loc($loc, $rev, $in_rev);
+}
+
+################################################################################
+# Returns the corresponding browser URL for the input VC location
+sub get_browser_url {
+    my ($in_loc, $in_rev) = @_;
+
+    my ($loc, $rev, @entry_trail_refs) = _expand($in_loc, $in_rev);
+    if (!@entry_trail_refs) {
+        croak(Fcm::Keyword::Exception->new({message => sprintf(
+            "%s: cannot be mapped to a browser URL", $in_loc,
+        )}));
+    }
+
+    my @entries = map {$_->[0]} @entry_trail_refs;
+    my $location_component_pattern
+        = _get_browser_url_setting(\@entries, 'location_component_pattern');
+    my $browser_url_template
+        = _get_browser_url_setting(\@entries, 'browser_url_template');
+    my $browser_rev_template
+        = _get_browser_url_setting(\@entries, 'browser_rev_template');
+
+    if (
+           $location_component_pattern
+        && $browser_url_template
+        && $browser_rev_template
+    ) {
+        my $uri = URI->new($loc);
+        my $sps = $uri->opaque();
+        my @matches = $sps =~ $location_component_pattern;
+        if (@matches) {
+            my $result = $browser_url_template;
+            for my $field_number (1 .. @matches) {
+                my $match = $matches[$field_number - 1];
+                $result =~ s/\{ $field_number \}/$match/xms;
+            }
+            my $rev_field = scalar(@matches) + 1;
+            if ($rev) {
+                my $rev_string = $browser_rev_template;
+                $rev_string =~ s/\{1\}/$rev/xms;
+                $result =~ s/\{ $rev_field \}/$rev_string/xms;
+            }
+            else {
+                $result =~ s/\{ $rev_field \}//xms;
+            }
+            return $result;
+        }
+    }
+    else {
+        croak(Fcm::Keyword::Exception->new({message => sprintf(
+            "%s: mapping templates not defined correctly", $in_loc,
+        )}));
+    }
+}
+
+################################################################################
+# Returns a browser URL setting, helper function for get_browser_url()
+sub _get_browser_url_setting {
+    my ($entries_ref, $setting) = @_;
+    my $getter = "get_$setting";
+    for my $entry (@{$entries_ref}) {
+        my $setting = $entry->$getter();
+        if ($setting) {
+            return $setting;
+        }
+    }
+    my $config = Fcm::Config->instance();
+    return $config->setting('URL_BROWSER_MAPPING_DEFAULT', uc($setting));
+}
+
+################################################################################
+# Un-expands the specfied location (and REV) to keywords, and returns them
+sub unexpand {
+    my ($in_loc, $in_rev) = @_;
+    my ($loc, $rev, @entry_trail_refs) = _parse_loc($in_loc, $in_rev);
+    if (@entry_trail_refs) {
+        my ($entry, $trail) = @{$entry_trail_refs[0]};
+        if ($rev) {
+            GET_REV_KEY:
+            for my $entry_trail_ref (@entry_trail_refs) {
+                my ($e, $t) = @{$entry_trail_ref};
+                my $rev_key
+                    = $e->get_revision_entries()->get_entry_by_value($rev);
+                if ($rev_key) {
+                    $rev = $rev_key->get_key();
+                    last GET_REV_KEY;
+                }
+            }
+        }
+        $loc = get_prefix_of_location_keyword(1) . $entry->get_key() . $trail;
+        return _unparse_loc($loc, $rev, $in_rev);
+    }
+    return _unparse_loc($in_loc, $in_rev, $in_rev);
+}
+
+################################################################################
+# Expands (the keywords in) the specfied location (and REV), and returns them
+sub _expand {
+    my ($in_loc, $in_rev) = @_;
+    my ($loc, $rev, @entry_trail_refs) = _parse_loc($in_loc, $in_rev);
+    if (@entry_trail_refs) {
+        my ($entry, $trail) = @{$entry_trail_refs[0]};
+        $loc = $entry->get_value() . $trail;
+        if ($rev && $rev !~ $PATTERN_OF_RESERVED_REVISION_KEYWORDS) {
+            my $r;
+            GET_REV:
+            for my $entry_trail_ref (@entry_trail_refs) {
+                my ($e, $t) = @{$entry_trail_ref};
+                $r = $e->get_revision_entries()->get_entry_by_key($rev);
+                if ($r) {
+                    $rev = $r->get_value();
+                    last GET_REV;
+                }
+            }
+            if (!$r) {
+                croak(Fcm::Keyword::Exception->new({message => sprintf(
+                    "%s: %s: unknown revision keyword",
+                    $loc, $rev,
+                )}));
+            }
+        }
+    }
+    return ($loc, $rev, @entry_trail_refs);
+}
+
+################################################################################
+# Parses $in_loc (and $in_rev)
+sub _parse_loc {
+    my ($in_loc, $in_rev) = @_;
+    if (!$in_loc) {
+        croak(Fcm::Exception->new({
+            message => 'internal error: $in_loc not defined',
+        }));
+    }
+    if ($in_loc) {
+        if (!defined($in_rev)) {
+            my ($loc, $rev) = $in_loc =~ qr{\A (.+) \@ ([^/\@]+) \z}xms;
+            if ($loc && $rev) {
+                return ($loc, $rev, _get_loc_entry($loc));
+            }
+            else {
+                return ($in_loc, $in_rev, _get_loc_entry($in_loc));
+            }
+        }
+        return ($in_loc, $in_rev, _get_loc_entry($in_loc));
+    }
+    return;
+}
+
+################################################################################
+# Returns a list of keyword entries/trailing path pairs for the input location
+sub _get_loc_entry {
+    my ($loc) = @_;
+    if ($loc) {
+        my $uri = URI->new($loc);
+        if (
+               $uri->scheme()
+            && $uri->scheme() eq get_prefix_of_location_keyword()
+        ) {
+            my ($key, $trail) = $uri->opaque() =~ qr{\A ([^/\@]+) (.*) \z}xms;
+            my $entry = get_entries()->get_entry_by_key($key);
+            if (!$entry || !$entry->get_value()) {
+                die(Fcm::Keyword::Exception->new({message => sprintf(
+                    "%s: unknown FCM location keyword", $loc,
+                )}));
+            }
+            $loc = $entry->get_value() . ($trail ? $trail : q{});
+        }
+        my @entry_trail_pairs = ();
+        my $lead = $loc;
+        GET_ENTRY:
+        while ($lead) {
+            my $entry = get_entries()->get_entry_by_value($lead);
+            if ($entry) {
+                my $trail = substr($loc, length($lead));
+                push @entry_trail_pairs, [$entry, $trail];
+            }
+            if (!($lead =~ s{/+ [^/]* \z}{}xms)) {
+                last GET_ENTRY;
+            }
+        }
+        if (@entry_trail_pairs) {
+            return @entry_trail_pairs;
+        }
+        else {
+            return;
+        }
+    }
+    return;
+}
+
+################################################################################
+# If $in_rev, returns (LOC, REV). Otherwise, returns LOC@REV
+sub _unparse_loc {
+    my ($loc, $rev, $in_rev) = @_;
+    if (!$loc) {
+        return;
+    }
+    return ($in_rev ? ($loc, $rev) : join(q{@}, $loc, ($rev ? $rev : ())));
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword;
+
+    $loc = Fcm::Keyword::expand('fcm:namespace/path@rev-keyword');
+    $loc = Fcm::Keyword::unexpand('svn://host/namespace/path@1234');
+
+    ($loc, $rev) = Fcm::Keyword::expand('fcm:namespace/path', 'rev-keyword');
+    ($loc, $rev) = Fcm::Keyword::unexpand('svn://host/namespace/path', 1234);
+
+    $loc = Fcm::Keyword::get_browser_url('fcm:namespace/path');
+    $loc = Fcm::Keyword::get_browser_url('svn://host/namespace/path');
+
+    $loc = Fcm::Keyword::get_browser_url('fcm:namespace/path@1234');
+    $loc = Fcm::Keyword::get_browser_url('svn://host/namespace/path@1234');
+
+    $loc = Fcm::Keyword::get_browser_url('fcm:namespace/path', 1234);
+    $loc = Fcm::Keyword::get_browser_url('svn://host/namespace/path', 1234);
+
+    $entries = Fcm::Keyword::get_entries();
+
+=head1 DESCRIPTION
+
+This module contains utilities to expand and unexpand FCM location and revision
+keywords.
+
+=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_entries([$reset])
+
+Returns the L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object for storing
+location keyword entries. If $reset if true, reloads the entries.
+
+=item get_location_entries_for($loc)
+
+Returns a list of L<Fcm::Keyword::Entry::Location|Fcm::Keyword::Entry::Location>
+objects matching $loc.
+
+=item get_prefix_of_location_keyword($with_delimiter)
+
+Returns the prefix of a FCM location keyword, (currently "fcm"). If
+$with_delimiter is specified and is true, returns the prefix with the delimiter,
+(currently "fcm:").
+
+=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 
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::Keyword::Exception|Fcm::Keyword::Exception>
+
+Functions in this module may die() with this exception when it fails to expand
+a keyword.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword::Config|Fcm::Keyword::Config>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Entry::Location|Fcm::Keyword::Entry::Location>,
+L<Fcm::Keyword::Exception|Fcm::Keyword::Exception>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Config.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Config.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Config.pm	(revision 1981)
@@ -0,0 +1,143 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Config;
+
+use Carp;
+use Fcm::Keyword::Entries;
+use Fcm::Keyword::Exception;
+use Fcm::Util::ClassLoader;
+
+our %CONFIG_OF = (
+    LOCATION_ENTRIES => {
+        entry_class => 'Fcm::Keyword::Entry::Location',
+        loaders => [
+            {
+                class => 'Fcm::Keyword::Loader::Config::Location',
+            },
+        ],
+    },
+    REVISION_ENTRIES => {
+        entry_class => 'Fcm::Keyword::Entry',
+        loaders => [
+            {
+                class   => 'Fcm::Keyword::Loader::Config::Revision',
+                options => [{key => 'namespace', valuekey => 'key'}],
+            },
+            {
+                class   => 'Fcm::Keyword::Loader::VC::Revision',
+                options => [{key => 'source', valuekey => 'value'}],
+            },
+        ],
+    },
+);
+
+################################################################################
+# Returns a Fcm::Keyword::Entries object for given configuration
+sub get_entries {
+    my ($context, $args_ref) = @_;
+    if (!exists($CONFIG_OF{$context})) {
+        croak(Fcm::Keyword::Exception->new({message => sprintf(
+            "%s: keyword configuration not found", $context,
+        )}));
+    }
+    my $config_ref = $CONFIG_OF{$context};
+    my @loaders;
+    if (exists($config_ref->{loaders})) {
+        for my $loader_config (@{$config_ref->{loaders}}) {
+            my $class = $loader_config->{class};
+            Fcm::Util::ClassLoader::load($class);
+            my %options;
+            if (exists($loader_config->{options})) {
+                for my $option_ref (@{$loader_config->{options}}) {
+                    my $key = $option_ref->{key};
+                    my $value;
+                    if (exists($option_ref->{value})) {
+                        $value = $option_ref->{value};
+                    }
+                    elsif (
+                           exists($option_ref->{valuekey})
+                        && $args_ref
+                        && ref($args_ref) eq 'HASH'
+                        && exists($args_ref->{$option_ref->{valuekey}})
+                    ) {
+                        $value = $args_ref->{$option_ref->{valuekey}};
+                    }
+                    $options{$key} = $value;
+                }
+            }
+            push @loaders, $class->new(\%options);
+        }
+    }
+    my %entries_options = (
+        (@loaders ? (loaders => \@loaders) : ()),
+        (
+            exists($config_ref->{entry_class})
+            ? (entry_class => $config_ref->{entry_class})
+            : ()
+        ),
+    );
+    return Fcm::Keyword::Entries->new(\%entries_options);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Config
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword::Config;
+
+=head1 DESCRIPTION
+
+This module stores the default configuration used by modules in the
+L<Fcm::Keyword> family.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item get_entries($context,$args_ref)
+
+Returns a L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object for a given
+$context. If there is no matching $context in the configuration, croak() with a
+L<Fcm::Keyword::Exception|Fcm::Keyword::Exception>. $args_ref is an optional
+argument, which should be a reference to a hash containing a I<key> and a
+I<value> element. It can be used by this function to set up the constructor
+options in the loaders of the returned
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=head1 TO DO
+
+Allow configuration to be changed in runtime.
+
+Convert this module to OO?
+
+Separate configuration from logic if this module becomes any bigger.
+
+Unit tests.
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Exception|Fcm::Keyword::Exception>,
+L<Fcm::Keyword::Entry::Location|Fcm::Keyword::Entry::Location>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Entries.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Entries.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Entries.pm	(revision 1981)
@@ -0,0 +1,211 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Entries;
+
+use Carp qw{croak};
+use Fcm::Util::ClassLoader;
+
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless(
+        {
+            entry_class             => 'Fcm::Keyword::Entry',
+            entry_by                => {key => {}, value => {}},
+            has_loaded_entries_from => {},
+            loaders                 => [],
+            ($args_ref && ref($args_ref) eq 'HASH' ? %{$args_ref} : ()),
+        },
+        $class,
+    );
+}
+
+################################################################################
+# Returns the class of entries stored by this entries list
+sub get_entry_class {
+    my ($self) = @_;
+    return $self->{entry_class};
+}
+
+################################################################################
+# Returns all entries
+sub get_all_entries {
+    my ($self) = @_;
+    if (!%{$self->{entry_by}{key}}) {
+        # Nothing set, attempt to load entries
+        $self->load_entries();
+    }
+    if (wantarray()) {
+        return values(%{$self->{entry_by}{key}});
+    }
+    else {
+        return [values(%{$self->{entry_by}{key}})];
+    }
+}
+
+################################################################################
+# Methods: get_entry_by_*
+for my $name (
+    ### Returns an entry with a matching key
+    'key',
+    ### Returns an entry with a matching value
+    'value'
+) {
+    no strict qw{refs};
+    my $method = "get_entry_by_$name";
+    *$method = sub {
+        my ($self, $search_key) = @_;
+        if (!defined($search_key)) {
+            return;
+        }
+        my $sk = ($name eq 'key') ? uc($search_key) : $search_key;
+        if (!exists($self->{entry_by}{$name}{$sk})) {
+            $self->load_entries($name, $sk);
+        }
+        if (exists($self->{entry_by}{$name}{$sk})) {
+            return $self->{entry_by}{$name}{$sk};
+        }
+        else {
+            return;
+        }
+    }
+}
+
+################################################################################
+# Adds an entry
+sub add_entry {
+    my ($self, $key, $value, $args_ref) = @_;
+    Fcm::Util::ClassLoader::load($self->get_entry_class());
+    my $entry = $self->get_entry_class()->new({
+        key   => uc($key),
+        value => $value,
+        ($args_ref && ref($args_ref) eq 'HASH' ? %{$args_ref} : ()),
+    });
+    $self->{entry_by}{key}{uc($key)} = $entry;
+    $self->{entry_by}{value}{$value} = $entry;
+    return $entry;
+}
+
+################################################################################
+# Returns the loaders for this entries list
+sub get_loaders {
+    my ($self) = @_;
+    return (wantarray() ? @{$self->{loaders}} : $self->{loaders});
+}
+
+################################################################################
+# Loads entries using its loaders
+sub load_entries {
+    my ($self, $name, $search_key) = @_;
+    LOADER:
+    for my $loader ($self->get_loaders()) {
+        if ($self->{has_loaded_entries_from}{$loader->get_source()}) {
+            next LOADER;
+        }
+        $self->{has_loaded_entries_from}{$loader->get_source()}
+            = $loader->load_to($self);
+        if ($name && exists($self->{entry_by}{$name}{$search_key})) {
+            last LOADER;
+        }
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Entries
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword::Entries;
+
+    my $entries = Fcm::Keyword::Entries->new({
+        entry_class => $entry_class,
+        loaders     => \@loaders,
+    });
+    $entry = $entries->get_entry_by_key($key);
+    $entry = $entries->get_entry_by_value($value);
+
+    for my $entry ($entries->get_entries()) {
+        # ...
+    }
+
+    $entries->add_entry($key, $value);
+
+=head1 DESCRIPTION
+
+This module is used to manipulate FCM keyword entries. It is used by
+L<Fcm::Keyword|Fcm::Keyword> to store keyword entries, which are
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry> objects.
+
+=head1 METHODS
+
+=over 4
+
+=item C<new({entry_class =E<gt> $entry_class, loaders =E<gt> \@loaders})>
+
+Constructor. The argument should be a reference to hash, where:
+
+I<entry_class> is a string representing the class name of entries in this
+object. The class must be a sub-class of
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>. The default is
+"L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>".
+
+I<loaders> is a reference to an array of
+L<Fcm::Keyword::Loader|Fcm::Keyword::Loader> objects, which will be used to
+load entries into this object. The default is an empty array.
+
+=item add_entry($key,$value)
+
+Adds an entry. Returns the added entry. (Keys are converted to uppercases
+automatically.)
+
+=item get_all_entries()
+
+Returns all entries that are currently loaded.
+
+=item get_entry_by_key($key)
+
+Return an entry, whose key matches $key. (Search is case-insensitive.) Returns
+undef if there is no matching entry.
+
+=item get_entry_by_value($value)
+
+Return an entry, whose value matches $value. (Search is case-sensitive.)
+Returns undef if there is no matching entry.
+
+=item get_loaders()
+
+Returns the loaders for loading entries.
+
+=item load_entries()
+
+Loads entries from its loaders, as returned by get_loaders(). This method can
+also be triggered by get_all_entries(), if the entry list is empty, or by
+get_entry_by_key($key) and get_entry_by_value($value) methods, if there is no
+matching entry in the current lookup lists.
+
+=back
+
+=head1 TO DO
+
+Handle duplicated entries in add_entry($key,$value).
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Loader|Fcm::Keyword::Loader>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Entry.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Entry.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Entry.pm	(revision 1981)
@@ -0,0 +1,83 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Entry;
+
+sub new {
+    my ($class, $args_ref) = @_;
+    if (!$args_ref) {
+        $args_ref = {};
+    }
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+### Methods: get_*
+for my $name (
+    # Returns the key of this entry
+    'key',
+    # Returns the value of this entry
+    'value',
+) {
+    no strict qw{refs};
+    my $getter = "get_$name";
+    *$getter = sub {
+        my ($self) = @_;
+        return $self->{$name};
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Entry
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword::Entry;
+
+    $entry = Fcm::Keyword::Entry->new({key => $key, value => $value});
+    $key = $entry->get_key();
+    $value = $entry->get_value();
+
+=head1 DESCRIPTION
+
+An object of this class represents a FCM keyword entry.
+
+=head1 METHODS
+
+=over 4
+
+=item C<new({key =E<gt> $key, value =E<gt> $value})>
+
+Constructor.
+
+=item get_key()
+
+Returns the key of this keyword entry.
+
+=item get_value()
+
+Returns the value of this keyword entry.
+
+=back
+
+Simple formatter for displaying an entry.
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Entry/Location.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Entry/Location.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Entry/Location.pm	(revision 1981)
@@ -0,0 +1,146 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Entry::Location;
+use base qw{Fcm::Keyword::Entry};
+
+use Fcm::Keyword::Config;
+
+sub new {
+    my ($class, $args_ref) = @_;
+    if (!$args_ref) {
+        $args_ref = {};
+    }
+    $args_ref = {
+        browser_rev_template       => undef,
+        browser_url_template       => undef,
+        implied_entry_list         => [],
+        is_implied                 => 0,
+        location_component_pattern => undef,
+        revision_entries           => Fcm::Keyword::Config::get_entries(
+            'REVISION_ENTRIES', $args_ref,
+        ),
+        %{$args_ref},
+    },
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Methods: get_*
+for my $key (
+    # Returns a template for constructing the browser URL
+    'browser_url_template',
+    # Returns a template for constructing the revision part in the browser URL
+    'browser_rev_template',
+    # Returns a list of entries implied this entry
+    'implied_entry_list',
+    # Returns the component pattern for a location matching this entry
+    'location_component_pattern',
+    # Returns the entries for revision keywords
+    'revision_entries',
+) {
+    no strict qw{refs};
+    my $getter = "get_$key";
+    *$getter = sub {
+        my ($self) = @_;
+        return $self->{$key};
+    }
+}
+
+################################################################################
+# Returns true if this is an implied entry
+sub is_implied {
+    my ($self) = @_;
+    return $self->{is_implied};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Entry::Location
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword::Entry::Location;
+
+    $entry = Fcm::Keyword::Entry::Location->new({
+        key => $key, value => $value, # ...
+    });
+
+    $key = $entry->get_key();
+    $value = $entry->get_value();
+    $revision_entries = $entry->get_revision_entries();
+
+=head1 DESCRIPTION
+
+This is a sub-class of L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>. An object of
+this class represents a FCM location keyword entry.
+
+=head1 METHODS
+
+See L<Fcm::Keyword::Entry|Fcm::Keyword::Entry> for inherited methods.
+
+=over 4
+
+=item new($args_ref)
+
+Constructor.
+
+=item get_browser_url_template()
+
+Returns the template string for constructing the browser URL. The string {1},
+{2}, {3}, etc in the template string will be substituted by the components
+captured by the location component pattern and the revision template. See
+C<get_url_component_pattern()> and C<get_browser_rev_template()>.
+
+=item get_browser_rev_template()
+
+Returns the template string for constructing the revision part of the browser
+URL. The string {1} in the template string will be substituted by the revision.
+See C<get_browser_url_template()>.
+
+=item get_implied_entry_list()
+
+Returns a list of entries implied by this entry.
+
+=item get_location_component_pattern()
+
+Returns a regular expression, when matched against the scheme-specific-part in
+the actual URI of a location in the namespace of this keyword entry, will
+capture a list of components, which can then be used to replace the numbered
+fields in the browser URL template. See C<get_browser_url_template()>.
+
+=item get_revision_entries()
+
+Returns a L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object containing the
+revision keyword entries of this location.
+
+=item is_implied()
+
+Returns true if this is an implied entry.
+
+=back
+
+=head1 TO DO
+
+Introduce a Fcm::Keyword::Config module to store entries constructor setting.
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Config|Fcm::Keyword::Config>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Exception.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Exception.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Exception.pm	(revision 1981)
@@ -0,0 +1,42 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Exception;
+use base qw{Fcm::Exception};
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Exception
+
+=head1 SYNOPSIS
+
+    use Carp qw{croak};
+    use Fcm::Keyword::Exception;
+    croak(Fcm::Keyword::Exception->new({message => 'something is wrong'}));
+
+=head1 DESCRIPTION
+
+This class extends L<Fcm::Exception|Fcm::Exception>. This exception is thrown
+on errors associated with the command line interface.
+
+=head1 METHODS
+
+See L<Fcm::Exception|Fcm::Exception> for a list of methods.
+
+=head1 SEE ALSO
+
+L<Fcm::Exception|Fcm::Exception>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entries.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entries.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entries.pm	(revision 1981)
@@ -0,0 +1,77 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Formatter::Entries;
+
+use Fcm::Keyword::Formatter::Entry;
+
+################################################################################
+# Constructor
+sub new {
+    my ($class) = @_;
+    return bless(\do{my $annon_scalar}, $class);
+}
+
+################################################################################
+# Formats a keyword entry
+sub format {
+    my ($self, $entries) = @_;
+    my $formatter = Fcm::Keyword::Formatter::Entry->new();
+    my $return = q{};
+    for my $entry (
+        sort {$a->get_key() cmp $b->get_key()}
+        grep {!$_->can('is_implied') || !$_->is_implied()}
+        $entries->get_all_entries()
+    ) {
+        $return .= $formatter->format($entry);
+    }
+    return $return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Formatter::Entries
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword::Formatter::Entries;
+    $formatter = Fcm::Keyword::Formatter::Entries->new();
+    print($formatter->format($entries));
+
+=head1 DESCRIPTION
+
+An object of this class is used to format a keyword entries object.
+
+=head1 METHODS
+
+=over 4
+
+=item new()
+
+Constructor.
+
+=item format($entries)
+
+Returns a simple string representation of $entries.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Formatter::Entry|Fcm::Keyword::Formatter::Entry>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entry.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entry.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entry.pm	(revision 1981)
@@ -0,0 +1,64 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Formatter::Entry;
+
+################################################################################
+# Constructor
+sub new {
+    my ($class) = @_;
+    return bless(\do{my $annon_scalar}, $class);
+}
+
+################################################################################
+# Formats a keyword entry
+sub format {
+    my ($self, $entry) = @_;
+    return sprintf("%s = %s\n", $entry->get_key(), $entry->get_value());
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Formatter::Entry
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword::Formatter::Entry;
+    $formatter = Fcm::Keyword::Formatter::Entry->new();
+    print($formatter->format($entry));
+
+=head1 DESCRIPTION
+
+An object of this class is used to format a keyword entry.
+
+=head1 METHODS
+
+=over 4
+
+=item new()
+
+Constructor.
+
+=item format($entry)
+
+Returns a simple string representation of $entry.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entry/Location.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entry/Location.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Formatter/Entry/Location.pm	(revision 1981)
@@ -0,0 +1,72 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Formatter::Entry::Location;
+use base qw{Fcm::Keyword::Formatter::Entry};
+
+use Fcm::Config;
+use Fcm::Keyword::Formatter::Entries;
+
+################################################################################
+# Formats a keyword entry
+sub format {
+    my ($self, $entry) = @_;
+    my $return = $self->SUPER::format($entry);
+    for my $implied_entry (@{$entry->get_implied_entry_list()}) {
+        $return .= $self->SUPER::format($implied_entry);
+    }
+    if (@{$entry->get_revision_entries()->get_all_entries()}) {
+        my $formatter = Fcm::Keyword::Formatter::Entries->new();
+        $return .= "\n[revision keyword]\n";
+        $return .= $formatter->format($entry->get_revision_entries());
+    }
+    return $return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Formatter::Entry::Location
+
+=head1 SYNOPSIS
+
+    use Fcm::Keyword::Formatter::Entry::Location;
+    $formatter = Fcm::Keyword::Formatter::Entry::Location->new();
+    print($formatter->format($entry));
+
+=head1 DESCRIPTION
+
+An object of this class is used to format the detail in a location keyword entry.
+
+=head1 METHODS
+
+=over 4
+
+=item new()
+
+Constructor.
+
+=item format($entry)
+
+Returns a string representation of $entry.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Formatter::Entry|Fcm::Keyword::Formatter::Entry>,
+L<Fcm::Keyword::Formatter::Entries|Fcm::Keyword::Formatter::Entries>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Loader.pod
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Loader.pod	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Loader.pod	(revision 1981)
@@ -0,0 +1,43 @@
+=head1 NAME
+
+Fcm::Keyword::Loader
+
+=head1 SYNOPSIS
+
+    $loader->load_to($entries);
+
+=head1 DESCRIPTION
+
+This is an interface of a class that loads FCM keywords into a
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object.
+
+=head1 METHODS
+
+=over 4
+
+=item get_source()
+
+The name of the source where this loader loads its FCM keywords from.
+
+=item load_to($entries)
+
+Loads FCM keywords into $entries, which should be a
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object. Returns the number of
+successfully loaded entries.
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Loader::Config::Location|Fcm::Keyword::Loader::Config::Location>,
+L<Fcm::Keyword::Loader::Config::Revision|Fcm::Keyword::Loader::Config::Revision>,
+L<Fcm::Keyword::Loader::VC::Revision|Fcm::Keyword::Loader::VC::Revision>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Loader/Config/Location.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Loader/Config/Location.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Loader/Config/Location.pm	(revision 1981)
@@ -0,0 +1,128 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Loader::Config::Location;
+
+use Fcm::Config;
+
+my %IMPLIED_NAMESPACE_SUFFIX = (tr => 'trunk', br => 'branches', tg => 'tags');
+
+sub new {
+    my ($class) = @_;
+    return bless(\do{my $annon_scalar}, $class);
+}
+
+################################################################################
+# Returns 'Fcm::Config'
+sub get_source {
+    my ($self) = @_;
+    return 'Fcm::Config';
+}
+
+################################################################################
+# Loads location keywords from Fcm::Config to $entries
+sub load_to {
+    my ($self, $entries) = @_;
+    my $config = $self->get_source()->instance();
+    my $load_counter = 0;
+    for my $key (keys(%{$config->setting('URL')})) {
+        my $value = $config->setting('URL', $key);
+        my $location_component_pattern = $config->setting(
+            'URL_BROWSER_MAPPING', $key, 'LOCATION_COMPONENT_PATTERN');
+        my $browser_url_template = $config->setting(
+            'URL_BROWSER_MAPPING', $key, 'BROWSER_URL_TEMPLATE');
+        my $browser_rev_template = $config->setting(
+            'URL_BROWSER_MAPPING', $key, 'BROWSER_REV_TEMPLATE');
+        my $entry = $entries->add_entry(
+            $key,
+            $value,
+            {
+                location_component_pattern => $location_component_pattern,
+                browser_url_template       => $browser_url_template,
+                browser_rev_template       => $browser_rev_template,
+            },
+        );
+        $load_counter++;
+
+        # Set up implied keywords
+        for my $suffix (keys(%IMPLIED_NAMESPACE_SUFFIX)) {
+            my $value_suf = $value . '/' . $IMPLIED_NAMESPACE_SUFFIX{$suffix};
+            for my $join (q{_}, q{-}) {
+                my $implied_entry = $entries->add_entry(
+                    uc($key . $join . $suffix),
+                    $value_suf,
+                    {is_implied => 1},
+                );
+                push(@{$entry->get_implied_entry_list()}, $implied_entry);
+                $load_counter++;
+            }
+        }
+    }
+    return ($config->is_initialising() ? 0 : defined($load_counter));
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Loader::Config::Location
+
+=head1 SYNOPSIS
+
+    $loader = Fcm::Keyword::Loader::Config::Location->new();
+    $loader->load_to($entries);
+
+=head1 DESCRIPTION
+
+This class implements the L<Fcm::Keyword::Loader|Fcm::Keyword::Loader>
+interface.
+
+Loads location keywords from L<Fcm::Config|Fcm::Config> into a
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object containing
+L<Fcm::Keyword::Entry::Location|Fcm::Keyword::Entry::Location> objects.
+
+=head1 METHODS
+
+=over 4
+
+=item new()
+
+Constructor.
+
+=item get_source()
+
+Returns the string "L<Fcm::Config|Fcm::Config>".
+
+=item load_to($entries)
+
+Loads location keywords and implied keywords from L<Fcm::Config|Fcm::Config> to
+$entries. It also loads settings for mapping location to browser URL. Returns
+true on success. (However, if L<Fcm::Config|Fcm::Config> is initialising,
+returns false to force a reload next time.)
+
+=back
+
+=head1 TO DO
+
+Need a more flexible system for implied keywords.
+
+=head1 SEE ALSO
+
+L<Fcm::Config|Fcm::Config>,
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Loader|Fcm::Keyword::Loader>,
+L<Fcm::Keyword::Loader::Config::Revision|Fcm::Keyword::Loader::Config::Revision>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Loader/Config/Revision.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Loader/Config/Revision.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Loader/Config/Revision.pm	(revision 1981)
@@ -0,0 +1,110 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Loader::Config::Revision;
+
+use Fcm::Config;
+
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Returns the namespace where the revision keywords belong
+sub get_namespace {
+    my ($self) = @_;
+    return $self->{namespace};
+}
+
+################################################################################
+# Returns 'Fcm::Config'
+sub get_source {
+    my ($self) = @_;
+    return 'Fcm::Config';
+}
+
+################################################################################
+# Loads revision keywords from Fcm::Config to $entries
+sub load_to {
+    my ($self, $entries) = @_;
+    my $load_counter = 0;
+    my $config = $self->get_source()->instance();
+    my $rev_keyword_ref = $config->setting(
+        qw/URL_REVISION/,
+        uc($self->get_namespace()),
+    );
+    if ($rev_keyword_ref) {
+        for my $key (keys(%{$rev_keyword_ref})) {
+            $entries->add_entry($key, $rev_keyword_ref->{$key});
+            $load_counter++;
+        }
+    }
+    return ($config->is_initialising() ? 0 : defined($load_counter));
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Loader::Config::Revision
+
+=head1 SYNOPSIS
+
+    $loader = Fcm::Keyword::Loader::Config::Revision->new({namespace => $name});
+    $loader->load_to($entries);
+
+=head1 DESCRIPTION
+
+Loads revision keywords from L<Fcm::Config|Fcm::Config> into a
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object containing
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry> objects.
+
+=head1 METHODS
+
+=over 4
+
+=item C<new({namespace =E<gt> $namespace})>
+
+Constructor. The argument $namespace is the namespace where the revision
+keywords belong.
+
+=item get_namespace()
+
+Returns the namespace where the revision keywords belong.
+
+=item get_source()
+
+Returns the string "L<Fcm::Config|Fcm::Config>".
+
+=item load_to($entries)
+
+Loads revision keywords in the namespace given by C<$self-E<gt>get_namespace()>
+from L<Fcm::Config|Fcm::Config> to $entries. Returns true on success. (However,
+if L<Fcm::Config|Fcm::Config> is initialising, returns false to force a reload
+next time.)
+
+=back
+
+=head1 SEE ALSO
+
+L<Fcm::Config|Fcm::Config>,
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Entry::Location|Fcm::Keyword::Entry::Location>,
+L<Fcm::Keyword::Loader|Fcm::Keyword::Loader>,
+L<Fcm::Keyword::Loader::Config::Location|Fcm::Keyword::Loader::Config::Location>
+L<Fcm::Keyword::Loader::VC::Revision|Fcm::Keyword::Loader::VC::Revision>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/Keyword/Loader/VC/Revision.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Keyword/Loader/VC/Revision.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Keyword/Loader/VC/Revision.pm	(revision 1981)
@@ -0,0 +1,103 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Keyword::Loader::VC::Revision;
+
+use Fcm::Util qw{run_command};
+
+sub new {
+    my ($class, $args_ref) = @_;
+    return bless({%{$args_ref}}, $class);
+}
+
+################################################################################
+# Returns the VC location where revision keywords will be loaded from
+sub get_source {
+    my ($self) = @_;
+    return $self->{source};
+}
+
+################################################################################
+# Loads revision keywords from $self->get_source() to $entries
+sub load_to {
+    my ($self, $entries) = @_;
+    my @lines = run_command(
+        [qw{svn pg fcm:revision}, $self->get_source()],
+        DEVNULL => 1,
+        ERROR   => 'ignore',
+        METHOD  => 'qx',
+    );
+    my $load_counter = 0;
+    for my $line (@lines) {
+        chomp($line);
+        my ($key, $value) = split(qr{\s+ = \s+}xms, $line);
+        if ($key && $value) {
+            $entries->add_entry($key, $value);
+            $load_counter++;
+        }
+    }
+    return defined($load_counter);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::Keyword::Loader::VC::Revision
+
+=head1 SYNOPSIS
+
+    $loader = Fcm::Keyword::Loader::VC::Revision->new({source => $source});
+    $loader->load_to($entries);
+
+=head1 DESCRIPTION
+
+Loads revision keywords from a VC location into a
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries> object containing
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry> objects.
+
+=head1 METHODS
+
+=over 4
+
+=item C<new({source =E<gt> $source})>
+
+Constructor. The argument $source is the VC location from which revision
+keywords will be loaded from.
+
+=item get_source()
+
+Returns the source VC location from which revision keywords will be loaded
+from.
+
+=item load_to($entries)
+
+Loads revision keywords from C<$self-E<gt>get_source()> to $entries.
+
+=back
+
+=head1 TO DO
+
+Abstract away the call to the VC system, which assumes the Subversion shell
+client at the moment.
+
+=head1 SEE ALSO
+
+L<Fcm::Keyword|Fcm::Keyword>,
+L<Fcm::Keyword::Entries|Fcm::Keyword::Entries>,
+L<Fcm::Keyword::Entry|Fcm::Keyword::Entry>,
+L<Fcm::Keyword::Entry::Location|Fcm::Keyword::Entry::Location>,
+L<Fcm::Keyword::Loader|Fcm::Keyword::Loader>,
+L<Fcm::Keyword::Loader::Config::Revision|Fcm::Keyword::Loader::Config::Revision>
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/lib/Fcm/ReposBranch.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/ReposBranch.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/ReposBranch.pm	(revision 1981)
@@ -0,0 +1,506 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::ReposBranch
+#
+# DESCRIPTION
+#   This class contains methods for gathering information for a repository
+#   branch. It currently supports Subversion repository and local user
+#   directory.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use warnings;
+use strict;
+
+package Fcm::ReposBranch;
+use base qw{Fcm::Base};
+
+use Fcm::CfgLine;
+use Fcm::Keyword;
+use Fcm::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 = Fcm::ReposBranch->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::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 = Fcm::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 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 = (Fcm::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,
+        );
+
+        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 && -r $path && !-l $path) {
+          my $dir_path      = dirname($path);
+          my $rel_dir_path  = File::Spec->abs2rel($dir_path, $root_locator);
+          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 = join(q{/}, $root_locator, $rel_dir_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{$Fcm::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($Fcm::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 Fcm::ReposBranch
+#   instance. It returns the total number of used sub-directories in the
+#   current repositories.
+# ------------------------------------------------------------------------------
+
+sub add_base_dirs {
+  my $self = shift;
+  my $base = shift;
+
+  my %base_dirs = %{ $base->dirs };
+
+  for my $key (keys %base_dirs) {
+    # Remove repository root from base directories
+    if ($base_dirs{$key} eq $base->repos) {
+      $base_dirs{$key} = '';
+
+    } else {
+      $base_dirs{$key} = substr $base_dirs{$key}, length ($base->repos) + 1;
+    }
+
+    # Append base directories to current repository root
+    $self->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 . $Fcm::Config::DELIMITER . $self->tag;
+  push @return, Fcm::CfgLine->new (
+    label => $self->cfglabel ('REPOS') . $Fcm::Config::DELIMITER . $suffix,
+    value => $self->repos,
+  ) if $self->repos;
+
+  push @return, Fcm::CfgLine->new (
+    label => $self->cfglabel ('REVISION') . $Fcm::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 . $Fcm::Config::DELIMITER . $self->tag;
+    $dsuffix = $suffix if $value ne $self->dirs ($key) and $key eq join (
+      $Fcm::Config::DELIMITER, $self->package, File::Spec->splitdir ($value)
+    );
+
+    push @return, Fcm::CfgLine->new (
+      label => $self->cfglabel ('DIRS') . $Fcm::Config::DELIMITER . $dsuffix,
+      value => $value,
+    );
+  }
+
+  push @return, Fcm::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, METHOD => 'qx', TIME => $self->verbose() > 2,
+    )
+  };
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/SrcDirLayer.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/SrcDirLayer.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/SrcDirLayer.pm	(revision 1981)
@@ -0,0 +1,264 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::SrcDirLayer
+#
+# DESCRIPTION
+#   This class contains methods to manipulate the extract of a source
+#   directory from a branch of a (Subversion) repository.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use warnings;
+use strict;
+
+package Fcm::SrcDirLayer;
+use base qw{Fcm::Base};
+
+use Fcm::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 = Fcm::SrcDirLayer->new (%args);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::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 = Fcm::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 (!-w $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: vendors/fcm/1-5/lib/Fcm/Timer.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Timer.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Timer.pm	(revision 1981)
@@ -0,0 +1,72 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Timer
+#
+# DESCRIPTION
+#   This is a package of timer utility used by the FCM command.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Timer;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Exports
+our (@ISA, @EXPORT, @EXPORT_OK);
+
+sub timestamp_command;
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(timestamp_command);
+
+# ------------------------------------------------------------------------------
+
+# Module level variables
+my %cmd_start_time = (); # Command start time, (key = command, value = time)
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = &Fcm::Timer::timestamp_command ($command[, $status]);
+#
+# DESCRIPTION
+#   This function returns a string adding to $command a prefix according the
+#   value of $status. If $status is not specified or does not match the word
+#   "end", the status is assumed to be "start". At "start", the prefix will
+#   contain the current timestamp. If $status is the word "end", the prefix
+#   will contain the total time taken since this function was called with the
+#   same $command at the "start" status.
+# ------------------------------------------------------------------------------
+
+sub timestamp_command {
+  (my $command, my $status) = @_;
+
+  my $prefix;
+  if ($status and $status =~ /end/i) {
+    # Status is "end", insert time taken
+    my $lapse = time () - $cmd_start_time{$command};
+    $prefix = sprintf "# Time taken: %12d s=> ", $lapse;
+
+  } else {
+    # Status is "start", insert time stamp
+    $cmd_start_time{$command} = time;
+
+    (my $sec, my $min, my $hour, my $mday, my $mon, my $year) = localtime;
+    $prefix = sprintf "# Start: %04d-%02d-%02d %02d:%02d:%02d=> ",
+              $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
+  }
+
+  return $prefix . $command . "\n";
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: vendors/fcm/1-5/lib/Fcm/Util.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Util.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Util.pm	(revision 1981)
@@ -0,0 +1,552 @@
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Util
+#
+# DESCRIPTION
+#   This is a package of misc utilities used by the FCM command.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use warnings;
+use strict;
+
+package Fcm::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 Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+
+# Module level variables
+my %svn_info       = (); # "svn info" log, (key1 = path,
+                         # key2 = URL, Revision, Last Changed Rev)
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %srcdir = &Fcm::Util::find_file_in_path ($file, \@path);
+#
+# DESCRIPTION
+#   Search $file in @path. Returns the full path of the $file if it is found
+#   in @path. Returns "undef" if $file is not found in @path.
+# ------------------------------------------------------------------------------
+
+sub find_file_in_path {
+  my ($file, $path) = @_;
+
+  for my $dir (@$path) {
+    my $full_file = File::Spec->catfile ($dir, $file);
+    return $full_file if -e $full_file;
+  }
+
+  return undef;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $expanded_path = &Fcm::Util::expand_tilde ($path);
+#
+# DESCRIPTION
+#   Returns an expanded path if $path is a path that begins with a tilde (~).
+# ------------------------------------------------------------------------------
+
+sub expand_tilde {
+  my $file = $_[0];
+
+  $file =~ s#^~([^/]*)#$1 ? (getpwnam $1)[7] : ($ENV{HOME} || $ENV{LOGDIR})#ex;
+
+  # Expand . and ..
+  while ($file =~ s#/+\.(?:/+|$)#/#g) {next}
+  while ($file =~ s#/+[^/]+/+\.\.(?:/+|$)#/#g) {next}
+
+  # Remove trailing /
+  $file =~ s#/*$##;
+
+  return $file;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &Fcm::Util::touch_file ($file);
+#
+# DESCRIPTION
+#   Touch $file if it exists. Create $file if it does not exist. Return 1 for
+#   success or 0 otherwise.
+# ------------------------------------------------------------------------------
+
+sub touch_file {
+  my $file = $_[0];
+  my $rc   = 1;
+
+  if (-e $file) {
+    my $now = time;
+    $rc = utime $now, $now, $file;
+
+  } else {
+    mkpath dirname ($file) unless -d dirname ($file);
+
+    $rc = open FILE, '>', $file;
+    $rc = close FILE if $rc;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = &is_wc ([$path]);
+#
+# DESCRIPTION
+#   Returns true if current working directory (or $path) is a Subversion
+#   working copy.
+# ------------------------------------------------------------------------------
+
+sub is_wc {
+  my $path = @_ ? $_[0] : cwd ();
+
+  if (-d $path) {
+    return (-e File::Spec->catfile ($path, qw/.svn format/)) ? 1 : 0;
+
+  } elsif (-f $path) {
+    return (-e File::Spec->catfile (dirname ($path), qw/.svn format/)) ? 1 : 0;
+
+  } else {
+    return 0;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = &is_url ($url);
+#
+# DESCRIPTION
+#   Returns true if $url is a URL.
+# ------------------------------------------------------------------------------
+
+sub is_url {
+  # This should handle URL beginning with svn://, http:// and svn+ssh://
+  return ($_[0] =~ m#^[\+\w]+://#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $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  = Fcm::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: vendors/fcm/1-5/lib/Fcm/Util/ClassLoader.pm
===================================================================
--- vendors/fcm/1-5/lib/Fcm/Util/ClassLoader.pm	(revision 1981)
+++ vendors/fcm/1-5/lib/Fcm/Util/ClassLoader.pm	(revision 1981)
@@ -0,0 +1,80 @@
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+package Fcm::Util::ClassLoader;
+use base qw{Exporter};
+
+our @EXPORT_OK = qw{load};
+
+use Carp qw{croak};
+use Fcm::Exception;
+
+sub load {
+    my ($class, $test_method) = @_;
+    if (!$test_method) {
+        $test_method = 'new';
+    }
+    if (!UNIVERSAL::can($class, $test_method)) {
+        eval('require ' . $class);
+        if ($@) {
+            croak(Fcm::Exception->new({message => sprintf(
+                "%s: class loading failed: %s", $class, $@,
+            )}));
+        }
+    }
+    return $class;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Fcm::ClassLoader
+
+=head1 SYNOPSIS
+
+    use Fcm::Util::ClassLoader;
+    $load_ok = Fcm::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<Fcm::Exception|Fcm::Exception>.
+
+=item load($class)
+
+Shorthand for C<load($class, 'new')>.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item L<Fcm::Exception|Fcm::Exception>
+
+The load($class,$test_method) function croak() with this exception if it fails
+to load the specified class.
+
+=back
+
+=head1 COPYRIGHT
+
+E<169> Crown copyright Met Office. All rights reserved.
+
+=cut
Index: vendors/fcm/1-5/man/man1/fcm.1
===================================================================
--- vendors/fcm/1-5/man/man1/fcm.1	(revision 1981)
+++ vendors/fcm/1-5/man/man1/fcm.1	(revision 1981)
@@ -0,0 +1,35 @@
+.\" Process this file with
+.\" groff -man -Tascii fcm.1
+.\"
+.TH fcm 1 "" "" "User Commands"
+.SH NAME
+fcm - command line client for the Flexible Configuration Management system
+.SH SYNOPSIS
+.B fcm
+.I command
+[
+.I options
+] [
+.I args
+]
+.SH OVERVIEW
+.B fcm
+is the command line client for code management commands, the extract system and
+the build system of the Flexible Configuration Management (FCM) system.
+For full detail of the system, please refer to the FCM user guide, which you
+should receive with this distribution in both HTML and PDF formats.
+.PP
+Run "fcm help" to access the built-in tool documentation.
+.SH AUTHOR
+FCM Team <fcm-team@metoffice.gov.uk>.
+Please feedback any bug reports or feature requests to us by e-mail.
+.SH COPYRIGHT
+British Crown Copyright \(co Met Office. All rights reserved.
+.PP
+You can use this release of
+.B FCM
+freely under the terms of the FCM LICENSE,
+which you should receive with this distribution.
+.SH SEE ALSO
+.BR svn (1),
+.BR perl (1)
Index: vendors/fcm/1-5/t/Fcm/Build/Fortran-extract-interface-result.f90
===================================================================
--- vendors/fcm/1-5/t/Fcm/Build/Fortran-extract-interface-result.f90	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Build/Fortran-extract-interface-result.f90	(revision 1981)
@@ -0,0 +1,70 @@
+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_end()
+end subroutine sub_with_end
+end interface
Index: vendors/fcm/1-5/t/Fcm/Build/Fortran-extract-interface-source.f90
===================================================================
--- vendors/fcm/1-5/t/Fcm/Build/Fortran-extract-interface-source.f90	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Build/Fortran-extract-interface-source.f90	(revision 1981)
@@ -0,0 +1,181 @@
+! 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
+
+! 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
+
+! 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 a variable named "end"
+subroutine sub_with_end()
+integer :: end
+end = 0
+end subroutine sub_with_end
Index: vendors/fcm/1-5/t/Fcm/Build/Fortran.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Build/Fortran.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Build/Fortran.t	(revision 1981)
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../../../lib";
+
+use Test::More (tests => 3);
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    my $CLASS = 'Fcm::Build::Fortran';
+    use_ok($CLASS);
+    my $util = $CLASS->new();
+    isa_ok($util, $CLASS);
+    test_extract_interface($util);
+}
+
+sub test_extract_interface {
+    my ($util) = @_;
+    my $root = ($0 =~ qr{\A(.+)\.t\z}msx)[0];
+    my $f90 = $root . '-extract-interface-source.f90';
+    my $f90_interface = $root . '-extract-interface-result.f90';
+    open(my($handle_for_source), '<', $f90) || die("$f90: $!");
+    my @actual_lines = $util->extract_interface($handle_for_source);
+    close($handle_for_source);
+    open(my($handle_for_result), '<', $f90_interface)
+        || die("$f90_interface: $!");
+    my @expected_lines = readline($handle_for_result);
+    close($handle_for_result);
+    is_deeply(\@actual_lines, \@expected_lines, 'extract_interface');
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI.t	(revision 1981)
@@ -0,0 +1,237 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+
+################################################################################
+# A sub-class of Fcm::CLI::Invoker for testing
+{
+    package TestInvoker;
+    use base qw{Fcm::CLI::Invoker};
+
+    our $LATEST_INSTANCE;
+
+    ############################################################################
+    # Returns a test attrib
+    sub get_test_attrib {
+        my ($self) = @_;
+        return $self->{test_attrib};
+    }
+
+    ############################################################################
+    # Invokes the sub-system
+    sub invoke {
+        my ($self) = @_;
+        $LATEST_INSTANCE = $self;
+    }
+}
+
+use Fcm::CLI::Config;
+use Fcm::CLI::Subcommand;
+use Test::More (tests => 25);
+
+main();
+
+sub main {
+    use_ok('Fcm::CLI');
+    test_invalid_subcommand();
+    test_invoker_not_implemented();
+    test_normal_invoke();
+    test_help_invoke();
+    test_get_invoker_normal();
+    test_load_invoker_class();
+}
+
+################################################################################
+# Tests to ensure that an invalid subcommand results in an exception
+sub test_invalid_subcommand {
+    Fcm::CLI::Config->instance({core_subcommands => [], vc_subcommands => []});
+    eval {
+        local(@ARGV) = ('foo');
+        Fcm::CLI::invoke();
+    };
+    like($@, qr{foo: unknown command}, 'invalid subcommand');
+}
+
+################################################################################
+# Tests to ensure that an unimplemented invoker results in an exception
+sub test_invoker_not_implemented {
+    Fcm::CLI::Config->instance({
+        core_subcommands => [
+            Fcm::CLI::Subcommand->new({names => ['foo']}),
+            Fcm::CLI::Subcommand->new({
+                names => ['bar'], invoker_class => 'barley',
+            }),
+        ],
+        vc_subcommands   => [],
+    });
+    eval {
+        local(@ARGV) = ('foo');
+        Fcm::CLI::invoke();
+    };
+    like($@, qr{foo: \s command \s not \s implemented}xms, 'not implemented');
+    eval {
+        local(@ARGV) = ('bar');
+        Fcm::CLI::invoke();
+    };
+    like($@, qr{barley: \s class \s loading \s failed}xms, 'not implemented');
+}
+
+################################################################################
+# Tests normal usage of invoke
+sub test_normal_invoke {
+    my $prefix = "normal invoke";
+    Fcm::CLI::Config->instance({
+        core_subcommands => [
+            Fcm::CLI::Subcommand->new({
+                names          => ['foo'],
+                invoker_class  => 'TestInvoker',
+                invoker_config => {test_attrib => 'test_attrib value'},
+            }),
+        ],
+        vc_subcommands   => [],
+    });
+    ok(!$TestInvoker::LATEST_INSTANCE, "$prefix: invoker not called");
+    local(@ARGV) = ('foo', 'bar', 'baz');
+    Fcm::CLI::invoke();
+    my $invoker = $TestInvoker::LATEST_INSTANCE;
+    if (!$invoker) {
+        fail($prefix);
+    }
+    else {
+        is($invoker->get_command(), 'foo', "$prefix: invoker command");
+        is_deeply({$invoker->get_options()}, {}, "$prefix: invoker options");
+        is_deeply([$invoker->get_arguments()], ['bar', 'baz'],
+            "$prefix: invoker arguments");
+        is($invoker->get_test_attrib(), 'test_attrib value',
+            "$prefix: invoker test attrib");
+    }
+    $TestInvoker::LATEST_INSTANCE = undef;
+}
+
+################################################################################
+# Tests help usage of invoke
+sub test_help_invoke {
+    my $prefix = "help invoke";
+    Fcm::CLI::Config->instance({
+        core_subcommands => [
+            Fcm::CLI::Subcommand->new({
+                names          => ['foo'],
+                invoker_class  => 'TestInvoker',
+                invoker_config => {test_attrib => 'test_attrib value normal'},
+                options        => [
+                    Fcm::CLI::Option->new({name => 'foo', is_help => 1}),
+                ],
+            }),
+            Fcm::CLI::Subcommand->new({
+                names          => [q{}],
+                invoker_class  => 'TestInvoker',
+            }),
+        ],
+        vc_subcommands   => [],
+    });
+    ok(!$TestInvoker::LATEST_INSTANCE, "$prefix: invoker not called");
+    local(@ARGV) = ('foo', '--foo');
+    Fcm::CLI::invoke();
+    my $invoker = $TestInvoker::LATEST_INSTANCE;
+    if (!$invoker) {
+        fail($prefix);
+    }
+    else {
+        is_deeply([$invoker->get_arguments()], ['foo'],
+            "$prefix: invoker argument");
+    }
+    $TestInvoker::LATEST_INSTANCE = undef;
+}
+
+################################################################################
+# Tests getting an invoker
+sub test_get_invoker_normal {
+    my $prefix = 'get invoker normal';
+    my @options = (
+        Fcm::CLI::Option->new({name => 'foo'}),
+        Fcm::CLI::Option->new({name => 'bar'}),
+        Fcm::CLI::Option->new({name => 'baz'}),
+        Fcm::CLI::Option->new({
+            name      => q{pork},
+            delimiter => q{,},
+            has_arg   => Fcm::CLI::Option->ARRAY_ARG,
+        }),
+    );
+    my $subcommand = Fcm::CLI::Subcommand->new({options => \@options});
+    my %TEST = (
+        test1 => {
+            argv      => ['--foo', '--bar', 'egg', 'ham', 'sausage'],
+            command   => 'command',
+            options   => {foo => 1, bar => 1},
+            arguments => ['egg', 'ham', 'sausage'],
+        },
+        test2 => {
+            argv      => ['--baz', '--foo', '--bar'],
+            command   => 'test',
+            options   => {foo => 1, bar => 1, baz => 1},
+            arguments => [],
+        },
+        test3 => {
+            argv      => ['egg', 'ham', 'sausage'],
+            command   => 'meal',
+            options   => {},
+            arguments => ['egg', 'ham', 'sausage'],
+        },
+        test4 => {
+            argv      => ['--pork', 'ham', '--pork', 'sausage'],
+            command   => 'pig',
+            options   => {pork => ['ham', 'sausage']},
+            arguments => [],
+        },
+        test5 => {
+            argv      => ['--pork', 'ham,sausage', '--pork', 'bacon', 'liver'],
+            command   => 'pig',
+            options   => {pork => ['ham', 'sausage', 'bacon']},
+            arguments => ['liver'],
+        },
+    );
+    for my $key (keys(%TEST)) {
+        local(@ARGV) = @{$TEST{$key}{argv}};
+        my ($opts_ref, $args_ref) = Fcm::CLI::_parse_argv_using($subcommand);
+        is_deeply($opts_ref, $TEST{$key}{options},
+            "$prefix $key: get options");
+        is_deeply($args_ref, $TEST{$key}{arguments},
+            "$prefix $key: get arguments");
+    }
+    my %BAD_TEST = (
+        test1 => {
+            argv => ['--egg', '--bar', 'foo', 'ham', 'sausage'],
+        },
+        test2 => {
+            argv => ['--foo=egg'],
+        },
+    );
+    for my $key (keys(%BAD_TEST)) {
+        local(@ARGV) = @{$BAD_TEST{$key}{argv}};
+        eval {
+            Fcm::CLI::_parse_argv_using($subcommand);
+        };
+        isa_ok($@, 'Fcm::CLI::Exception', "$prefix $key");
+    }
+}
+
+################################################################################
+# Tests loading an invoker with a different class
+sub test_load_invoker_class {
+    my $prefix = 'get invoker class';
+    eval {
+        my $subcommand = Fcm::CLI::Subcommand->new({invoker_class => 'foo'});
+        Fcm::CLI::_load_invoker_class_of($subcommand);
+    };
+    isa_ok($@, 'Fcm::Exception', "$prefix");
+
+    my $invoker_class = 'Fcm::CLI::Invoker::ConfigSystem';
+    my $subcommand
+        = Fcm::CLI::Subcommand->new({invoker_class => $invoker_class});
+    my $class = Fcm::CLI::_load_invoker_class_of($subcommand);
+    is($class, $invoker_class, "$prefix: $invoker_class");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Config.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Config.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Config.t	(revision 1981)
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Fcm::CLI::Config::Default;
+use Fcm::CLI::Subcommand;
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Config';
+    use_ok($class);
+    test_get_instance($class);
+    test_get_subcommand_of_string($class);
+}
+
+################################################################################
+# Tests normal usage of getting an instance
+sub test_get_instance {
+    my ($class) = @_;
+    my $prefix = 'constructor';
+    my $cli_config = $class->instance();
+    isa_ok($cli_config, $class, "$prefix");
+    is_deeply(
+        [$cli_config->get_core_subcommands()],
+        \@Fcm::CLI::Config::Default::CORE_SUBCOMMANDS,
+        "$prefix: default core",
+    );
+    is_deeply(
+        [$cli_config->get_vc_subcommands()],
+        \@Fcm::CLI::Config::Default::VC_SUBCOMMANDS,
+        "$prefix: default vc",
+    );
+    is_deeply(
+        [$cli_config->get_subcommands()],
+        [$cli_config->get_core_subcommands(), $cli_config->get_vc_subcommands()],
+        "$prefix: default",
+    );
+    is($class->instance(), $cli_config, "$prefix: same instance");
+    isnt($class->instance({}), $cli_config, "$prefix: not the same instance");
+    my $empty_cli_config = $class->instance({
+        core_subcommands => [],
+        vc_subcommands   => [],
+    });
+    is_deeply(
+        [$empty_cli_config->get_core_subcommands()],
+        [],
+        "$prefix: empty core",
+    );
+    is_deeply(
+        [$empty_cli_config->get_vc_subcommands()],
+        [],
+        "$prefix: empty vc",
+    );
+    is_deeply(
+        [$empty_cli_config->get_subcommands()],
+        [],
+        "$prefix: empty",
+    );
+}
+
+################################################################################
+# Tests getting a subcommand of a matching string
+sub test_get_subcommand_of_string {
+    my ($class) = @_;
+    my $prefix = 'get_subcommand_of';
+    my $foo_subcommand = Fcm::CLI::Subcommand->new({names => ['food', 'foo']});
+    my $bar_subcommand = Fcm::CLI::Subcommand->new({names => ['barley', 'bar']});
+    my $cli_config = $class->instance({
+        core_subcommands => [$foo_subcommand, $bar_subcommand],
+        vc_subcommands   => [],
+    });
+    for my $key ('food', 'foo') {
+        is($cli_config->get_subcommand_of($key), $foo_subcommand,
+            "$prefix: $key");
+    }
+    for my $key ('barley', 'bar') {
+        is($cli_config->get_subcommand_of($key), $bar_subcommand,
+            "$prefix: $key");
+    }
+    is($cli_config->get_subcommand_of('baz'), undef, "$prefix: baz");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Config/Default.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Config/Default.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Config/Default.t	(revision 1981)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Config::Default';
+    use_ok($class);
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Exception.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Exception.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Exception.t	(revision 1981)
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Exception';
+    use_ok($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = @_;
+    my $prefix = 'normal';
+    my $e = $class->new({message => 'message'});
+    isa_ok($e, $class, $prefix);
+    is("$e", "$class: message\n", "$prefix: as_string()");
+    is($e->get_message(), 'message', "$prefix: get_message()");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker.t	(revision 1981)
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker';
+    use_ok($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = @_;
+    my $prefix = "normal";
+    my %OPTIONS   = (option1 => 1, option2 => 2, option3 => 3);
+    my @ARGUMENTS = ('argument 1', 'argument 2');
+    my $invoker = $class->new({
+        command   => 'command',
+        options   => \%OPTIONS,
+        arguments => \@ARGUMENTS,
+    });
+    isa_ok($invoker, $class, $prefix);
+    is($invoker->get_command(), 'command', "$prefix: command");
+    is_deeply({$invoker->get_options()}, \%OPTIONS, "$prefix: options");
+    is_deeply([$invoker->get_arguments()], \@ARGUMENTS, "$prefix: arguments");
+    eval {
+        $invoker->invoke();
+    };
+    isa_ok($@, 'Fcm::CLI::Exception', "$prefix: invoke");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/Browser.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/Browser.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/Browser.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::Browser';
+    use_ok($class);
+}
+
+# TODO: actual unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/CM.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/CM.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/CM.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::CM';
+    use_ok($class);
+}
+
+# TODO: actual unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/CfgPrinter.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/CfgPrinter.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/CfgPrinter.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::CfgPrinter';
+    use_ok($class);
+}
+
+# TODO: actual unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/ConfigSystem.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/ConfigSystem.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/ConfigSystem.t	(revision 1981)
@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+################################################################################
+# A mock Fcm::ConfigSystem object
+{
+    package MockConfigSystem;
+    use base qw{Fcm::ConfigSystem};
+
+    our $LATEST_INVOKED_INSTANCE;
+
+    ############################################################################
+    # Returns the arguments to the last invoke() call
+    sub get_invoke_args {
+        my ($self) = @_;
+        return $self->{invoke_args};
+    }
+
+    ############################################################################
+    # Does nothing but captures the arguments
+    sub invoke {
+        my ($self, %args) = @_;
+        $LATEST_INVOKED_INSTANCE = $self;
+        $self->{invoke_args} = \%args;
+        return 1;
+    }
+}
+
+use Cwd;
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::ConfigSystem';
+    use_ok($class);
+    test_invoke($class);
+}
+
+################################################################################
+# Tests normal usage of invoke()
+sub test_invoke {
+    my ($class) = @_;
+    my $prefix = "invoke";
+    my %TEST = (
+        test1 => {
+            command            => 'pig',
+            options            => {'egg' => 1},
+            arguments          => ['bacon'],
+            expected_options   => {FOO => undef, BAR_BAZ => undef, EGGS => 1},
+            expected_arguments => 'bacon',
+        },
+        test2 => {
+            command            => 'pig',
+            options            => {'foo' => 1, 'bar-baz' => 1},
+            arguments          => [],
+            expected_options   => {FOO => 1, BAR_BAZ => 1, EGGS => undef},
+            expected_arguments => cwd(),
+        }
+    );
+    for my $key (keys(%TEST)) {
+        my $invoker = $class->new({
+            command            => $TEST{$key}{command},
+            options            => $TEST{$key}{options},
+            arguments          => $TEST{$key}{arguments},
+            impl_class         => 'MockConfigSystem',
+            cli2invoke_key_map => {
+                'foo' => 'FOO', 'bar-baz' => 'BAR_BAZ', 'egg' => 'EGGS',
+            },
+        });
+        isa_ok($invoker, 'Fcm::CLI::Invoker::ConfigSystem', "$prefix: $key");
+        $invoker->invoke();
+        my $config_system_instance = $MockConfigSystem::LATEST_INVOKED_INSTANCE;
+        isa_ok(
+            $config_system_instance,
+            'Fcm::ConfigSystem',
+            "$prefix: $key: Fcm::ConfigSystem",
+        );
+        is(
+            $config_system_instance->cfg()->src(),
+            $TEST{$key}{expected_arguments},
+            "$prefix: $key: cfg()->src()",
+        );
+        is_deeply(
+            $config_system_instance->get_invoke_args(),
+            $TEST{$key}{expected_options},
+            "$prefix: $key: invoke args",
+        );
+    }
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/ExtractConfigComparator.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/ExtractConfigComparator.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/ExtractConfigComparator.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::ExtractConfigComparator';
+    use_ok($class);
+}
+
+# TODO: actual unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/GUI.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/GUI.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/GUI.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::GUI';
+    use_ok($class);
+}
+
+# TODO: actual unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/Help.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/Help.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/Help.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::Help';
+    use_ok($class);
+}
+
+# TODO: actual unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Invoker/KeywordPrinter.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Invoker/KeywordPrinter.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Invoker/KeywordPrinter.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Invoker::KeywordPrinter';
+    use_ok($class);
+}
+
+# TODO: actual unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Option.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Option.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Option.t	(revision 1981)
@@ -0,0 +1,168 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Option';
+    use_ok($class);
+    test_simplest($class);
+    test_simplest_scalar_arg($class);
+    test_simplest_array_arg($class);
+    test_simplest_hash_arg($class);
+    test_simple($class);
+    test_simple_scalar_arg($class);
+    test_simple_array_arg($class);
+    test_simple_hash_arg($class);
+    test_long_letter($class);
+}
+
+################################################################################
+# Tests simplest usage
+sub test_simplest {
+    my ($class) = @_;
+    my $prefix = 'simplest';
+    my $option = $class->new({
+        delimiter   => 'delimiter-value',
+        description => 'description value',
+        name        => 'name-value',
+    });
+    isa_ok($option, $class);
+    is($option->get_delimiter(), 'delimiter-value', "$prefix: delimiter");
+    is($option->get_description(), 'description value', "$prefix: description");
+    is($option->get_name(), 'name-value', "$prefix: name");
+    is($option->get_letter(), undef, "$prefix: letter");
+    ok(!$option->has_arg(), "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value', "$prefix: has arg");
+}
+
+################################################################################
+# Tests simplest usage with a scalar argument
+sub test_simplest_scalar_arg {
+    my ($class) = @_;
+    my $prefix = 'simplest scalar arg';
+    my $option = $class->new({
+        description => 'description value',
+        name        => 'name-value',
+        has_arg     => $class->SCALAR_ARG,
+    });
+    isa_ok($option, $class);
+    is($option->has_arg(), $class->SCALAR_ARG, "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value=s', "$prefix: has arg");
+}
+
+################################################################################
+# Tests simplest usage with array argument
+sub test_simplest_array_arg {
+    my ($class) = @_;
+    my $prefix = 'simplest array arg';
+    my $option = $class->new({
+        description => 'description value',
+        name        => 'name-value',
+        has_arg     => $class->ARRAY_ARG,
+    });
+    isa_ok($option, $class);
+    is($option->has_arg(), $class->ARRAY_ARG, "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value=s@', "$prefix: has arg");
+}
+
+################################################################################
+# Tests simplest usage with hash argument
+sub test_simplest_hash_arg {
+    my ($class) = @_;
+    my $prefix = 'simplest hash arg';
+    my $option = $class->new({
+        description => 'description value',
+        name        => 'name-value',
+        has_arg     => $class->HASH_ARG,
+    });
+    isa_ok($option, $class);
+    is($option->has_arg(), $class->HASH_ARG, "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value=s%', "$prefix: has arg");
+}
+
+################################################################################
+# Tests simple usage
+sub test_simple {
+    my ($class) = @_;
+    my $prefix = 'simple';
+    my $option = $class->new({
+        description => 'description value',
+        name        => 'name-value',
+        letter      => 'n',
+    });
+    isa_ok($option, $class);
+    is($option->get_description(), 'description value', "$prefix: description");
+    is($option->get_name(), 'name-value', "$prefix: name");
+    is($option->get_letter(), 'n', "$prefix: letter");
+    is($option->has_arg(), $class->NO_ARG, "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value|n', "$prefix: has arg");
+}
+
+################################################################################
+# Tests simple usage with a scalar argument
+sub test_simple_scalar_arg {
+    my ($class) = @_;
+    my $prefix = 'simple scalar arg';
+    my $option = $class->new({
+        description => 'description value',
+        name        => 'name-value',
+        letter      => 'n',
+        has_arg     => $class->SCALAR_ARG,
+    });
+    isa_ok($option, $class);
+    is($option->has_arg(), $class->SCALAR_ARG, "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value|n=s', "$prefix: has arg");
+}
+
+################################################################################
+# Tests simplest usage with array argument
+sub test_simple_array_arg {
+    my ($class) = @_;
+    my $prefix = 'simple array arg';
+    my $option = $class->new({
+        description => 'description value',
+        name        => 'name-value',
+        letter      => 'n',
+        has_arg     => $class->ARRAY_ARG,
+    });
+    isa_ok($option, $class);
+    is($option->has_arg(), $class->ARRAY_ARG, "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value|n=s@', "$prefix: has arg");
+}
+
+################################################################################
+# Tests simplest usage with hash argument
+sub test_simple_hash_arg {
+    my ($class) = @_;
+    my $prefix = 'simple hash arg';
+    my $option = $class->new({
+        description => 'description value',
+        name        => 'name-value',
+        letter      => 'n',
+        has_arg     => $class->HASH_ARG,
+    });
+    isa_ok($option, $class);
+    is($option->has_arg(), $class->HASH_ARG, "$prefix: has arg");
+    is($option->get_arg_for_getopt_long(), 'name-value|n=s%', "$prefix: has arg");
+}
+
+################################################################################
+# Tests longer than 1 letter
+sub test_long_letter {
+    my ($class) = @_;
+    my $prefix = 'long letter';
+    my $option = $class->new({
+        name   => 'name-value',
+        letter => 'name',
+    });
+    isa_ok($option, $class);
+    is($option->get_letter(), 'n', "$prefix: letter");
+    is($option->get_arg_for_getopt_long(), 'name-value|n', "$prefix: has arg");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/CLI/Subcommand.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/CLI/Subcommand.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/CLI/Subcommand.t	(revision 1981)
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Fcm::CLI::Option;
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::CLI::Subcommand';
+    use_ok($class);
+    test_constructor($class);
+    test_has_a_name($class);
+    test_as_string($class);
+}
+
+################################################################################
+# Tests the constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = 'constructor';
+    my %OPTION_OF = (
+        description    => 'description value',
+        invoker_class  => 'invoker_class value',
+        invoker_config => 'invoker_config value',
+        is_vc          => 'is_vc value',
+        names          => 'names value',
+        options        => 'options value',
+        synopsis       => 'synopsis value',
+        usage          => 'usage value',
+    );
+    my $subcommand = Fcm::CLI::Subcommand->new(\%OPTION_OF);
+    isa_ok($subcommand, $class, $prefix);
+    for my $key (keys(%OPTION_OF)) {
+        my $getter = index($key, 'is') == 0 ? $key : "get_$key";
+        is($subcommand->$getter(), $OPTION_OF{$key}, "$prefix: $getter");
+    }
+}
+
+################################################################################
+# Tests match a string name to a subcommand
+sub test_has_a_name {
+    my ($class) = @_;
+    my $prefix = 'has a name';
+    my @NAMES = ('foo', 'bar', 'baz');
+    my $subcommand = $class->new({names => \@NAMES});
+    for my $name (@NAMES) {
+        ok($subcommand->has_a_name($name), "$prefix: $name");
+    }
+    for my $name (qw{egg ham mayo}) {
+        ok(!$subcommand->has_a_name($name), "$prefix: $name");
+    }
+}
+
+################################################################################
+# Tests string representation of a subcommand
+sub test_as_string {
+    my ($class) = @_;
+    my $prefix = 'as string';
+    my %OPTION_OF = (
+        'foo (bar, baz)' => ['foo', 'bar', 'baz'],
+        'foo (bar)'      => ['foo', 'bar'],
+        'foo'            => ['foo'],
+        q{}              => [],
+    );
+    for my $key (keys(%OPTION_OF)) {
+        my $subcommand = $class->new({names => $OPTION_OF{$key}});
+        is($subcommand->as_string(), $key, "$prefix: $key");
+    }
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/ConfigSystem.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/ConfigSystem.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/ConfigSystem.t	(revision 1981)
@@ -0,0 +1,170 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# (C) Crown copyright Met Office. All rights reserved.
+# For further details please refer to the file COPYRIGHT.txt
+# which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use Fcm::CfgLine;
+use Fcm::Config;
+use Scalar::Util qw{reftype};
+use Test::More (tests => 90);
+
+BEGIN: {
+    use_ok('Fcm::ConfigSystem');
+}
+
+my $CONFIG = undef;
+
+# ------------------------------------------------------------------------------
+if (!caller()) {
+    main(@ARGV);
+}
+
+# ------------------------------------------------------------------------------
+sub main {
+    local @ARGV = @_;
+    test_compare_setting_in_config();
+}
+
+# ------------------------------------------------------------------------------
+# Tests "compare_setting_in_config".
+sub test_compare_setting_in_config {
+    my $PREFIX = 'TEST';
+    my %S = (egg => [qw{boiled poached}], ham => 'roasted', bacon => 'fried');
+    my %S_MOD = (ham => 'boiled');
+    my %S_MOD_ARRAY = (egg => [qw{scrambled omelette}]);
+    my %S_ADD = (mushroom => 'sauteed');
+    my %S_DEL = (bacon => undef);
+
+    my @ITEMS = (
+        {
+            name     => 'empty',
+            original => {},
+            added    => {},
+            removed  => {},
+            modified => {},
+        },
+        {
+            name     => 'add keys to empty',
+            original => {},
+            added    => {%S},
+            removed  => {},
+            modified => {%S},
+        },
+        {
+            name     => 'remove all',
+            original => {%S},
+            added    => {},
+            removed  => {},
+            modified => {map {($_, undef)} keys(%S)},
+        },
+        {
+            name     => 'no change',
+            original => {%S},
+            added    => {%S},
+            removed  => {},
+            modified => {},
+        },
+        {
+            name     => 'modify key',
+            original => {%S},
+            added    => {%S, %S_MOD},
+            removed  => {},
+            modified => {%S_MOD},
+        },
+        {
+            name     => 'modify an array key',
+            original => {%S},
+            added    => {%S, %S_MOD_ARRAY},
+            removed  => {},
+            modified => {%S_MOD_ARRAY},
+        },
+        {
+            name     => 'add a key',
+            original => {%S},
+            added    => {%S, %S_ADD},
+            removed  => {},
+            modified => {%S_ADD},
+        },
+        {
+            name     => 'delete a key',
+            original => {%S},
+            added    => {%S},
+            removed  => {%S_DEL},
+            modified => {%S_DEL},
+        },
+        {
+            name     => 'modify a key and delete a key',
+            original => {%S},
+            added    => {%S, %S_MOD},
+            removed  => {%S_DEL},
+            modified => {%S_MOD, %S_DEL},
+        },
+        {
+            name     => 'add a key and delete a key',
+            original => {%S},
+            added    => {%S, %S_ADD},
+            removed  => {%S_DEL},
+            modified => {%S_ADD, %S_DEL},
+        },
+    );
+
+    # A naive function to serialise an array reference
+    my $flatten = sub {
+        if (ref($_[0]) && reftype($_[0]) eq 'ARRAY') {
+            join(q{ }, sort(@{$_[0]}))
+        }
+        else {
+            $_[0];
+        }
+    };
+
+    my $CONFIG = Fcm::Config->instance();
+    for my $item (@ITEMS) {
+        # New settings
+        $CONFIG->{setting}{$PREFIX} = {%{$item->{added}}};
+        for my $key (keys(%{$item->{removed}})) {
+            delete($CONFIG->{setting}{$PREFIX}{$key});
+        }
+
+        # Old lines
+        my @old_lines = map {
+            Fcm::CfgLine->new(
+                LABEL => $PREFIX . $Fcm::Config::DELIMITER . $_,
+                VALUE => $flatten->($item->{original}{$_}),
+            )
+        } keys(%{$item->{original}});
+
+        # Invokes the method
+        my $system = Fcm::ConfigSystem->new();
+        my ($changed_hash_ref, $new_cfg_lines_ref)
+            = $system->compare_setting_in_config($PREFIX, \@old_lines);
+
+        # Tests the return values
+        my $T = $item->{name};
+        is_deeply(
+            $changed_hash_ref, $item->{modified},
+            "$T: \$changed_hash_ref content",
+        );
+        is(
+            scalar(@{$new_cfg_lines_ref}),
+            scalar(keys(%{$item->{added}})) - scalar(keys(%{$item->{removed}})),
+            "$T: \$new_cfg_lines_ref length",
+        );
+        for my $line (@{$new_cfg_lines_ref}) {
+            my $key = $line->label_from_field(1);
+            ok(exists($item->{added}{$key}), "$T: expected label $key");
+            ok(!exists($item->{removed}{$key}), "$T: unexpected label $key");
+            is(
+                $line->value(), $flatten->($item->{added}{$key}),
+                "$T: line content $key",
+            );
+        }
+    }
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Exception.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Exception.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Exception.t	(revision 1981)
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Exception';
+    use_ok($class);
+    test_constructor_empty($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests empty constructor
+sub test_constructor_empty {
+    my ($class) = @_;
+    my $prefix = 'empty constructor';
+    my $e = $class->new();
+    isa_ok($e, $class, $prefix);
+    isnt("$e", undef, "$prefix: as_string() not undef");
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = @_;
+    my $prefix = 'normal';
+    my $e = $class->new({message => 'message'});
+    isa_ok($e, $class, $prefix);
+    is("$e", "$class: message\n", "$prefix: as_string()");
+    is($e->get_message(), 'message', "$prefix: get_message()");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/ExtractConfigComparator.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/ExtractConfigComparator.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/ExtractConfigComparator.t	(revision 1981)
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::ExtractConfigComparator';
+    use_ok($class);
+}
+
+# TODO: more real tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Interactive.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Interactive.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Interactive.t	(revision 1981)
@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+################################################################################
+# A sub-class of Fcm::Interactive::InputGetter for testing
+{
+    package TestInputGetter;
+    use base qw{Fcm::Interactive::InputGetter};
+
+    ############################################################################
+    # A callback for testing
+    sub get_callback {
+        my ($self) = @_;
+        return $self->{callback};
+    }
+
+    ############################################################################
+    # Returns some pre-defined strings
+    sub invoke {
+        my ($self) = @_;
+        $self->get_callback()->(
+            $self->get_title(),
+            $self->get_message(),
+            $self->get_type(),
+            $self->get_default(),
+        );
+        return 'answer';
+    }
+}
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    use_ok('Fcm::Interactive');
+    test_default_impl();
+    test_set_impl();
+    test_get_input();
+}
+
+################################################################################
+# Tests default setting of input getter implementation
+sub test_default_impl {
+    my $prefix = 'default impl';
+    my ($class_name, $class_options_ref) = Fcm::Interactive::get_default_impl();
+    is($class_name, 'Fcm::Interactive::InputGetter::CLI', "$prefix: class name");
+    is_deeply($class_options_ref, {}, "$prefix: class options");
+}
+
+################################################################################
+# Tests setting the input getter implementation
+sub test_set_impl {
+    my $prefix = 'set impl';
+    my %options = (extra => 'extra-value');
+    my $name = 'TestInputGetter';
+    Fcm::Interactive::set_impl($name, \%options);
+    my ($class_name, $class_options_ref) = Fcm::Interactive::get_impl();
+    is($class_name, $name, "$prefix: class name");
+    is_deeply($class_options_ref, \%options, "$prefix: class options");
+}
+
+################################################################################
+# Tests getting input with test input getter
+sub test_get_input {
+    my $prefix = 'get input';
+    my %EXPECTED = (
+        TITLE   => 'title-value',
+        MESSAGE => 'message-value',
+        TYPE    => 'type-value',
+        DEFAULT => 'default-value',
+        ANSWER  => 'answer',
+    );
+    Fcm::Interactive::set_impl('TestInputGetter', {
+        callback => sub {
+            my ($title, $message, $type, $default) = @_;
+            is($title, $EXPECTED{TITLE}, "$prefix: title");
+            is($message, $EXPECTED{MESSAGE}, "$prefix: message");
+            is($type, $EXPECTED{TYPE}, "$prefix: type");
+            is($default, $EXPECTED{DEFAULT}, "$prefix: default");
+        },
+    });
+    my $ans = Fcm::Interactive::get_input(
+        title   => $EXPECTED{TITLE},
+        message => $EXPECTED{MESSAGE},
+        type    => $EXPECTED{TYPE},
+        default => $EXPECTED{DEFAULT},
+    );
+    is($ans, $EXPECTED{ANSWER}, "$prefix: answer");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Interactive/InputGetter.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Interactive/InputGetter.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Interactive/InputGetter.t	(revision 1981)
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Interactive::InputGetter';
+    use_ok($class);
+    test_constructor($class);
+}
+
+################################################################################
+# Tests usage of constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = 'constructor';
+    my $input_getter = $class->new({
+        title   => 'title-value',
+        message => 'message-value',
+        type    => 'type-value',
+        default => 'default-value',
+    });
+    isa_ok($input_getter, $class);
+    is($input_getter->get_title(), 'title-value', "$prefix: get title");
+    is($input_getter->get_message(), 'message-value', "$prefix: get message");
+    is($input_getter->get_type(), 'type-value', "$prefix: get type");
+    is($input_getter->get_default(), 'default-value', "$prefix: get default");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Interactive/InputGetter/CLI.pm
===================================================================
--- vendors/fcm/1-5/t/Fcm/Interactive/InputGetter/CLI.pm	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Interactive/InputGetter/CLI.pm	(revision 1981)
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Interactive::InputGetter::CLI';
+    use_ok($class);
+    test_constructor($class);
+}
+
+################################################################################
+# Tests usage of constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = 'constructor';
+    my $input_getter = $class->new({});
+    isa_ok($input_getter, $class);
+}
+
+# TODO: tests the invoke method
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Interactive/InputGetter/GUI.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Interactive/InputGetter/GUI.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Interactive/InputGetter/GUI.t	(revision 1981)
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Interactive::InputGetter::GUI';
+    use_ok($class);
+    test_constructor($class);
+}
+
+################################################################################
+# Tests usage of constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = 'constructor';
+    my $input_getter = $class->new({
+        title   => 'title-value',
+        message => 'message-value',
+        type    => 'type-value',
+        default => 'default-value',
+        geometry => 'geometry-value',
+    });
+    isa_ok($input_getter, $class);
+    is($input_getter->get_geometry(), 'geometry-value', "$prefix: geometry");
+}
+
+# TODO: tests the invoke method
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword.t	(revision 1981)
@@ -0,0 +1,323 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Carp qw{croak};
+use Fcm::Keyword::Config;
+use Test::More (tests => 227);
+
+BEGIN: {
+    use_ok('Fcm::Keyword');
+}
+
+if (!caller()) {
+    main(@ARGV);
+}
+
+sub main {
+    local @ARGV = @_;
+    local %Fcm::Keyword::Config::CONFIG_OF = (
+        LOCATION_ENTRIES => {entry_class => 'Fcm::Keyword::Entry::Location'},
+        REVISION_ENTRIES => {entry_class => 'Fcm::Keyword::Entry'},
+    );
+    test_get_prefix_of_location_keyword();
+    test_get_entries();
+    test_expand();
+    test_unexpand();
+    test_get_browser_url();
+}
+
+################################################################################
+# Tests get_prefix_of_location_keyword().
+sub test_get_prefix_of_location_keyword {
+    is(Fcm::Keyword::get_prefix_of_location_keyword(), 'fcm');
+    is(Fcm::Keyword::get_prefix_of_location_keyword(1), 'fcm:');
+}
+
+################################################################################
+# Tests get_entries().
+sub test_get_entries {
+    my $entries = Fcm::Keyword::get_entries();
+    isa_ok($entries, 'Fcm::Keyword::Entries');
+    for (1 .. 10) {
+        is(Fcm::Keyword::get_entries(), $entries, "get_entries: is singleton");
+    }
+    isnt(Fcm::Keyword::get_entries(1), $entries, "get_entries: can reset");
+}
+
+################################################################################
+# Tests expand().
+sub test_expand {
+    my $T = 'expand';
+
+    # Add some keywords for testing
+    _add_keyword_entries([
+    #   ['name'  , 'value'                   , {'rev1' => rev, ...}],
+        ['FOO'   , 'test://foo/foo'      , {'V1.0' => 256, 'V1-1' => 4790}],
+        ['FOO-TR', 'test://foo/foo/trunk', {}],
+    ]);
+
+    _do_keyword_tests($T, \&Fcm::Keyword::expand, [
+    # Tests to ensure that valid targets are expanded
+    #   [['input'               ], ['expected'                     ]],
+        [['fcm:FOO'             ], ['test://foo/foo'               ]],
+        [['fcm:FOO'     , 'V1.0'], ['test://foo/foo'       , '256' ]],
+        [['fcm:Foo'             ], ['test://foo/foo'               ]],
+        [['fcm:foo'             ], ['test://foo/foo'               ]],
+        [['fcm:foo'     , 'v1.0'], ['test://foo/foo'       , '256' ]],
+        [['fcm:foo'     , 'head'], ['test://foo/foo'       , 'head']],
+        [['fcm:foo/'            ], ['test://foo/foo/'              ]],
+        [['fcm:foo/'    , '1234'], ['test://foo/foo/'      , '1234']],
+        [['fcm:foo/'    , 'v1.0'], ['test://foo/foo/'      , '256' ]],
+        [['fcm:foo/'    , 'v1-1'], ['test://foo/foo/'      , '4790']],
+        [['fcm:foo/bar'         ], ['test://foo/foo/bar'           ]],
+        [['fcm:foo/bar' , 'PREV'], ['test://foo/foo/bar'   , 'PREV']],
+        [['fcm:foo/bar' , 'base'], ['test://foo/foo/bar'   , 'base']],
+        [['fcm:foo/bar' , 'v1-1'], ['test://foo/foo/bar'   , '4790']],
+        [['fcm:foo/bar/', '7777'], ['test://foo/foo/bar/'  , '7777']],
+        [['fcm:foo/bar/', '{11}'], ['test://foo/foo/bar/'  , '{11}']],
+        [['fcm:foo/bar/', 'v1.0'], ['test://foo/foo/bar/'  , '256' ]],
+        [['fcm:foo-tr'          ], ['test://foo/foo/trunk'         ]],
+        [['fcm:foo-tr'  , 'head'], ['test://foo/foo/trunk' , 'head']],
+        [['fcm:foo-tr'  , 'v1.0'], ['test://foo/foo/trunk' , '256' ]],
+        [['fcm:foo-tr/'         ], ['test://foo/foo/trunk/'        ]],
+        [['fcm:foo-tr/' , '1234'], ['test://foo/foo/trunk/', '1234']],
+        [['fcm:foo-tr/' , 'v1-1'], ['test://foo/foo/trunk/', '4790']],
+    # Tests to ensure that non-keyword targets are not expanded
+    #   [['input'                     ]], # 'expected' same as 'input'
+        [['no-change'                 ]],
+        [['foo/bar'                   ]],
+        [['/foo/bar'                  ]],
+        [['/foo/bar'     , 'head'     ]],
+        [['/foo/bar/'                 ]],
+        [['/foo/bar/'    , 'not-a-key']],
+        [['svn://foo/bar'             ]],
+        [['svn://foo/bar', '1234'     ]],
+        [['file://foo/bar'            ]],
+        [['http://foo/bar'            ]],
+    ]);
+
+    # Tests for unexpected keywords
+    for my $key (qw{foo bar baz}) {
+        eval {
+            Fcm::Keyword::expand("fcm:foo\@$key");
+        };
+        isa_ok($@, 'Fcm::Keyword::Exception', "$T: $key: invalid revision");
+    }
+
+    # Tests for "undef", all expecting exceptions
+    for my $target_ref ([undef], [undef, undef], [undef, 'foo']) {
+        eval {
+            Fcm::Keyword::expand(@{$target_ref});
+        };
+        isa_ok($@, 'Fcm::Exception', "$T: undef");
+    }
+}
+
+################################################################################
+# Tests unexpand().
+sub test_unexpand {
+    my $T = 'unexpand';
+
+    # Add some keywords for testing
+    _add_keyword_entries([
+    #   ['name'  , 'value'               , {'rev1' => rev, ...}],
+        ['FOO'   , 'test://foo/foo'      , {'V1.0' => 256, 'V1-1' => 4790}],
+        ['FOO_TR', 'test://foo/foo/trunk', {}],
+        ['FOO-TR', 'test://foo/foo/trunk', {}],
+    ]);
+
+    _do_keyword_tests($T, \&Fcm::Keyword::unexpand, [
+    # Tests to ensure that valid targets are expanded
+    #   [['input'                        ], ['expected'            ]],
+        [['test://foo/foo'               ], ['fcm:FOO'             ]],
+        [['test://foo/foo'       , '256' ], ['fcm:FOO'     , 'V1.0']],
+        [['test://foo/foo'       , 'head'], ['fcm:FOO'     , 'head']],
+        [['test://foo/foo/'              ], ['fcm:FOO/'            ]],
+        [['test://foo/foo/'      , '1234'], ['fcm:FOO/'    , '1234']],
+        [['test://foo/foo/'      , '256' ], ['fcm:FOO/'    , 'V1.0']],
+        [['test://foo/foo/'      , '4790'], ['fcm:FOO/'    , 'V1-1']],
+        [['test://foo/foo/bar'           ], ['fcm:FOO/bar'         ]],
+        [['test://foo/foo/bar'   , 'PREV'], ['fcm:FOO/bar' , 'PREV']],
+        [['test://foo/foo/bar'   , 'base'], ['fcm:FOO/bar' , 'base']],
+        [['test://foo/foo/bar'   , '4790'], ['fcm:FOO/bar' , 'V1-1']],
+        [['test://foo/foo/bar/'  , '7777'], ['fcm:FOO/bar/', '7777']],
+        [['test://foo/foo/bar/'  , '{11}'], ['fcm:FOO/bar/', '{11}']],
+        [['test://foo/foo/bar/'  , '256' ], ['fcm:FOO/bar/', 'V1.0']],
+        [['test://foo/foo/trunk'         ], ['fcm:FOO-TR'          ]],
+        [['test://foo/foo/trunk' , 'head'], ['fcm:FOO-TR'  , 'head']],
+        [['test://foo/foo/trunk' , '256' ], ['fcm:FOO-TR'  , 'V1.0']],
+        [['test://foo/foo/trunk/'        ], ['fcm:FOO-TR/'         ]],
+        [['test://foo/foo/trunk/', '1234'], ['fcm:FOO-TR/' , '1234']],
+        [['test://foo/foo/trunk/', '4790'], ['fcm:FOO-TR/' , 'V1-1']],
+    # Tests to ensure that non-keyword targets are not expanded
+    #   [['input'                     ]], # 'expected' same as 'input'
+        [['no-change'                 ]],
+        [['foo/bar'                   ]],
+        [['/foo/bar'                  ]],
+        [['/foo/bar'     , 'head'     ]],
+        [['/foo/bar/'                 ]],
+        [['/foo/bar/'    , 'not-a-key']],
+        [['svn://foo/bar'             ]],
+        [['svn://foo/bar', '1234'     ]],
+        [['file://foo/bar'            ]],
+        [['http://foo/bar'            ]],
+    ]);
+
+    # Tests for "undef", all expecting exceptions
+    for my $target_ref ([undef], [undef, undef], [undef, 'foo']) {
+        eval {
+            Fcm::Keyword::unexpand(@{$target_ref});
+        };
+        isa_ok($@, 'Fcm::Exception', "$T: undef");
+    }
+}
+
+################################################################################
+# Tests get_browser_url().
+sub test_get_browser_url {
+    my $T = 'get_browser_url';
+
+    # Add some keywords for testing
+    _add_keyword_entries([
+    #   ['name'  , 'value'                        , {'rev1' => rev, ...}],
+        ['FOO'   , 'test://foo/foo_svn/foo'       , {'V1' => 256, 'W2' => 479}],
+        ['FOO-TR', 'test://foo/foo_svn/foo/trunk'],
+        ['FOO_TR', 'test://foo/foo_svn/foo/trunk'],
+    ]);
+
+    my ($INPUT, $EXPECTED) = (0, 1);
+    my ($LOC, $REV) = (0, 1);
+    for my $test_ref (
+    # Tests to ensure that valid targets are expanded
+    #   [['input'                                ], 'expected'                                                ],
+        [['test://foo/foo_svn/foo'               ], 'http://foo/projects/foo/intertrac/source:foo'            ],
+        [['test://foo/foo_svn/foo'       , '256' ], 'http://foo/projects/foo/intertrac/source:foo@256'        ],
+        [['test://foo/foo_svn/foo'       , 'head'], 'http://foo/projects/foo/intertrac/source:foo@head'       ],
+        [['test://foo/foo_svn/foo/'              ], 'http://foo/projects/foo/intertrac/source:foo/'           ],
+        [['test://foo/foo_svn/foo/'      , '1234'], 'http://foo/projects/foo/intertrac/source:foo/@1234'      ],
+        [['test://foo/foo_svn/foo/'      , '256' ], 'http://foo/projects/foo/intertrac/source:foo/@256'       ],
+        [['test://foo/foo_svn/foo/'      , '479' ], 'http://foo/projects/foo/intertrac/source:foo/@479'       ],
+        [['test://foo/foo_svn/foo/bar'           ], 'http://foo/projects/foo/intertrac/source:foo/bar'        ],
+        [['test://foo/foo_svn/foo/bar'   , '479' ], 'http://foo/projects/foo/intertrac/source:foo/bar@479'    ],
+        [['test://foo/foo_svn/foo/bar/'  , '7777'], 'http://foo/projects/foo/intertrac/source:foo/bar/@7777'  ],
+        [['test://foo/foo_svn/foo/bar/'  , '{11}'], 'http://foo/projects/foo/intertrac/source:foo/bar/@{11}'  ],
+        [['test://foo/foo_svn/foo/bar/'  , '256' ], 'http://foo/projects/foo/intertrac/source:foo/bar/@256'   ],
+        [['test://foo/foo_svn/foo/trunk'         ], 'http://foo/projects/foo/intertrac/source:foo/trunk'      ],
+        [['test://foo/foo_svn/foo/trunk' , 'head'], 'http://foo/projects/foo/intertrac/source:foo/trunk@head' ],
+        [['test://foo/foo_svn/foo/trunk' , '256' ], 'http://foo/projects/foo/intertrac/source:foo/trunk@256'  ],
+        [['test://foo/foo_svn/foo/trunk/'        ], 'http://foo/projects/foo/intertrac/source:foo/trunk/'     ],
+        [['test://foo/foo_svn/foo/trunk/', '1234'], 'http://foo/projects/foo/intertrac/source:foo/trunk/@1234'],
+        [['test://foo/foo_svn/foo/trunk/', '479' ], 'http://foo/projects/foo/intertrac/source:foo/trunk/@479' ],
+        [['fcm:FOO'                              ], 'http://foo/projects/foo/intertrac/source:foo'            ],
+        [['fcm:FOO'                      , 'V1'  ], 'http://foo/projects/foo/intertrac/source:foo@256'        ],
+        [['fcm:FOO'                      , 'head'], 'http://foo/projects/foo/intertrac/source:foo@head'       ],
+        [['fcm:FOO/'                             ], 'http://foo/projects/foo/intertrac/source:foo/'           ],
+        [['fcm:FOO/'                     , '1234'], 'http://foo/projects/foo/intertrac/source:foo/@1234'      ],
+        [['fcm:FOO/'                     , 'V1'  ], 'http://foo/projects/foo/intertrac/source:foo/@256'       ],
+        [['fcm:FOO/'                     , 'W2'  ], 'http://foo/projects/foo/intertrac/source:foo/@479'       ],
+        [['fcm:FOO/bar'                          ], 'http://foo/projects/foo/intertrac/source:foo/bar'        ],
+        [['fcm:FOO/bar'                  , 'W2'  ], 'http://foo/projects/foo/intertrac/source:foo/bar@479'    ],
+        [['fcm:FOO/bar/'                 , '7777'], 'http://foo/projects/foo/intertrac/source:foo/bar/@7777'  ],
+        [['fcm:FOO/bar/'                 , '{11}'], 'http://foo/projects/foo/intertrac/source:foo/bar/@{11}'  ],
+        [['fcm:FOO/bar/'                 , 'v1'  ], 'http://foo/projects/foo/intertrac/source:foo/bar/@256'   ],
+        [['fcm:FOO-TR'                           ], 'http://foo/projects/foo/intertrac/source:foo/trunk'      ],
+        [['fcm:FOO-TR'                   , 'head'], 'http://foo/projects/foo/intertrac/source:foo/trunk@head' ],
+        [['fcm:FOO-TR'                   , 'V1'  ], 'http://foo/projects/foo/intertrac/source:foo/trunk@256'  ],
+        [['fcm:FOO-TR/'                          ], 'http://foo/projects/foo/intertrac/source:foo/trunk/'     ],
+        [['fcm:FOO-TR/'                  , '1234'], 'http://foo/projects/foo/intertrac/source:foo/trunk/@1234'],
+        [['fcm:FOO-TR/'                  , 'w2'  ], 'http://foo/projects/foo/intertrac/source:foo/trunk/@479' ],
+    ) {
+        my $input = $test_ref->[$INPUT][$LOC];
+        if (exists($test_ref->[$INPUT][$REV])) {
+            $input .= '@' . $test_ref->[$INPUT][$REV];
+        }
+        for (
+            {name => "$T: scalar input: $input", input => [$input]},
+            {name => "$T: list input: $input"  , input => $test_ref->[$INPUT]},
+        ) {
+            my $output;
+            eval {
+                $output = Fcm::Keyword::get_browser_url(@{$_->{input}});
+                is($output, $test_ref->[$EXPECTED], $_->{name});
+            };
+            if ($@) {
+                fail("$_->{name}: $@");
+            }
+        }
+    }
+
+    # Tests correct behaviour for "undef"
+    for my $bad_url (undef, '') {
+        eval {
+            Fcm::Keyword::get_browser_url($bad_url);
+        };
+        isa_ok($@, 'Fcm::Exception', sprintf(
+            "$T: %s", (defined($bad_url) ? $bad_url : 'undef'),
+        ));
+    }
+
+    # Tests correct behaviour for invalid inputs
+    for my $bad_url ('foo', 'svn://no/such/url', 'fcm:no_such_project/trunk') {
+        eval {
+            Fcm::Keyword::get_browser_url($bad_url);
+        };
+        isa_ok($@, 'Fcm::Keyword::Exception', "$T: $bad_url: invalid keyword");
+    }
+}
+
+################################################################################
+# Adds keyword entries.
+sub _add_keyword_entries {
+    my ($items_ref) = @_;
+    my ($NAME, $LOC, $REV) = (0 .. 2);
+    my $entries = Fcm::Keyword::get_entries(1); # reset
+    for my $item_ref (@{$items_ref}) {
+        my $entry = $entries->add_entry($item_ref->[$NAME], $item_ref->[$LOC]);
+        while (my ($key, $value) = each(%{$item_ref->[$REV]})) {
+            $entry->get_revision_entries()->add_entry($key, $value);
+        }
+    }
+}
+
+################################################################################
+# Performs keyword testings.
+sub _do_keyword_tests {
+    my ($T, $action_ref, $tests_ref) = @_;
+    my ($INPUT, $EXPECTED) = (0, 1);
+    my ($LOC, $REV) = (0, 1);
+    for my $test_ref (@{$tests_ref}) {
+        if (!defined($test_ref->[$EXPECTED])) {
+            $test_ref->[$EXPECTED] = $test_ref->[$INPUT];
+        }
+        my %value_of;
+        for my $i (0 .. $#{$test_ref}) {
+            $value_of{$i} = $test_ref->[$i][$LOC];
+            if (exists($test_ref->[$i][$REV])) {
+                $value_of{$i} .= '@' . $test_ref->[$i][$REV];
+            }
+        }
+        eval {
+            is(
+                $action_ref->($value_of{$INPUT}), $value_of{$EXPECTED},
+                "$T: scalar context: $value_of{$INPUT}",
+            );
+        };
+        if ($@) {
+            fail("$T: scalar context: $value_of{$INPUT}: $@");
+        }
+        eval {
+            is_deeply(
+                [$action_ref->(@{$test_ref->[$INPUT]})],
+                $test_ref->[$EXPECTED],
+                "$T: list context: $value_of{$INPUT}",
+            );
+        };
+        if ($@) {
+            fail("$T: list context: $value_of{$INPUT}: $@");
+        }
+    }
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Config.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Config.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Config.t	(revision 1981)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $module = 'Fcm::Keyword::Config';
+    use_ok($module);
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Entries.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Entries.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Entries.t	(revision 1981)
@@ -0,0 +1,228 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+################################################################################
+# A Fcm::Keyword::Entry sub-class for testing
+{
+    package TestEntry;
+    use base qw{Fcm::Keyword::Entry};
+}
+
+################################################################################
+# A mock loader implementing the Fcm::Keyword::Loader interface
+{
+    package MockLoader0;
+    use Scalar::Util qw{blessed};
+
+    ############################################################################
+    # Constructor
+    sub new {
+        my ($class) = @_;
+        return bless({number_of_calls_to_load_to => 0}, $class);
+    }
+
+    ############################################################################
+    ##Returns the package name
+    sub get_source {
+        my ($self) = @_;
+        return blessed($self);
+    }
+
+    ############################################################################
+    # Returns number of times $self->load_to() has been called
+    sub get_number_of_calls_to_load_to {
+        my ($self) = @_;
+        return $self->{number_of_calls_to_load_to};
+    }
+
+    ############################################################################
+    # Loads data into $entries, and returns number of entries loaded
+    sub load_to {
+        my ($self, $entries) = @_;
+        $self->{number_of_calls_to_load_to}++;
+        return $self->load_to_impl($entries);
+    }
+
+    ############################################################################
+    # Returns 0
+    sub load_to_impl {
+        my ($self, $entries) = @_;
+        return 0;
+    }
+}
+
+################################################################################
+# A mock loader implementing the Fcm::Keyword::Loader interface
+{
+    package MockLoader1;
+    our @ISA = qw{MockLoader0};
+
+    my %VALUE_OF = (foo => 'foo1', bar => 'bar2', baz => 'baz3');
+
+    ############################################################################
+    # Returns a reference to the mock data
+    sub get_data {
+        my ($class) = @_;
+        return \%VALUE_OF;
+    }
+
+    ############################################################################
+    ##Writes mock data to the $entries object
+    sub load_to_impl {
+        my ($self, $entries) = @_;
+        my $counter = 0;
+        for my $key (keys(%{$self->get_data()})) {
+            $entries->add_entry($key, $self->get_data()->{$key});
+            $counter++;
+        }
+        return $counter;
+    }
+}
+
+################################################################################
+# A mock loader implementing the Fcm::Keyword::Loader interface
+{
+    package MockLoader2;
+    our @ISA = qw{MockLoader1};
+
+    my %VALUE_OF = (sausages => 'pig', eggs => 'hen', chips => 'potato');
+
+    ############################################################################
+    # Returns a reference to the mock data
+    sub get_data {
+        my ($class) = @_;
+        return \%VALUE_OF;
+    }
+}
+
+package main;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Entries';
+    use_ok($class);
+    test_empty_constructor($class);
+    test_constructor($class);
+    test_add_entry($class);
+    test_loaders($class);
+}
+
+################################################################################
+# Tests empty constructor
+sub test_empty_constructor {
+    my ($class) = @_;
+    my $prefix = 'empty constructor';
+    my $entries = $class->new();
+    isa_ok($entries, $class);
+    is($entries->get_entry_class(), 'Fcm::Keyword::Entry',
+        "$prefix: default entry class");
+    is_deeply([$entries->get_loaders()], [], "$prefix: empty list of loaders");
+    is_deeply([$entries->get_all_entries()], [],
+        "$prefix: empty list of entries");
+    for my $arg ('foo', undef) {
+        is($entries->get_entry_by_key($arg), undef,
+            "$prefix: entry by key: undef");
+        is($entries->get_entry_by_value($arg), undef,
+            "$prefix: entry by value: undef");
+    }
+}
+
+################################################################################
+# Tests other constructor usages
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = 'constructor';
+    my @loaders = (MockLoader1->new(), MockLoader2->new());
+    my $entries = $class->new({
+        entry_class => 'not-a-class',
+        loaders     => \@loaders,
+    });
+    isa_ok($entries, $class);
+    is($entries->get_entry_class(), 'not-a-class', "$prefix: entry class");
+    is_deeply([$entries->get_loaders()], \@loaders, "$prefix: list of loaders");
+    eval {
+        $entries->add_entry('key', 'value');
+    };
+    isnt($@, undef, "$prefix: invalid entry class");
+}
+
+################################################################################
+# Tests adding entries
+sub test_add_entry {
+    my ($class) = @_;
+    my $prefix = 'add entry';
+    my %VALUE_OF = (key1 => 'value1', egg => 'white and yolk', 'xyz.abc' => '');
+    for my $entry_class ('Fcm::Keyword::Entry', 'TestEntry') {
+        my $entries = $class->new({entry_class => $entry_class});
+        my $number_of_entries = 0;
+        for my $key (keys(%VALUE_OF)) {
+            my $entry = $entries->add_entry($key, $VALUE_OF{$key});
+            isa_ok($entry, $entry_class);
+            is(scalar(@{$entries->get_all_entries()}), ++$number_of_entries,
+                "$prefix: number of entries: $number_of_entries");
+        }
+        for my $key (keys(%VALUE_OF)) {
+            my $entry = $entries->get_entry_by_key($key);
+            isa_ok($entry, $entry_class);
+            is($entry->get_key(), uc($key), "$prefix: get by key: $key");
+            is($entry->get_value(), $VALUE_OF{$key},
+                "$prefix: get by key: $key: value");
+        }
+        for my $key (keys(%VALUE_OF)) {
+            my $entry = $entries->get_entry_by_value($VALUE_OF{$key});
+            isa_ok($entry, $entry_class);
+            is($entry->get_key(), uc($key), "$prefix: get by value: $key");
+            is($entry->get_value(), $VALUE_OF{$key},
+                "$prefix: get by value: $key: value");
+        }
+        is($entries->get_entry_by_key('no-such-key'), undef,
+            "$prefix: get by key: no-such-key");
+        is($entries->get_entry_by_value('no-such-value'), undef,
+            "$prefix: get by value: no-such-value");
+    }
+}
+
+################################################################################
+# Tests usage of loaders
+sub test_loaders {
+    my ($class) = @_;
+    my $prefix = "loader";
+    my @loaders = (MockLoader0->new(), MockLoader1->new(), MockLoader2->new());
+    my $entries = $class->new({loaders => \@loaders});
+    for my $loader (@loaders) {
+        is($loader->get_number_of_calls_to_load_to(), 0, "$prefix: not loaded");
+    }
+    for my $key (keys(%{$loaders[1]->get_data()})) {
+        my $value = $loaders[1]->get_data()->{$key};
+        my $entry = $entries->get_entry_by_key($key);
+        is($entry->get_key(), uc($key), "$prefix: by key: $key: key");
+        is($entries->get_entry_by_value($value), $entry,
+            "$prefix: by value: $key: object");
+    }
+    is($loaders[0]->get_number_of_calls_to_load_to(), 1,
+        "$prefix: loaded once: 0");
+    is($loaders[1]->get_number_of_calls_to_load_to(), 1,
+        "$prefix: loaded once: 1");
+    is($loaders[2]->get_number_of_calls_to_load_to(), 0,
+        "$prefix: not loaded: 2");
+    for my $key (keys(%{$loaders[2]->get_data()})) {
+        my $value = $loaders[2]->get_data()->{$key};
+        my $entry = $entries->get_entry_by_key($key);
+        is($entry->get_key(), uc($key), "$prefix: by key: $key: key");
+        is($entries->get_entry_by_value($value), $entry,
+            "$prefix: by value: $key: object");
+    }
+    is($loaders[0]->get_number_of_calls_to_load_to(), 2,
+        "$prefix: loaded once: 0");
+    is($loaders[1]->get_number_of_calls_to_load_to(), 1,
+        "$prefix: loaded once: 1");
+    is($loaders[2]->get_number_of_calls_to_load_to(), 1,
+        "$prefix: not loaded: 2");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Entry.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Entry.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Entry.t	(revision 1981)
@@ -0,0 +1,26 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Entry';
+    use_ok($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = 'Fcm::Keyword::Entry';
+    my $entry = $class->new({key => 'key', value => 'value'});
+    isa_ok($entry, $class);
+    is($entry->get_key(), 'key', "normal: get_key()");
+    is($entry->get_value(), 'value', "normal: get_value()");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Entry/Location.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Entry/Location.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Entry/Location.t	(revision 1981)
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my ($class) = 'Fcm::Keyword::Entry::Location';
+    use_ok($class);
+    test_constructor($class);
+}
+
+################################################################################
+# Tests constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = 'constructor';
+    isa_ok($class->new(), $class, "$prefix: empty");
+    my $entry = $class->new({key => 'key', value => 'value'});
+    isa_ok($entry, $class, "$prefix: normal");
+    is($entry->get_key(), 'key', "$prefix: normal: get_key()");
+    is($entry->get_value(), 'value', "$prefix: normal: get_value()");
+    isa_ok($entry->get_revision_entries(), 'Fcm::Keyword::Entries');
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Exception.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Exception.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Exception.t	(revision 1981)
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Exception';
+    use_ok($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = @_;
+    my $prefix = 'normal';
+    my $e = $class->new({message => 'message'});
+    isa_ok($e, $class, $prefix);
+    is("$e", "$class: message\n", "$prefix: as_string()");
+    is($e->get_message(), 'message', "$prefix: get_message()");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entries.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entries.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entries.t	(revision 1981)
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Fcm::Keyword::Entries;
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Formatter::Entries';
+    use_ok($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = @_;
+    my $prefix = 'normal';
+    my $formatter = $class->new();
+    isa_ok($formatter, $class, $prefix);
+    my $entries = Fcm::Keyword::Entries->new();
+    $entries->add_entry('foo', 'food');
+    $entries->add_entry('bar', 'barley');
+    is($formatter->format($entries), "BAR = barley\nFOO = food\n",
+        "$prefix: format");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entry.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entry.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entry.t	(revision 1981)
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Fcm::Keyword::Entry;
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Formatter::Entry';
+    use_ok($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = @_;
+    my $prefix = 'normal';
+    my $formatter = $class->new();
+    isa_ok($formatter, $class, $prefix);
+    my $entry = Fcm::Keyword::Entry->new({key => 'k', value => 'v'});
+    is($formatter->format($entry), "k = v\n", "$prefix: format");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entry/Location.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entry/Location.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Formatter/Entry/Location.t	(revision 1981)
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Fcm::Keyword::Entry::Location;
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Formatter::Entry::Location';
+    use_ok($class);
+    test_normal($class);
+}
+
+################################################################################
+# Tests normal usage
+sub test_normal {
+    my ($class) = @_;
+    my $prefix = 'normal';
+    my $formatter = $class->new();
+    isa_ok($formatter, $class, $prefix);
+    my $entry = Fcm::Keyword::Entry::Location->new({key => 'k', value => 'v'});
+    like($formatter->format($entry), qr{k \s = \s v}xms, "$prefix: format");
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Loader/Config/Location.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Loader/Config/Location.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Loader/Config/Location.t	(revision 1981)
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Fcm::Config;
+use Fcm::Keyword::Entries;
+use Test::More qw{no_plan};
+
+my %VALUE_OF = (
+    foo => 'fcm-test://foo/foo',
+    bar => 'fcm-test://bar/bar',
+    baz => 'fcm-test://baz/baz',
+);
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Loader::Config::Location';
+    use_ok($class);
+    test_constructor($class);
+    test_load_to($class);
+}
+
+################################################################################
+# Tests simple usage of the constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = "constructor";
+    my $loader = $class->new();
+    isa_ok($loader, $class);
+    is($loader->get_source(), 'Fcm::Config', "$prefix: get_source()");
+}
+
+################################################################################
+# Tests loading to an Fcm::Keyword::Entries object
+sub test_load_to {
+    my ($class) = @_;
+    my $prefix = 'load to';
+    my $config = Fcm::Config->instance();
+    for my $key (keys(%VALUE_OF)) {
+        $config->setting(['URL', $key], $VALUE_OF{$key});
+    }
+    my $loader = $class->new();
+    my $entries = Fcm::Keyword::Entries->new({
+        entry_class => 'Fcm::Keyword::Entry::Location',
+    });
+    isnt($loader->load_to($entries), 0, "$prefix: number loaded");
+    for my $key (keys(%VALUE_OF)) {
+        my $entry = $entries->get_entry_by_key($key);
+        if ($entry) {
+            is($entry->get_key(), uc($key), "$prefix: by key: $key");
+            is($entry->get_value(), $VALUE_OF{$key}, "$prefix: by value: $key");
+            is(
+                $entries->get_entry_by_value($VALUE_OF{$key}),
+                $entry,
+                "$prefix: by key: $key: object",
+            );
+        }
+        else {
+            fail("$prefix: by key: $key");
+        }
+    }
+}
+
+# TODO: tests loading of browser mapping
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Loader/Config/Revision.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Loader/Config/Revision.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Loader/Config/Revision.t	(revision 1981)
@@ -0,0 +1,80 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Fcm::Config;
+use Fcm::Keyword::Entries;
+use Test::More qw{no_plan};
+
+my %VALUE_OF = (
+    bar => {
+        'bar3'    => 3,
+        'bar3.1'  => 31,
+        'bar3.14' => 314,
+    },
+    baz => {
+        'bear'   => 4,
+        'bee'    => 6,
+        'spider' => 8,
+    },
+);
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Loader::Config::Revision';
+    use_ok($class);
+    test_constructor($class);
+    test_load_to($class);
+}
+
+################################################################################
+# Tests simple usage of the constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = "constructor";
+    my $loader = $class->new({namespace => 'namespace'});
+    isa_ok($loader, $class);
+    is($loader->get_namespace(), 'namespace', "$prefix: get_namespace()");
+    is($loader->get_source(), 'Fcm::Config', "$prefix: get_source()");
+}
+
+################################################################################
+# Tests loading to an Fcm::Keyword::Entries object
+sub test_load_to {
+    my ($class) = @_;
+    my $prefix = 'load to';
+    my $config = Fcm::Config->instance();
+    for my $key (keys(%VALUE_OF)) {
+        for my $rev_key (keys(%{$VALUE_OF{$key}})) {
+            my $value = $VALUE_OF{$key}{$rev_key};
+            $config->setting(['URL_REVISION', uc($key), uc($rev_key)], $value);
+        }
+        my $entries = Fcm::Keyword::Entries->new();
+        my $loader = $class->new({namespace => $key});
+        isnt($loader->load_to($entries), 0, "$prefix: number loaded");
+        for my $rev_key (keys(%{$VALUE_OF{$key}})) {
+            my $entry = $entries->get_entry_by_key($rev_key);
+            my $value = $VALUE_OF{$key}{$rev_key};
+            if ($entry) {
+                is(
+                    $entry->get_key(),
+                    uc($rev_key),
+                    "$prefix: by key: $rev_key",
+                );
+                is($entry->get_value(), $value, "$prefix: by value: $rev_key");
+                is(
+                    $entries->get_entry_by_value($value),
+                    $entry,
+                    "$prefix: by key: $key: object",
+                );
+            }
+            else {
+                fail("$prefix: by key: $rev_key");
+            }
+        }
+    }
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Keyword/Loader/VC/Revision.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Keyword/Loader/VC/Revision.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Keyword/Loader/VC/Revision.t	(revision 1981)
@@ -0,0 +1,101 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Carp qw{croak};
+use Fcm::Keyword::Entries;
+use File::Basename qw{dirname};
+use File::Spec;
+use File::Temp qw{tempdir};
+use IO::File;
+use IO::Pipe;
+use POSIX qw{WIFEXITED};
+use Test::More (tests => 17);
+
+my %VALUE_OF = (
+    bar => {
+        'bar3'    => 3,
+        'bar3.1'  => 31,
+        'bar3.14' => 314,
+    },
+    baz => {
+        'bear'   => 4,
+        'bee'    => 6,
+        'spider' => 8,
+    },
+);
+
+main();
+
+sub main {
+    my $class = 'Fcm::Keyword::Loader::VC::Revision';
+    use_ok($class);
+    test_constructor($class);
+    test_load_to($class);
+}
+
+################################################################################
+# Tests simple usage of the constructor
+sub test_constructor {
+    my ($class) = @_;
+    my $prefix = "constructor";
+    my $loader = $class->new({source => 'foo'});
+    isa_ok($loader, $class);
+    is($loader->get_source(), 'foo', "$prefix: get_source()");
+    ok($loader->load_to(), "$prefix: load_to"); # FIXME: should fail?
+}
+
+################################################################################
+# Tests loading to an Fcm::Keyword::Entries object
+sub test_load_to {
+    my ($class) = @_;
+    my $prefix = 'load to';
+    my $temp_dir = tempdir(CLEANUP => 1);
+    my $repos = File::Spec->catfile($temp_dir, 'repos');
+    WIFEXITED(system(qw{svnadmin create}, $repos))
+        || croak("$repos: cannot create: $?");
+    my $dump_file = File::Spec->catfile(dirname($0), 'Revision.dump');
+    my $handle = IO::File->new($dump_file, 'r');
+    if (!$handle) {
+        croak("$dump_file: cannot load: $!");
+    }
+    my $dump = do{local $/; $handle->getline()};
+    $handle->close();
+    my $pipe = IO::Pipe->new();
+    $pipe->writer(qw{svnadmin load -q}, $repos);
+    print($pipe $dump);
+    $pipe->close();
+    if ($?) {
+        croak("$dump_file: cannot load: $?");
+    }
+    my $repos_url = "file://$repos";
+    my $loader = $class->new({source => $repos_url});
+    my $entries = Fcm::Keyword::Entries->new();
+    ok($loader->load_to($entries), "$prefix: nothing to load");
+    for my $key (keys(%VALUE_OF)) {
+        my $url = "$repos_url/$key";
+        my $loader = $class->new({source => $url});
+        $loader->load_to($entries);
+        for my $rev_key (keys(%{$VALUE_OF{$key}})) {
+            my $entry = $entries->get_entry_by_key($rev_key);
+            if ($entry) {
+                is(
+                    $entry->get_key(),
+                    uc($rev_key),
+                    "$prefix: by key: $rev_key",
+                );
+                is(
+                    $entries->get_entry_by_value($VALUE_OF{$key}{$rev_key}),
+                    $entry,
+                    "$prefix: by value: $rev_key: object",
+                );
+            }
+            else {
+                fail("$prefix: by key: $rev_key");
+            }
+        }
+    }
+}
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Util.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Util.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Util.t	(revision 1981)
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    use_ok('Fcm::Util');
+    test_tidy_url();
+}
+
+################################################################################
+# Tests tidy_url
+sub test_tidy_url {
+    my $prefix = "tidy_url";
+    my %RESULT_OF = (
+        ''                         => '',
+        'foo'                      => 'foo',
+        'foo/bar'                  => 'foo/bar',
+        'http://foo/bar'           => 'http://foo/bar',
+        'http://foo/bar@1234'      => 'http://foo/bar@1234',
+        'http://foo/bar/@1234'     => 'http://foo/bar@1234',
+        'http://foo/bar/.'         => 'http://foo/bar',
+        'http://foo/bar/.@1234'    => 'http://foo/bar@1234',
+        'http://foo/bar/./@1234'   => 'http://foo/bar@1234',
+        'http://foo/bar/./baz'     => 'http://foo/bar/baz',
+        'http://foo/bar/..'        => 'http://foo',
+        'http://foo/bar/..@1234'   => 'http://foo@1234',
+        'http://foo/bar/../@1234'  => 'http://foo@1234',
+        'http://foo/bar/../baz'    => 'http://foo/baz',
+        'http://foo/bar/../.'      => 'http://foo',
+        'http://foo/bar/baz/../..' => 'http://foo',
+    );
+    for my $key (sort keys(%RESULT_OF)) {
+        is(tidy_url($key), $RESULT_OF{$key}, "$prefix: $key");
+    }
+}
+
+# TODO: more unit tests
+
+__END__
Index: vendors/fcm/1-5/t/Fcm/Util/ClassLoader.t
===================================================================
--- vendors/fcm/1-5/t/Fcm/Util/ClassLoader.t	(revision 1981)
+++ vendors/fcm/1-5/t/Fcm/Util/ClassLoader.t	(revision 1981)
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+################################################################################
+# A class for testing the loader
+{
+    package MyTestClass;
+
+    sub new {
+        my ($class) = @_;
+        return bless(\do{my $annon_scalar}, $class);
+    }
+}
+
+use Test::More qw{no_plan};
+
+main();
+
+sub main {
+    use_ok('Fcm::Util::ClassLoader');
+    test_normal();
+    test_bad();
+}
+
+################################################################################
+# Tests loading classes that should load OK
+sub test_normal {
+    my $prefix = 'normal';
+    my @CLASSES = (
+        'Fcm::CLI::Config',
+        'Fcm::Exception',
+        'Fcm::CLI::Config', # repeat
+        'MyTestClass',
+    );
+    for my $class (@CLASSES) {
+        ok(Fcm::Util::ClassLoader::load($class), "$prefix: load $class");
+    }
+}
+
+################################################################################
+# Tests loading classes that should fail
+sub test_bad {
+    my $prefix = 'bad';
+    my @CLASSES = ('Foo', 'Bar', 'Baz', 'No::Such::Class', 'Foo');
+    for my $class (@CLASSES) {
+        eval {
+            Fcm::Util::ClassLoader::load($class);
+        };
+        isa_ok($@, 'Fcm::Exception', "$prefix: load $class");
+    }
+}
+
+__END__
Index: vendors/fcm/1-5/test/compare_results
===================================================================
--- vendors/fcm/1-5/test/compare_results	(revision 1981)
+++ vendors/fcm/1-5/test/compare_results	(revision 1981)
@@ -0,0 +1,182 @@
+#!/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 and run output 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
+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.build.commands.1 ]]; then
+  CONTROL_FILES=$(ls -1 $TEST_NAME.build.commands.*)
+fi
+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 build command or run output files differ 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
+  if [[ -f control/$TEST_NAME.extract.stdout.1 ]]; then
+    echo "Extract times:"
+    cat control/$TEST_NAME.extract.stdout.1 | grep "\->.*second" > $TEST_NAME.control_extract_times
+    cat test/$TEST_NAME.extract.stdout.1 | 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
+  fi
+  if [[ -f control/$TEST_NAME.build.stdout.1 ]]; then
+    echo "Build times:"
+    cat control/$TEST_NAME.build.stdout.1 | grep "\->.*second" > $TEST_NAME.control_build_times
+    cat test/$TEST_NAME.build.stdout.1 | 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
+  fi
+fi
+
+rm -f $DIFF_OUTPUT
+exit 0
Index: vendors/fcm/1-5/test/create_hpc_batch_script
===================================================================
--- vendors/fcm/1-5/test/create_hpc_batch_script	(revision 1981)
+++ vendors/fcm/1-5/test/create_hpc_batch_script	(revision 1981)
@@ -0,0 +1,57 @@
+#!/bin/ksh
+
+cat <<EOF >$BATCH_SCRIPT
+#!/bin/ksh
+#
+# @ job_type = serial
+# @ class = serial
+# @ resources = ConsumableCpus(4) ConsumableMemory(1gb)
+# @ initialdir = $RUN_DIR_HPC
+# @ output = hpc_batch.stdout
+# @ error  = hpc_batch.stderr
+# @ queue
+
+. prg_12_1_0_2 
+export PATH=$RUN_DIR_HPC/fcm/bin:\$PATH
+export DIFF="/opt/freeware/bin/diff"
+
+. $BATCH_DIRS_NAME
+let failed=0
+for TEST in \$TESTS
+do
+  cd $RUN_DIR_HPC/\$TEST
+  echo "\$(date): Running \$TEST ..."
+  find src -exec touch {} \\;
+  fcm build -v 2 -j 4 >../\$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
+    export DEBUG=$DEBUG
+    cd $BASE_DIR_HPC
+    ./compare_results \$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: vendors/fcm/1-5/test/create_repos
===================================================================
--- vendors/fcm/1-5/test/create_repos	(revision 1981)
+++ vendors/fcm/1-5/test/create_repos	(revision 1981)
@@ -0,0 +1,140 @@
+#!/bin/ksh
+
+REPOS_FILES=$PWD/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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/:/: Message - /' subroutine/hello_sub.h
+svn ci -m" "
+
+# Add a new file
+branch=add_file
+fcm br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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" "
+
+# Delete a file
+branch=delete_file
+fcm br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm subroutine/hello_c.c
+svn ci -m" "
+
+# Delete a Fortran include file
+branch=delete_inc_file
+fcm br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm subroutine/hello_sub.h
+svn ci -m" "
+
+# Delete a directory
+branch=delete_directory
+fcm br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+svn sw $REPOS_URL/branches/dev/Share/$branch
+svn rm subroutine
+svn ci -m" "
+
+# Rename the executable
+branch=exe_rename
+fcm br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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 br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+svn sw $REPOS_URL/branches/dev/Share/$branch
+perl -pi -e 's/hello_constants.inc/hello_constants_inc.f90/g' module/hello_constants.f90
+svn mv module/hello_constants.inc module/hello_constants_inc.f90
+svn ci -m" "
+
+# Add a symbolic link
+branch=symbolic_link
+fcm br -c -n $branch -t SHARE -r 1 --rev-flag NONE --non-interactive $REPOS_URL
+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" "
+
+echo "$(date): Finished"
Index: vendors/fcm/1-5/test/get_hpc_results
===================================================================
--- vendors/fcm/1-5/test/get_hpc_results	(revision 1981)
+++ vendors/fcm/1-5/test/get_hpc_results	(revision 1981)
@@ -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=hpc1e
+ssh $HPC "cd fcm_test_suite; ./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: vendors/fcm/1-5/test/perform_test
===================================================================
--- vendors/fcm/1-5/test/perform_test	(revision 1981)
+++ vendors/fcm/1-5/test/perform_test	(revision 1981)
@@ -0,0 +1,152 @@
+echo "$(date): Running $test_name ..."
+THIS_RUN_DIR=$RUN_DIR/$test_name
+rm -rf $THIS_RUN_DIR
+mkdir $THIS_RUN_DIR
+cd $THIS_RUN_DIR
+if [[ $mirror == remote ]]; then
+  export THIS_RUN_DIR_HPC=$RUN_DIR_HPC/$test_name
+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 2 $REPOS_URL/trunk/cfg/$this_cfg.cfg >../$test_name.extract.stdout.$count 2>../$test_name.extract.stderr.$count
+  RC=$?
+  if [[ $extract == fail ]]; then
+    if [[ $RC == 0 ]]; then
+      echo "FAILED: $test_name ($count) extract did not fail"
+      exit 1
+    fi
+  elif [[ $extract == fail_known ]]; then
+    if [[ $RC == 0 ]]; then
+      echo "FAILED: $test_name ($count) extract did not fail as expected (known problem fixed?)"
+      exit 1
+    else
+      if [[ $DEBUG == true ]]; then
+        echo "Known problem: $test_name ($count) extract failed"
+      fi
+      break
+    fi
+  elif [[ $extract == succeed_known ]]; then
+    if [[ $RC == 0 ]]; then
+      if [[ $DEBUG == true ]]; then
+        echo "Known problem: $test_name ($count) extract did not fail"
+      fi
+      break
+    else
+      echo "FAILED: $test_name ($count) extract did not succeed as expected (known problem fixed?)"
+      exit 1
+    fi
+  else
+    if [[ $RC != 0 ]]; then
+      echo "FAILED: $test_name ($count) extract failed"
+      exit 1
+    else
+      if [[ $mirror == local ]]; then
+        cd ${THIS_RUN_DIR}_mirror
+      elif [[ $mirror == remote ]]; then
+        echo "$test_name" >> $BATCH_DIRS
+        break
+      fi
+      if [[ $DEBUG == true ]]; then
+        echo "Running build ($count) ..."
+      fi
+      export command_file=$RUN_DIR/$test_name.build.commands.$count
+      fcm build -v 2 -j $NPROC >../$test_name.build.stdout.$count 2>../$test_name.build.stderr.$count
+      RC=$?
+      if [[ $build == fail ]]; then
+        if [[ $RC == 0 ]]; then
+          echo "FAILED: $test_name ($count) build did not fail"
+          exit 1
+        fi
+      elif [[ $build == fail_known ]]; then
+        if [[ $RC == 0 ]]; then
+          echo "FAILED: $test_name ($count) build did not fail as expected (known problem fixed?)"
+          exit 1
+        else
+          if [[ $DEBUG == true ]]; then
+            echo "Known problem: $test_name ($count) build failed"
+          fi
+          break
+        fi
+      elif [[ $build == succeed_known ]]; then
+        if [[ $RC == 0 ]]; then
+          if [[ $DEBUG == true ]]; then
+            echo "Known problem: $test_name ($count) build did not fail"
+          fi
+          break
+        else
+          echo "FAILED: $test_name ($count) build did not succeed as expected (known problem fixed?)"
+          exit 1
+        fi
+      else
+        if [[ $RC != 0 ]]; then
+          echo "FAILED: $test_name ($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_name ($count) env file does not exist"
+              exit 1
+            else
+              . $env_file
+              if [[ $DEBUG == true ]]; then
+                echo "Running executable ($count) ..."
+              fi
+              $exe_name >../$test_name.exe.stdout.$count 2>../$test_name.exe.stderr.$count
+              RC=$?
+              if [[ $run == fail ]]; then
+                if [[ $RC == 0 ]]; then
+                  echo "FAILED: $test_name ($count) run did not fail"
+                  exit 1
+                fi
+              elif [[ $run == fail_known ]]; then
+                if [[ $RC == 0 ]]; then
+                  echo "FAILED: $test_name ($count) run did not fail as expected (known problem fixed?)"
+                  exit 1
+                else
+                  if [[ $DEBUG == true ]]; then
+                    echo "Known problem: $test_name ($count) run failed"
+                  fi
+                  break
+                fi
+              elif [[ $run == succeed_known ]]; then
+                if [[ $RC == 0 ]]; then
+                  if [[ $DEBUG == true ]]; then
+                    echo "Known problem: $test_name ($count) run did not fail"
+                  fi
+                  break
+                else
+                  echo "FAILED: $test_name ($count) run did not succeed as expected (known problem fixed?)"
+                  exit 1
+                fi
+              else
+                if [[ $RC != 0 ]]; then
+                  echo "FAILED: $test_name ($count) run failed"
+                  exit 1
+                fi
+              fi
+            fi
+          fi
+        fi
+      fi
+    fi
+  fi
+  let count=count+1
+done
+
+if [[ $TYPE == control ]]; then
+  touch $THIS_RUN_DIR/.tests.complete
+else
+  cd $BASE_DIR
+  ./compare_results $test_name
+fi
Index: vendors/fcm/1-5/test/report_hpc_results
===================================================================
--- vendors/fcm/1-5/test/report_hpc_results	(revision 1981)
+++ vendors/fcm/1-5/test/report_hpc_results	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/add_subroutine/hello.F90
===================================================================
--- vendors/fcm/1-5/test/repos/add_subroutine/hello.F90	(revision 1981)
+++ vendors/fcm/1-5/test/repos/add_subroutine/hello.F90	(revision 1981)
@@ -0,0 +1,20 @@
+PROGRAM Hello
+
+USE Hello_Constants, ONLY: hello_string
+
+IMPLICIT NONE
+
+#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: vendors/fcm/1-5/test/repos/add_subroutine/hello_sub2.f90
===================================================================
--- vendors/fcm/1-5/test/repos/add_subroutine/hello_sub2.f90	(revision 1981)
+++ vendors/fcm/1-5/test/repos/add_subroutine/hello_sub2.f90	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/blockdata/hello_blockdata.F90
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/blockdata/hello_blockdata.F90	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/blockdata/hello_blockdata.F90	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_directory.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_directory.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_directory.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_directory_expsrc.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_directory_expsrc.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_directory_expsrc.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_file.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_file.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_file.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_file_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_file_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_add_file_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/add_file
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_base.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_base.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_base.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_base_inc.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_base_inc.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_base_inc.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_clash.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_clash.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_clash.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_conflict_fail.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_conflict_fail.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_conflict_fail.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_conflict_override.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_conflict_override.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_conflict_override.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_inherit.cfg	(revision 1981)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_inherit_wrong_include.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_inherit_wrong_include.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_inherit_wrong_include.cfg	(revision 1981)
@@ -0,0 +1,12 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_work.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_work.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_branches_merge_work.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_cflags.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_cflags.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_cflags.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::CFLAGS::test/subroutine      -O2
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_change_src_type.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_change_src_type.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_change_src_type.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_directory.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_directory.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_directory.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_directory_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_directory_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_directory_inherit.cfg	(revision 1981)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_file.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_file.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_file.cfg	(revision 1981)
@@ -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_file
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_file_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_file_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_file_inherit.cfg	(revision 1981)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_files_base
+REPOS::test::branch2                    fcm:test_suite_br/dev/Share/delete_file
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_inc_file
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit_force.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit_force.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_inc_file_inherit_force.cfg	(revision 1981)
@@ -0,0 +1,10 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_inc_file
+
+BLD::TOOL::FFLAGS::test/module          -assume nosource_include -O3
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/delete_ppinc_file
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit_force.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit_force.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_delete_ppinc_file_inherit_force.cfg	(revision 1981)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exclude_dependency.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exclude_dependency.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exclude_dependency.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::EXCL_DEP                           USE::Hello_Constants
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exe_permissions.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exe_permissions.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exe_permissions.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exe_rename.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exe_rename.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_exe_rename.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fc.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fc.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fc.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FC                           wrap_fc2
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags1.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags1.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags1.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FFLAGS::test                 %BLD::TOOL::FFLAGS -O2
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags2.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags2.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags2.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FFLAGS::test/subroutine/hello_sub     -O2
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_fflags_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+BLD::TOOL::FFLAGS::test/subroutine/hello_sub     -O2
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_inherit_invalid_path.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_inherit_invalid_path.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_inherit_invalid_path.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     /invalid/path
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_inherit_target.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_inherit_target.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_inherit_target.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+BLD::INHERIT::TARGET                    true
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_invalid_base_url.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_invalid_base_url.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_invalid_base_url.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::base                       fcm:test_suite/invalid
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_invalid_branch_url.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_invalid_branch_url.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_invalid_branch_url.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+REPOS::test::branch1                    fcm:test_suite/invalid
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_ld.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_ld.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_ld.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::LD                           wrap_ld2
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_library.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_library.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_library.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base_inc.cfg
+
+BLD::TARGET                             libtest.a
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_library_rename.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_library_rename.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_library_rename.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_mirror.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_mirror.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_mirror.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_modify_subroutine_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_modify_subroutine_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_modify_subroutine_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_subroutine
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_modify_subroutine_interface_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_modify_subroutine_interface_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_modify_subroutine_interface_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_subroutine_interface
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_no_dep.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_no_dep.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_no_dep.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::NO_DEP                             true
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_ops.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_ops.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_ops.cfg	(revision 1981)
@@ -0,0 +1,209 @@
+# ------------------------------------------------------------------------------
+# 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                                                             gcc
+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                                                             ifort
+bld::tool::fflags                                                         -implicitnone -stand f95 -warn all -i8 -r8 -i-static
+bld::tool::fflags::gcom                                                   -implicitnone -stand f95 -warn all -i8 -r8 -i-static -warn none -I$(OPSDIR)/mpi/mpich2-1.2-ukmo-v1/ifort-10/include
+bld::tool::fflags::ops::src::code::Ops_RTTOV9                             -implicitnone -stand f95 -warn all -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                                                             ifort
+bld::tool::ldflags                                                        -i-static -L$(OPSDIR)/mpi/mpich2-1.2-ukmo-v1/ifort-10/lib -lmpich -lpthread
+bld::tool::make                                                           gmake
+
+# ------------------------------------------------------------------------------
+# 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.2-ukmo-v1/ifort-10/lib -lmpich -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_postproc_hpc.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_postproc_hpc.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_postproc_hpc.cfg	(revision 1981)
@@ -0,0 +1,211 @@
+# ------------------------------------------------------------------------------
+# 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::fflags::gen  -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::pp::trunk         2529
+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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_blockdata.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_blockdata.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_blockdata.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FPPKEYS::test/blockdata      ODD 
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_dependency.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_dependency.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_dependency.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FPPKEYS::test/program/hello 
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_include.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_include.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_include.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_include_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_include_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_change_include_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+REPOS::test::branch1                    fcm:test_suite_br/dev/Share/modify_pp_include
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_base.cfg
+
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub 
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub 
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit_force.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit_force.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_pp_empty_subroutine_inherit_force.cfg	(revision 1981)
@@ -0,0 +1,9 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_fcm1_base
+
+BLD::TOOL::FPPKEYS::test/subroutine/hello_sub 
+BLD::TOOL::LD                           wrap_ld2
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_revmatch_false.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_revmatch_false.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_revmatch_false.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_revmatch_true.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_revmatch_true.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_revmatch_true.cfg	(revision 1981)
@@ -0,0 +1,6 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+INC                                     $HERE/fcm1_revmatch_false.cfg
+
+REVMATCH                                true
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_sps.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_sps.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_sps.cfg	(revision 1981)
@@ -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
+%SPSDIR     /home/h04/cfsa/SPS
+%SPS_LIBDIR /home/h04/cfsa/SPS/libraries
+bld::tool::fc      ifort
+bld::tool::fflags  %FOPT -I%{SPS_LIBDIR}/grib_api/include
+bld::tool::cc      gcc
+bld::tool::cflags  -Wall -O2 -DLOWERCASE -I%{SPS_LIBDIR}/hdf5/include
+bld::tool::ld      ifort
+bld::tool::ldflags -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 \
+                   -ljpeg -lpng
+bld::target   SpsScr_Install
+bld::target   sps__coeffs__Sps_Coeffs.etc
+bld::target   sps__coeffs__Sps_Fire.etc
+bld::exe_name::h5admin  h5admin
+bld::excl_dep use::grib_api
+bld::tool::fflags::gen::src::code::GenMod_UMConstants             %FOPT -w -132
+bld::tool::fflags::sps::src::code::SpsMod_CompositeAutosatHeader  %FOPT -auto -assume byterecl
+bld::tool::cflags::sps::src::code::SpsMod_Image                   -Wall -DLOWERCASE -I%{SPSDIR}/libraries/hdf5/include
+bld::tool::fflags::sps::src::code::SpsMod_ProcessComposite        %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsMod_Utilities               %FOPT -Duse_f90_unix=''
+bld::tool::fflags::sps::src::code::SpsProg_AutosatBatch           %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsTask_HDFReader              %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsProg_ImageGeneration        %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsProg_ImageGrib              %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsTask_ProcessComposite       %FOPT -auto -assume byterecl
+bld::tool::fflags::sps::src::code::SpsProg_AddGrid                %FOPT -auto -assume byterecl
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+repos::sps::base  fcm:sps_tr
+revision::sps::base   3578
+src::sps::base    src/code/SpsMod_AutosatBatch
+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_CompositeAutosatHeader
+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_ImageGeneration
+src::sps::base    src/code/SpsMod_InterpolateModelToPixel
+src::sps::base    src/code/SpsMod_LoadCloudMaskInfo
+src::sps::base    src/code/SpsMod_LoadSatelliteData
+src::sps::base    src/code/SpsMod_MPEFBufrDecode
+src::sps::base    src/code/SpsMod_NimrodFile
+src::sps::base    src/code/SpsMod_ProcessComposite
+src::sps::base    src/code/SpsMod_RTTOV
+src::sps::base    src/code/SpsMod_Reprojection
+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_AddGrid
+src::sps::base    src/code/SpsProg_AutosatBatch
+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_ImageGeneration
+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_ProcessGOES10
+src::sps::base    src/code/SpsProg_RunRTTOV
+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_DisplaySlotStoreData
+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_ProcessComposite
+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/BUFR_15_0
+src::sps::base    src/code/rttov85
+src::sps::base    src/code/rttov9
+src::sps::base    src/idl/Sps_FireDetection
+src::sps::base    src/scripts/Sps_CycleLog
+src::sps::base    src/scripts/Sps_Perl
+src::sps::base    src/scripts/Sps_Scheduler
+src::sps::base    src/scripts/Sps_Scripts
+src::sps::base    src/scripts/Sps_Slotstore
+src::sps::base    src/scripts/Sps_Utils
+expsrc::sps::base coeffs
+expsrc::sps::base control
+
+repos::gen::base  fcm:gen_tr
+revision::gen::base   3209
+src::gen::base    src/code/GenMod_UMConstants
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_suite.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_suite.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_suite.cfg	(revision 1981)
@@ -0,0 +1,8 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+BLD::SRC::test                          $RUN_DIR/test_fcm1_base/bin
+
+BLD::TARGET                             hello.sh
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_symbolic_link.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_symbolic_link.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_symbolic_link.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um.cfg	(revision 1981)
@@ -0,0 +1,52 @@
+# ------------------------------------------------------------------------------
+# 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                                                             gcc
+bld::tool::cpp                                                            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 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                                                             mpif90
+bld::tool::fflags                                                         -i8 -r8 -w -I /home/fr0100/frum/gcom/gcom3.6b/linux_ifort9.0_mpich2/inc -O0
+bld::tool::fppflags                                                       -E -P -traditional -I /home/h04/opsrc/ops0/mpi/mpich2-1.0.8p1-ukmo-v2/ifort-10/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 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                                                             mpif90
+bld::tool::ldflags                                                        -L/home/fr0100/frum/gcom/gcom3.6b/linux_ifort9.0_mpich2/lib -lgcom -Wl,--noinhibit-exec -Vaxlib
+
+# ------------------------------------------------------------------------------
+# Project and branches
+# ------------------------------------------------------------------------------
+
+repos::UM::base        fcm:um-tr/src/
+version::UM::base      vn7.3
+expsrc::UM::base
Index: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_hpc.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_hpc.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_hpc.cfg	(revision 1981)
@@ -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/usr/local/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/usr/local/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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_inherit.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_inherit.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_inherit.cfg	(revision 1981)
@@ -0,0 +1,49 @@
+CFG::TYPE                               ext
+CFG::VERSION                            1.0
+
+DEST::ROOTDIR                           $PWD
+
+USE                                     $RUN_DIR/test_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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_inherit_hpc.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_inherit_hpc.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_um_inherit_hpc.cfg	(revision 1981)
@@ -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/test_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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_var.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_var.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_var.cfg	(revision 1981)
@@ -0,0 +1,228 @@
+# ------------------------------------------------------------------------------
+# 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                                                           gcc
+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                                                           ifort
+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.0.8p1-ukmo-v3/ifort-10/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.0.8p1-ukmo-v3/ifort-10/lib -lmpich -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
+
+# ------------------------------------------------------------------------------
+# 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: vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_var_hpc.cfg
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_var_hpc.cfg	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/cfg/fcm1_var_hpc.cfg	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/module/hello_constants.f90
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/module/hello_constants.f90	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/module/hello_constants.f90	(revision 1981)
@@ -0,0 +1,5 @@
+MODULE Hello_Constants
+
+INCLUDE 'hello_constants.inc'
+
+END MODULE Hello_Constants
Index: vendors/fcm/1-5/test/repos/trunk/module/hello_constants.inc
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/module/hello_constants.inc	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/module/hello_constants.inc	(revision 1981)
@@ -0,0 +1,1 @@
+CHARACTER (LEN=80), PARAMETER :: hello_string = 'Hello Earth!'
Index: vendors/fcm/1-5/test/repos/trunk/namelist/namelist.NL
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/namelist/namelist.NL	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/namelist/namelist.NL	(revision 1981)
@@ -0,0 +1,3 @@
+&TestNL
+  test = ,
+/
Index: vendors/fcm/1-5/test/repos/trunk/pro/hello.pro
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/pro/hello.pro	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/pro/hello.pro	(revision 1981)
@@ -0,0 +1,2 @@
+PRO HELLO
+END
Index: vendors/fcm/1-5/test/repos/trunk/pro/plot.pro
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/pro/plot.pro	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/pro/plot.pro	(revision 1981)
@@ -0,0 +1,3 @@
+PRO PLOT
+;  Calls    : hello.pro
+END
Index: vendors/fcm/1-5/test/repos/trunk/program/hello.F90
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/program/hello.F90	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/program/hello.F90	(revision 1981)
@@ -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: vendors/fcm/1-5/test/repos/trunk/script/hello.sh
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/script/hello.sh	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/script/hello.sh	(revision 1981)
@@ -0,0 +1,5 @@
+#!/usr/bin/ksh
+# Calls : hello.exe
+# Calls : plot.pro
+
+hello.exe
Index: vendors/fcm/1-5/test/repos/trunk/subroutine/hello_c.c
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/subroutine/hello_c.c	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/subroutine/hello_c.c	(revision 1981)
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+void hello_c_ () {
+  printf ("%s\n", "Hello_C: Hello Earth!");
+}
Index: vendors/fcm/1-5/test/repos/trunk/subroutine/hello_sub.F90
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/subroutine/hello_sub.F90	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/subroutine/hello_sub.F90	(revision 1981)
@@ -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.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: vendors/fcm/1-5/test/repos/trunk/subroutine/hello_sub.h
===================================================================
--- vendors/fcm/1-5/test/repos/trunk/subroutine/hello_sub.h	(revision 1981)
+++ vendors/fcm/1-5/test/repos/trunk/subroutine/hello_sub.h	(revision 1981)
@@ -0,0 +1,1 @@
+WRITE (*, '(A)') this // ': ' // TRIM (hello_string)
Index: vendors/fcm/1-5/test/run_tests
===================================================================
--- vendors/fcm/1-5/test/run_tests	(revision 1981)
+++ vendors/fcm/1-5/test/run_tests	(revision 1981)
@@ -0,0 +1,200 @@
+#!/bin/ksh
+
+while getopts ":c:t:flradgh" 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 ;;
+    \? ) 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'
+ 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
+
+export HPC=hpc1e
+export BASE_DIR_HPC=$(ssh $HPC 'echo $PWD')/fcm_test_suite
+
+export BASE_DIR=$LOCALTEMP/fcm_test_suite
+if [[ $DELETE == true ]]; then
+  if [[ $DEBUG == true ]]; then
+    echo "Removing any previous test directory ..."
+  fi
+  rm -rf $BASE_DIR
+  ssh $HPC "rm -rf $BASE_DIR_HPC"
+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 ..."
+  ./create_repos > $BASE_DIR/repos.stdout 2> $BASE_DIR/repos.stderr
+fi
+
+cp compare_results $BASE_DIR
+PATH_BASE=$PWD/wrapper_scripts:$PATH:~opsrc/ops0/mpi/mpich2-1.0.8p1-ukmo-v2/ifort-10/bin
+
+export DEBUG
+export TYPE
+for TYPE in $TYPES
+do
+  if [[ $TYPE == test ]]; then
+    URL=$TEST_URL
+  else
+    URL=$CONTROL_URL
+  fi
+  URL_BASE=${URL%@*}
+  REV=${URL#$URL_BASE}
+  URL=$URL_BASE$REV
+  fcm ls $URL/bin/fcm >/dev/null 2>&1
+  if [[ $? != 0 ]]; then
+    echo 'URL must be a URL or the path to a working copy of the FCM project tree'
+    exit 1
+  fi
+  echo FCM version to be used:
+  fcm info $URL | grep URL
+  fcm info $URL | grep Revision
+
+  export RUN_DIR=$BASE_DIR/$TYPE
+  rm -rf $RUN_DIR
+  mkdir $RUN_DIR
+
+  if [[ $DEBUG == true ]]; then
+    echo "Creating local copy of FCM ..."
+  fi
+  fcm export -q $URL $RUN_DIR/fcm
+  echo "set::url::test_suite $REPOS_URL" >>$RUN_DIR/fcm/etc/fcm.cfg
+  export PATH=$RUN_DIR/fcm/bin:$PATH_BASE
+
+  if [[ $FUNC_TESTS == true ]]; then
+    . ./tests_functional.list
+    export COMPARE_TIMES=false
+    let failed=0
+    for TEST in $TESTS
+    do
+      ./test_scripts/$TEST
+      if [[ $? != 0 ]]; then
+        let failed=failed+1
+      fi
+    done
+
+    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
+    . ./tests_perf_local.list
+    export COMPARE_TIMES=true
+    let failed=0
+    for TEST in $TESTS
+    do
+      ./test_scripts/$TEST
+      if [[ $? != 0 ]]; then
+        let failed=failed+1
+      fi
+    done
+
+    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 == true ]]; 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_results 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
+    ./create_hpc_batch_script
+    export BATCH_DIRS=$RUN_DIR/$BATCH_DIRS_NAME
+
+    echo 'TESTS="' >$BATCH_DIRS
+    . ./tests_perf_remote.list
+    let failed=0
+    for TEST in $TESTS
+    do
+      ./test_scripts/$TEST
+      if [[ $? != 0 ]]; then
+        let failed=failed+1
+      else
+        SUBMIT_REMOTE=true
+      fi
+    done
+    echo '"' >>$BATCH_DIRS
+
+    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: vendors/fcm/1-5/test/test_scripts/test_fcm1_add_directory
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_add_directory	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_add_directory	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_add_directory
+test_name=test_$cfg_name
+build_1=fail_known
+. ./perform_test
+
+# This case fails since the extract system does not detect the new directory.
+# Fixed in FCM 2 ?
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_add_directory_expsrc
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_add_directory_expsrc	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_add_directory_expsrc	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_add_directory_expsrc
+test_name=test_$cfg_name
+. ./perform_test
+
+# Although this case works, the output from the extract system does not correctly
+# identify the file as added from branch1 (unlike in test_add_file).
+# Fixed in FCM 2 ?
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_add_file
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_add_file	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_add_file	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_add_file
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_add_file_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_add_file_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_add_file_inherit	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_add_file_inherit
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_base
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_base	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_base	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_base
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_clash
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_clash	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_clash	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_branches_clash
+test_name=test_$cfg_name
+extract_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_branches_merge
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_conflict_fail
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_conflict_fail	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_conflict_fail	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_branches_merge_conflict_fail
+test_name=test_$cfg_name
+extract_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_conflict_override
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_conflict_override	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_conflict_override	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_branches_merge_conflict_override
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_incremental	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name="fcm1_base fcm1_branches_merge"
+test_name=test_fcm1_branches_merge_incremental
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_inherit	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_branches_merge_inherit
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_inherit_wrong_include
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_inherit_wrong_include	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_inherit_wrong_include	(revision 1981)
@@ -0,0 +1,6 @@
+cfg_name=fcm1_branches_merge_inherit_wrong_include
+test_name=test_$cfg_name
+. ./perform_test
+
+# The wrong include file gets used as demonstrated by the output from running the program.
+# See ticket:206
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_work
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_work	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_branches_merge_work	(revision 1981)
@@ -0,0 +1,8 @@
+cfg_name=fcm1_branches_merge_work
+test_name=test_$cfg_name
+
+cd $BASE_DIR/work
+svn sw -q $REPOS_URL/branches/dev/Share/modify_files_merge2
+cd $OLDPWD
+
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_cflags_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_cflags_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_cflags_incremental	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name="fcm1_base fcm1_cflags fcm1_base"
+test_name=test_fcm1_cflags_incremental
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_change_src_type_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_change_src_type_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_change_src_type_incremental	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name="fcm1_change_src_type fcm1_change_src_type"
+test_name=test_fcm1_change_src_type_incremental
+. ./perform_test
+
+# 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: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_directory
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_directory	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_directory	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_delete_directory
+test_name=test_$cfg_name
+extract_1=fail_known
+. ./perform_test
+
+# This case fails since the extract system does not handle deleted directories.
+# Fixed in FCM 2 ?
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_directory_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_directory_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_directory_inherit	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_delete_directory_inherit
+test_name=test_$cfg_name
+extract_1=fail_known
+. ./perform_test
+
+# This case fails since the extract system does not handle deleted directories.
+# Fixed in FCM 2 ?
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_file
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_file	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_file	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_delete_file
+test_name=test_$cfg_name
+build_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_file_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_file_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_file_inherit	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_delete_file_inherit
+test_name=test_$cfg_name
+build_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_delete_inc_file
+test_name=test_$cfg_name
+build_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file_inherit	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_delete_inc_file_inherit
+test_name=test_$cfg_name
+build_1=succeed_known
+. ./perform_test
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file_inherit_force
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file_inherit_force	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_inc_file_inherit_force	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_delete_inc_file_inherit_force
+test_name=test_$cfg_name
+build_1=succeed_known
+. ./perform_test
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_delete_ppinc_file
+test_name=test_$cfg_name
+build_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file_inherit	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_delete_ppinc_file_inherit
+test_name=test_$cfg_name
+build_1=succeed_known
+. ./perform_test
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file_inherit_force
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file_inherit_force	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_delete_ppinc_file_inherit_force	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_delete_ppinc_file_inherit_force
+test_name=test_$cfg_name
+build_1=succeed_known
+. ./perform_test
+
+# The build succeeds when it should, in fact, fail.
+# See ticket:335
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_exclude_dependency
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_exclude_dependency	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_exclude_dependency	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_exclude_dependency
+test_name=test_$cfg_name
+build_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_permissions
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_permissions	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_permissions	(revision 1981)
@@ -0,0 +1,14 @@
+cfg_name=fcm1_exe_permissions
+test_name=test_$cfg_name
+run_1=fail
+
+cd $BASE_DIR/work
+svn sw -q $REPOS_URL/branches/dev/Share/exe_rename
+chmod 644 script/hello.sh
+cd $OLDPWD
+
+. ./perform_test
+
+cd $BASE_DIR/work
+chmod 755 script/hello.sh
+cd $OLDPWD
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_rename
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_rename	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_rename	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_exe_rename
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_rename_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_rename_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_exe_rename_incremental	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name="fcm1_base fcm1_exe_rename"
+test_name=test_fcm1_exe_rename_incremental
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_fc_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_fc_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_fc_incremental	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name="fcm1_base fcm1_fc fcm1_base"
+test_name=test_fcm1_fc_incremental
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_fflags_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_fflags_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_fflags_incremental	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name="fcm1_base fcm1_fflags1 fcm1_fflags2 fcm1_base"
+test_name=test_fcm1_fflags_incremental
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_fflags_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_fflags_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_fflags_inherit	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_fflags_inherit
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_inherit_invalid_path
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_inherit_invalid_path	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_inherit_invalid_path	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_inherit_invalid_path
+test_name=test_$cfg_name
+extract_1=fail
+. ./perform_test
+
+# Incorrect error message using FCM 1
+# Fixed in FCM 2 ?
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_inherit_target
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_inherit_target	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_inherit_target	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_inherit_target
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_invalid_base_url
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_invalid_base_url	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_invalid_base_url	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_invalid_base_url
+test_name=test_$cfg_name
+extract_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_invalid_branch_url
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_invalid_branch_url	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_invalid_branch_url	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_invalid_branch_url
+test_name=test_$cfg_name
+extract_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_ld_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_ld_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_ld_incremental	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name="fcm1_base fcm1_ld fcm1_base"
+test_name=test_fcm1_ld_incremental
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_library
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_library	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_library	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_library
+test_name=test_$cfg_name
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_library_rename
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_library_rename	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_library_rename	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_library_rename
+test_name=test_$cfg_name
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_mirror
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_mirror	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_mirror	(revision 1981)
@@ -0,0 +1,7 @@
+cfg_name=fcm1_mirror
+test_name=test_$cfg_name
+mirror=local
+
+export THIS_RUN_DIR=$run_dir/$test_name
+
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_modify_subroutine_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_modify_subroutine_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_modify_subroutine_inherit	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_modify_subroutine_inherit
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_modify_subroutine_interface_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_modify_subroutine_interface_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_modify_subroutine_interface_inherit	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_modify_subroutine_interface_inherit
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_no_dep
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_no_dep	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_no_dep	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_no_dep
+test_name=test_$cfg_name
+run_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_ops_parallel
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_ops_parallel	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_ops_parallel	(revision 1981)
@@ -0,0 +1,5 @@
+cfg_name=fcm1_ops
+test_name=test_${cfg_name}_parallel
+NPROC=6
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_postproc_hpc
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_postproc_hpc	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_postproc_hpc	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_postproc_hpc
+test_name=test_$cfg_name
+mirror=remote
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_include
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_include	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_include	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_pp_change_include
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_include_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_include_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_include_inherit	(revision 1981)
@@ -0,0 +1,6 @@
+cfg_name=fcm1_pp_change_include_inherit
+test_name=test_$cfg_name
+. ./perform_test
+
+# The wrong include file gets used as demonstrated by the lack of anything being compiled.
+# See ticket:206
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_keys_incremental
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_keys_incremental	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_change_keys_incremental	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name="fcm1_pp_change_dependency fcm1_base fcm1_pp_change_blockdata"
+test_name=test_fcm1_pp_change_keys_incremental
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_pp_empty_subroutine
+test_name=test_$cfg_name
+build_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine_inherit	(revision 1981)
@@ -0,0 +1,6 @@
+cfg_name=fcm1_pp_empty_subroutine_inherit
+test_name=test_$cfg_name
+build_1=succeed_known
+. ./perform_test
+
+# This case does not fail as expected (see #335)
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine_inherit_force
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine_inherit_force	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_pp_empty_subroutine_inherit_force	(revision 1981)
@@ -0,0 +1,8 @@
+cfg_name=fcm1_pp_empty_subroutine_inherit_force
+test_name=test_$cfg_name
+build_1=fail
+. ./perform_test
+
+# 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: vendors/fcm/1-5/test/test_scripts/test_fcm1_revmatch_false
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_revmatch_false	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_revmatch_false	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_revmatch_false
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_revmatch_true
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_revmatch_true	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_revmatch_true	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_revmatch_true
+test_name=test_$cfg_name
+extract_1=fail
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_sps_parallel
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_sps_parallel	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_sps_parallel	(revision 1981)
@@ -0,0 +1,5 @@
+cfg_name=fcm1_sps
+test_name=test_${cfg_name}_parallel
+NPROC=6
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_suite
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_suite	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_suite	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_suite
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_symbolic_link
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_symbolic_link	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_symbolic_link	(revision 1981)
@@ -0,0 +1,3 @@
+cfg_name=fcm1_symbolic_link
+test_name=test_$cfg_name
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_um
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_um	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_um	(revision 1981)
@@ -0,0 +1,5 @@
+cfg_name=fcm1_um
+test_name=test_$cfg_name
+NPROC=6
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_um_hpc
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_um_hpc	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_um_hpc	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_um_hpc
+test_name=test_$cfg_name
+mirror=remote
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_um_inherit
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_um_inherit	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_um_inherit	(revision 1981)
@@ -0,0 +1,5 @@
+cfg_name=fcm1_um_inherit
+test_name=test_$cfg_name
+NPROC=6
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_um_inherit_hpc
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_um_inherit_hpc	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_um_inherit_hpc	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_um_inherit_hpc
+test_name=test_$cfg_name
+mirror=remote
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_var
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_var	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_var	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_var
+test_name=test_$cfg_name
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_var_hpc
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_var_hpc	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_var_hpc	(revision 1981)
@@ -0,0 +1,4 @@
+cfg_name=fcm1_var_hpc
+test_name=test_$cfg_name
+mirror=remote
+. ./perform_test
Index: vendors/fcm/1-5/test/test_scripts/test_fcm1_var_parallel
===================================================================
--- vendors/fcm/1-5/test/test_scripts/test_fcm1_var_parallel	(revision 1981)
+++ vendors/fcm/1-5/test/test_scripts/test_fcm1_var_parallel	(revision 1981)
@@ -0,0 +1,5 @@
+cfg_name=fcm1_var
+test_name=test_${cfg_name}_parallel
+NPROC=6
+run_1=no
+. ./perform_test
Index: vendors/fcm/1-5/test/tests_functional.list
===================================================================
--- vendors/fcm/1-5/test/tests_functional.list	(revision 1981)
+++ vendors/fcm/1-5/test/tests_functional.list	(revision 1981)
@@ -0,0 +1,55 @@
+TESTS="
+test_fcm1_base
+test_fcm1_branches_merge
+test_fcm1_branches_merge_work
+test_fcm1_branches_merge_incremental
+test_fcm1_branches_merge_inherit
+test_fcm1_branches_merge_inherit_wrong_include
+test_fcm1_branches_merge_conflict_override
+test_fcm1_branches_merge_conflict_fail
+test_fcm1_branches_clash
+test_fcm1_add_file
+test_fcm1_add_file_inherit
+test_fcm1_add_directory
+test_fcm1_add_directory_expsrc
+test_fcm1_delete_file
+test_fcm1_delete_file_inherit
+test_fcm1_delete_inc_file
+test_fcm1_delete_inc_file_inherit
+test_fcm1_delete_inc_file_inherit_force
+test_fcm1_delete_ppinc_file
+test_fcm1_delete_ppinc_file_inherit
+test_fcm1_delete_ppinc_file_inherit_force
+test_fcm1_delete_directory
+test_fcm1_delete_directory_inherit
+test_fcm1_invalid_base_url
+test_fcm1_invalid_branch_url
+test_fcm1_inherit_invalid_path
+test_fcm1_mirror
+test_fcm1_revmatch_false
+test_fcm1_revmatch_true
+test_fcm1_exe_rename
+test_fcm1_exe_rename_incremental
+test_fcm1_exe_permissions
+test_fcm1_fflags_incremental
+test_fcm1_fflags_inherit
+test_fcm1_cflags_incremental
+test_fcm1_fc_incremental
+test_fcm1_ld_incremental
+test_fcm1_modify_subroutine_inherit
+test_fcm1_modify_subroutine_interface_inherit
+test_fcm1_pp_empty_subroutine
+test_fcm1_pp_empty_subroutine_inherit
+test_fcm1_pp_empty_subroutine_inherit_force
+test_fcm1_pp_change_keys_incremental
+test_fcm1_pp_change_include
+test_fcm1_pp_change_include_inherit
+test_fcm1_library
+test_fcm1_library_rename
+test_fcm1_suite
+test_fcm1_exclude_dependency
+test_fcm1_change_src_type_incremental
+test_fcm1_no_dep
+test_fcm1_symbolic_link
+test_fcm1_inherit_target
+"
Index: vendors/fcm/1-5/test/tests_perf_local.list
===================================================================
--- vendors/fcm/1-5/test/tests_perf_local.list	(revision 1981)
+++ vendors/fcm/1-5/test/tests_perf_local.list	(revision 1981)
@@ -0,0 +1,8 @@
+TESTS="
+test_fcm1_var
+test_fcm1_var_parallel
+test_fcm1_ops_parallel
+test_fcm1_sps_parallel
+test_fcm1_um
+test_fcm1_um_inherit
+"
Index: vendors/fcm/1-5/test/tests_perf_remote.list
===================================================================
--- vendors/fcm/1-5/test/tests_perf_remote.list	(revision 1981)
+++ vendors/fcm/1-5/test/tests_perf_remote.list	(revision 1981)
@@ -0,0 +1,6 @@
+TESTS="
+test_fcm1_var_hpc
+test_fcm1_postproc_hpc
+test_fcm1_um_hpc
+test_fcm1_um_inherit_hpc
+"
Index: vendors/fcm/1-5/test/wrapper_scripts/wrap_ar
===================================================================
--- vendors/fcm/1-5/test/wrapper_scripts/wrap_ar	(revision 1981)
+++ vendors/fcm/1-5/test/wrapper_scripts/wrap_ar	(revision 1981)
@@ -0,0 +1,2 @@
+echo "wrap_ar $@" | sed "s#$RUN_DIR##g" >>$command_file
+ar  "$@"
Index: vendors/fcm/1-5/test/wrapper_scripts/wrap_cc
===================================================================
--- vendors/fcm/1-5/test/wrapper_scripts/wrap_cc	(revision 1981)
+++ vendors/fcm/1-5/test/wrapper_scripts/wrap_cc	(revision 1981)
@@ -0,0 +1,2 @@
+echo "wrap_cc $@" | sed "s#$RUN_DIR##g" >>$command_file
+gcc  "$@"
Index: vendors/fcm/1-5/test/wrapper_scripts/wrap_fc
===================================================================
--- vendors/fcm/1-5/test/wrapper_scripts/wrap_fc	(revision 1981)
+++ vendors/fcm/1-5/test/wrapper_scripts/wrap_fc	(revision 1981)
@@ -0,0 +1,2 @@
+echo "wrap_fc $@" | sed "s#$RUN_DIR##g" >>$command_file
+ifort  "$@"
Index: vendors/fcm/1-5/test/wrapper_scripts/wrap_fc2
===================================================================
--- vendors/fcm/1-5/test/wrapper_scripts/wrap_fc2	(revision 1981)
+++ vendors/fcm/1-5/test/wrapper_scripts/wrap_fc2	(revision 1981)
@@ -0,0 +1,2 @@
+echo "wrap_fc2 $@" | sed "s#$RUN_DIR##g" >>$command_file
+ifort  "$@"
Index: vendors/fcm/1-5/test/wrapper_scripts/wrap_ld
===================================================================
--- vendors/fcm/1-5/test/wrapper_scripts/wrap_ld	(revision 1981)
+++ vendors/fcm/1-5/test/wrapper_scripts/wrap_ld	(revision 1981)
@@ -0,0 +1,2 @@
+echo "wrap_ld $@" | sed "s#$RUN_DIR##g" >>$command_file
+ifort  "$@"
Index: vendors/fcm/1-5/test/wrapper_scripts/wrap_ld2
===================================================================
--- vendors/fcm/1-5/test/wrapper_scripts/wrap_ld2	(revision 1981)
+++ vendors/fcm/1-5/test/wrapper_scripts/wrap_ld2	(revision 1981)
@@ -0,0 +1,2 @@
+echo "wrap_ld2 $@" | sed "s#$RUN_DIR##g" >>$command_file
+ifort  "$@"
Index: vendors/fcm/1-5/test/wrapper_scripts/wrap_pp
===================================================================
--- vendors/fcm/1-5/test/wrapper_scripts/wrap_pp	(revision 1981)
+++ vendors/fcm/1-5/test/wrapper_scripts/wrap_pp	(revision 1981)
@@ -0,0 +1,2 @@
+echo "wrap_pp $@" | sed "s#$RUN_DIR##g" >>$command_file
+cpp  "$@"
Index: vendors/fcm/1-5/tutorial/README
===================================================================
--- vendors/fcm/1-5/tutorial/README	(revision 1981)
+++ vendors/fcm/1-5/tutorial/README	(revision 1981)
@@ -0,0 +1,25 @@
+This directory contains the files necessary to set up a Subversion repository
+for the FCM tutorial.
+
+The "svn.dump" file should be loaded into an empty Subversion repository using
+the "svnadmin load" command.
+
+The hook scripts in "hooks/" should then be installed in this repository in
+order to prevent any commits to the trunk. Note that the configuration file
+"svnperms.conf" assumes that the tutorial repository is called "tutorial_svn".
+Please edit this file if you use a different name.
+
+For example (in bash/ksh):
+
+(shell)$ cd fcm-release/tutorial/ # to this directory
+(shell)$ path_to_tutorial_repos=/path/to/repos/tutorial_svn
+(shell)$ svnadmin create $path_to_tutorial_repos
+(shell)$ svnadmin load $path_to_tutorial_repos <svn.dump
+(shell)$ cp hooks/* $path_to_repos/tutorial_svn/hooks/
+
+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: vendors/fcm/1-5/tutorial/hooks/pre-commit
===================================================================
--- vendors/fcm/1-5/tutorial/hooks/pre-commit	(revision 1981)
+++ vendors/fcm/1-5/tutorial/hooks/pre-commit	(revision 1981)
@@ -0,0 +1,6 @@
+#!/bin/sh
+REPOS=$1
+TXN=$2
+SCRIPT=$REPOS/hooks/svnperms.py
+CONFIG=$REPOS/hooks/svnperms.conf
+$SCRIPT -r "$REPOS" -t "$TXN" -f $CONFIG || exit $?
Index: vendors/fcm/1-5/tutorial/hooks/svnperms.conf
===================================================================
--- vendors/fcm/1-5/tutorial/hooks/svnperms.conf	(revision 1981)
+++ vendors/fcm/1-5/tutorial/hooks/svnperms.conf	(revision 1981)
@@ -0,0 +1,10 @@
+# ----------------------------------------------------------------- #
+# Access Configuration file for tutorial_svn                        #
+# Note: Use blank separators in the group lists, rather than commas #
+# ----------------------------------------------------------------- #
+
+[message]
+permerrors_prefix = Commits to the trunk are not permitted in the tutorial repository
+
+[tutorial_svn]
+tutorial/branches/[^/]+/.* = *(add,remove,update)
Index: vendors/fcm/1-5/tutorial/hooks/svnperms.py
===================================================================
--- vendors/fcm/1-5/tutorial/hooks/svnperms.py	(revision 1981)
+++ vendors/fcm/1-5/tutorial/hooks/svnperms.py	(revision 1981)
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+
+# $HeadURL: https://svn.collab.net/repos/svn/trunk/tools/hook-scripts/svnperms.py $
+# $LastChangedDate: 2005-03-17 04:39:51 -0600 (Thu, 17 Mar 2005) $
+# $LastChangedBy: maxb $
+# $LastChangedRevision: 13456 $
+
+import commands
+import sys, os
+import getopt
+import re
+
+__author__ = "Gustavo Niemeyer <niemeyer@conectiva.com>"
+
+class Error(Exception): pass
+
+SECTION = re.compile(r'\[([^]]+)\]')
+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.xreadlines():
+            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)
+                    cursectdict = self._sections_dict.setdefault(sectname, {})
+                    cursectlist = []
+                    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 self._sections_dict.keys()
+
+    def options(self, section):
+        return 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:
+            self._group[option] = value.split()
+            
+    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 = commands.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 kwargs.has_key(key):
+                execcmd_kwargs[key] = kwargs[key]
+        return self._execcmd(*execcmd_args, **execcmd_kwargs)
+
+    def _add_txnrev(self, cmd_args, received_kwargs):
+        if received_kwargs.has_key("txn"):
+            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 received_kwargs.has_key("rev"):
+            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:
+        SEC_NAME = "message"
+        OPT_NAME = "permerrors_prefix"
+        message = config.get(SEC_NAME, OPT_NAME)
+        if message is None:
+            message = config.get(" ".join((section, SEC_NAME)), OPT_NAME)
+        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 commited it (for tests)
+    -h         Show this message
+"""
+
+class MissingArgumentsException(Exception):
+    "Thrown when required arguments are missing."
+    pass
+
+def parse_options():
+    try:
+        opts, args = getopt.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
