//
// aegis - project change supervisor
// Copyright (C) 2004-2006, 2008 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// .
//
//
// Directory
//
// Additional data:
//
// Response expected: no.
// Root required: yes.
//
// Tell the server what directory to use. The should be
// a directory name from a previous server response. Note that this
// both gives a default for 'Entry' and 'Modified' and also for 'ci'
// and the other commands; normal usage is to send 'Directory' for each
// directory in which there will be an 'Entry' or 'Modified', and then
// a final 'Directory' for the original directory, then the command.
// The is relative to the top level at which the
// command is occurring (i.e. the last 'Directory' which is sent before
// the command); to indicate that top level, "." should be sent for
// .
//
// Here is an example of where a client gets and
// . Suppose that there is a module defined by
//
// moddir 1dir
//
// That is, one can check out 'moddir' and it will take '1dir' in
// the repository and check it out to 'moddir' in the working
// directory. Then an initial check out could proceed like this:
//
// C: Root /home/kingdon/zwork/cvsroot
// . . .
// C: Argument moddir
// C: Directory .
// C: /home/kingdon/zwork/cvsroot
// C: co
// S: Clear-sticky moddir/
// S: /home/kingdon/zwork/cvsroot/1dir/
// . . .
// S: ok
//
// In this example the response shown is 'Clear-sticky', but it could
// be another response instead. Note that it returns two pathnames.
// The first one, "moddir/", indicates the working
// directory to check out into. The second one, ending in "1dir/",
// indicates the directory to pass back to the server in a subsequent
// 'Directory' request. For example, a subsequent 'update'
// request might look like:
//
// C: Directory moddir
// C: /home/kingdon/zwork/cvsroot/1dir
// . . .
// C: update
//
// For a given , the repository will be the same for
// each of the responses, so one can use the repository from whichever
// response is most convenient. Typically a client will store the
// repository along with the sources for each , use
// that same setting whenever operating on that ,
// and not update the setting as long as the exists.
//
// A client is free to rename a at any time (for
// example, in response to an explicit user request). While it is true
// that the server supplies a to the client, as noted
// above, this is only the default place to put the directory. Of course,
// the various 'Directory' requests for a single command (for example,
// 'update' or 'ci' request) should name a particular directory with
// the same .
//
// Each 'Directory' request specifies a brand-new
// and ; that is, and are
// never relative to paths specified in any previous 'Directory' request.
//
// Here's a more complex example, in which we request an update of a
// working directory which has been checked out from multiple places in
// the repository.
//
// C: Argument dir1
// C: Directory dir1
// C: /home/foo/repos/mod1
// . . .
// C: Argument dir2
// C: Directory dir2
// C: /home/foo/repos/mod2
// . . .
// C: Argument dir3
// C: Directory dir3/subdir3
// C: /home/foo/repos/mod3
// . . .
// C: update
//
// While directories 'dir1' and 'dir2' will be handled in similar fashion
// to the other examples given above, 'dir3' is slightly different from
// the server's standpoint. Notice that module 'mod3' is actually
// checked out into 'dir3/subdir3', meaning that directory 'dir3' is
// either empty or does not contain data checked out from this repository.
//
// The above example will work correctly in cvs 1.10.1 and later.
// The server will descend the tree starting from all directories
// mentioned in 'Argument' requests and update those directories
// specifically mentioned in 'Directory' requests.
//
// Previous versions of cvs (1.10 and earlier) do not behave the same way.
// While the descent of the tree begins at all directories mentioned in
// 'Argument' requests, descent into subdirectories only occurs if a
// directory has been mentioned in a 'Directory' request. Therefore, the
// above example would succeed in updating 'dir1' and 'dir2', but would
// skip 'dir3' because that directory was not specifically mentioned in
// a 'Directory' request. A functional version of the above that would
// run on a 1.10 or earlier server is as follows:
//
// C: Argument dir1
// C: Directory dir1
// C: /home/foo/repos/mod1
// . . .
// C: Argument dir2
// C: Directory dir2
// C: /home/foo/repos/mod2
// . . .
// C: Argument dir3
// C: Directory dir3
// C: /home/foo/repos/.
// . . .
// C: Directory dir3/subdir3
// C: /home/foo/repos/mod3
// . . .
// C: update
//
// Note the extra 'Directory dir3' request. It might be better to use
// 'Emptydir' as the repository for the 'dir3' directory, but the above
// will certainly work.
//
// One more peculiarity of the 1.10 and earlier protocol is the ordering
// of 'Directory' arguments. In order for a subdirectory to be registered
// correctly for descent by the recursion processor, its parent must
// be sent first. For example, the following would not work to update
// 'dir3/subdir3':
//
// . . .
// C: Argument dir3
// C: Directory dir3/subdir3
// C: /home/foo/repos/mod3
// . . .
// C: Directory dir3
// C: /home/foo/repos/.
// . . .
// C: update
//
// The implementation of the server in 1.10 and earlier writes the
// administration files for a given directory at the time of the
// 'Directory' request. It also tries to register the directory
// with its parent to mark it for recursion. In the above example,
// at the time 'dir3/subdir3' is created, the physical directory for
// 'dir3' will be created on disk, but the administration files will
// not have been created. Therefore, when the server tries to register
// 'dir3/subdir3' for recursion, the operation will silently fail because
// the administration files do not yet exist for 'dir3'.
//
#include
#include
#include
#include
#include
request_directory::~request_directory()
{
}
request_directory::request_directory()
{
}
void
request_directory::run_inner(server_ty *sp, string_ty *client_side)
const
{
static const char root_path[] = ROOT_PATH;
size_t root_path_len;
assert(sp);
assert(sp->np);
if (server_root_required(sp, "Directory"))
return;
nstring server_side;
if (!sp->np->getline(server_side))
{
server_error(sp, "Directory: additional data required");
return;
}
//
// Make sure the server side is below the root.
// (Seems weird that the protocol doesn't elide this already.)
//
root_path_len = strlen(root_path);
if
(
server_side.size() == root_path_len
&&
0 == memcmp(server_side.c_str(), root_path, root_path_len)
)
{
server_side = ".";
}
else if
(
server_side.size() > root_path_len
&&
0 == memcmp(server_side.c_str(), root_path, root_path_len)
&&
server_side[root_path_len] == '/'
)
{
//
// Strip out the Root part, we don't need it because it's fake,
// and it makes some of the other processing cumbersome.
//
server_side =
nstring
(
server_side.c_str() + (root_path_len + 1),
server_side.size() - (root_path_len + 1)
);
}
else
{
server_error(sp, "Directory: server-side path must include Root");
return;
}
sp->np->directory_set(client_side, server_side.get_ref());
}
const char *
request_directory::name()
const
{
return "Directory";
}
bool
request_directory::reset()
const
{
return false;
}