FreeBSD/contrib/subversion/subversion/libsvn_fs_x/tree.c
/* Open the node identified by PATH in ROOT, allocating in POOL.  Set
   *PARENT_PATH_P to a path from the node up to ROOT.  The resulting
   **PARENT_PATH_P value is guaranteed to contain at least one
   *element, for the root directory.  PATH must be in canonical form.

   If resulting *PARENT_PATH_P will eventually be made mutable and
   modified, or if copy ID inheritance information is otherwise needed,
   IS_TXN_PATH must be set.  If IS_TXN_PATH is FALSE, no copy ID
   inheritance information will be calculated for the *PARENT_PATH_P chain.

   If FLAGS & open_path_last_optional is zero, return the error
   SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
   non-zero, require all the parent directories to exist as normal,
   but if the final path component doesn't exist, simply return a path
   whose bottom `node' member is zero.  This option is useful for
   callers that create new nodes --- we find the parent directory for
   them, and tell them whether the entry exists already.

   The remaining bits in FLAGS are hints that allow this function
   to take shortcuts based on knowledge that the caller provides,
   such as the caller is not actually being interested in PARENT_PATH_P,
   but only in (*PARENT_PATH_P)->NODE.

   NOTE: Public interfaces which only *read* from the filesystem
   should not call this function directly, but should instead use
   get_dag().
*/
Open the node identified by PATH in ROOT, allocating in POOL.  Set
PARENT_PATH_P to a path from the node up to ROOT.  The resulting
PARENT_PATH_P value is guaranteed to contain at least one
element, for the root directory.  PATH must be in canonical form.

If resulting *PARENT_PATH_P will eventually be made mutable and
modified, or if copy ID inheritance information is otherwise needed,
IS_TXN_PATH must be set.  If IS_TXN_PATH is FALSE, no copy ID
inheritance information will be calculated for the *PARENT_PATH_P chain.

If FLAGS & open_path_last_optional is zero, return the error
SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
non-zero, require all the parent directories to exist as normal,
but if the final path component doesn't exist, simply return a path
whose bottom `node' member is zero.  This option is useful for
callers that create new nodes --- we find the parent directory for
them, and tell them whether the entry exists already.

The remaining bits in FLAGS are hints that allow this function
to take shortcuts based on knowledge that the caller provides,
such as the caller is not actually being interested in PARENT_PATH_P,
but only in (*PARENT_PATH_P)->NODE.

Public interfaces which only *read* from the filesystem
should not call this function directly, but should instead use
get_dag().
Open the node identified by PATH in ROOT allocating in POOL . Set PARENT_PATH_P to a path from the node up to ROOT . The resulting PARENT_PATH_P value is guaranteed to contain at least one element for the root directory . PATH must be in canonical form . If resulting * PARENT_PATH_P will eventually be made mutable and modified or if copy ID inheritance information is otherwise needed IS_TXN_PATH must be set . If IS_TXN_PATH is FALSE no copy ID inheritance information will be calculated for the * PARENT_PATH_P chain . If FLAGS & open_path_last_optional is zero return the error SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist . If non - zero require all the parent directories to exist as normal but if the final path component doesn ' t exist simply return a path whose bottom ` node ' member is zero . This option is useful for callers that create new nodes --- we find the parent directory for them and tell them whether the entry exists already . The remaining bits in FLAGS are hints that allow this function to take shortcuts based on knowledge that the caller provides such as the caller is not actually being interested in PARENT_PATH_P but only in ( * PARENT_PATH_P ) - > NODE . Public interfaces which only * read * from the filesystem should not call this function directly but should instead use get_dag () .
static svn_error_t *
open_path(parent_path_t **parent_path_p,
          svn_fs_root_t *root,
          const char *path,
          int flags,
          svn_boolean_t is_txn_path,
          apr_pool_t *pool)
{
  svn_fs_t *fs = root->fs;
  dag_node_t *here = NULL; 
  parent_path_t *parent_path; 
  const char *rest = NULL; 
  apr_pool_t *iterpool = svn_pool_create(pool);
  svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
  apr_size_t path_len = path_so_far->len;
  assert(svn_fs__is_canonical_abspath(path));
  path_so_far->len = 0; 
  if (flags & open_path_node_only)
    {
      const char *directory;
      if (!root->is_txn_root)
        {
          dag_node_t *node;
          SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
          if (node)
            {
              svn_pool_destroy(iterpool);
              parent_path = make_parent_path(node, 0, 0, pool);
              parent_path->copy_inherit = copy_id_inherit_self;
              *parent_path_p = parent_path;
              return SVN_NO_ERROR;
            }
        }
      directory = svn_dirent_dirname(path, pool);
      if (directory[1] != 0) 
        {
          SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
          if (here)
            {
              apr_size_t dirname_len = strlen(directory);
              path_so_far->len = dirname_len;
              rest = path + dirname_len + 1;
            }
        }
    }
  if (!here)
    {
      SVN_ERR(root_node(&here, root, pool, iterpool));
      rest = path + 1; 
    }
  path_so_far->data[path_so_far->len] = '\0';
  parent_path = make_parent_path(here, 0, 0, pool);
  parent_path->copy_inherit = copy_id_inherit_self;
  for (;;)
    {
      const char *next;
      char *entry;
      dag_node_t *child;
      svn_pool_clear(iterpool);
      here = parent_path->node;
      entry = svn_fs__next_entry_name(&next, rest, pool);
      path_so_far->data[path_so_far->len] = '/';
      path_so_far->len += strlen(entry) + 1;
      path_so_far->data[path_so_far->len] = '\0';
      if (*entry != '\0')
        {
          copy_id_inherit_t inherit;
          const char *copy_path = NULL;
          dag_node_t *cached_node = NULL;
          if (next || !(flags & open_path_uncached))
            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
                                       pool));
          if (cached_node)
            child = cached_node;
          else
            SVN_ERR(svn_fs_x__dag_open(&child, here, entry, pool, iterpool));
          if (child == NULL)
            {
              if ((flags & open_path_last_optional)
                  && (! next || *next == '\0'))
                {
                  parent_path = make_parent_path(NULL, entry, parent_path,
                                                 pool);
                  break;
                }
              else if (flags & open_path_allow_null)
                {
                  parent_path = NULL;
                  break;
                }
              else
                {
                  return SVN_FS__NOT_FOUND(root, path);
                }
            }
          if (flags & open_path_node_only)
            {
              parent_path->node = svn_fs_x__dag_copy_into_pool(child, pool);
            }
          else
            {
              parent_path = make_parent_path(child, entry, parent_path, pool);
              if (is_txn_path)
                {
                  SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
                                               parent_path, iterpool));
                  parent_path->copy_inherit = inherit;
                  parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
                }
            }
          if (! cached_node)
            SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
                                       iterpool));
        }
      if (! next)
        break;
      if (svn_fs_x__dag_node_kind(child) != svn_node_dir)
        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data),
                  apr_psprintf(iterpool, _("Failure opening '%s'"), path));
      rest = next;
    }
  svn_pool_destroy(iterpool);
  *parent_path_p = parent_path;
  return SVN_NO_ERROR;
}
static svn_error_t * open_path ( parent_path_t * * parent_path_p , svn_fs_root_t * root , const char * path , int flags , svn_boolean_t is_txn_path , apr_pool_t * pool ) { svn_fs_t * fs = root -> fs ; dag_node_t * here = NULL ; parent_path_t * parent_path ; const char * rest = NULL ; apr_pool_t * iterpool = svn_pool_create ( pool ) ; svn_stringbuf_t * path_so_far = svn_stringbuf_create ( path , pool ) ; apr_size_t path_len = path_so_far -> len ; assert ( svn_fs__is_canonical_abspath ( path ) ) ; path_so_far -> len = 0 ; if ( flags & open_path_node_only ) { const char * directory ; if ( ! root -> is_txn_root ) { dag_node_t * node ; SVN_ERR ( try_match_last_node ( & node , root , path , path_len , iterpool ) ) ; if ( node ) { svn_pool_destroy ( iterpool ) ; parent_path = make_parent_path ( node , 0 , 0 , pool ) ; parent_path -> copy_inherit = copy_id_inherit_self ; * parent_path_p = parent_path ; return SVN_NO_ERROR ; } } directory = svn_dirent_dirname ( path , pool ) ; if ( directory [ 1 ] != 0 ) { SVN_ERR ( dag_node_cache_get ( & here , root , directory , pool ) ) ; if ( here ) { apr_size_t dirname_len = strlen ( directory ) ; path_so_far -> len = dirname_len ; rest = path + dirname_len + 1 ; } } } if ( ! here ) { SVN_ERR ( root_node ( & here , root , pool , iterpool ) ) ; rest = path + 1 ; } path_so_far -> data [ path_so_far -> len ] = ' \0 ' ; parent_path = make_parent_path ( here , 0 , 0 , pool ) ; parent_path -> copy_inherit = copy_id_inherit_self ; for ( ; ; ) { const char * next ; char * entry ; dag_node_t * child ; svn_pool_clear ( iterpool ) ; here = parent_path -> node ; entry = svn_fs__next_entry_name ( & next , rest , pool ) ; path_so_far -> data [ path_so_far -> len ] = ' ' ; path_so_far -> len += strlen ( entry ) + 1 ; path_so_far -> data [ path_so_far -> len ] = ' \0 ' ; if ( * entry != ' \0 ' ) { copy_id_inherit_t inherit ; const char * copy_path = NULL ; dag_node_t * cached_node = NULL ; if ( next || ! ( flags & open_path_uncached ) ) SVN_ERR ( dag_node_cache_get ( & cached_node , root , path_so_far -> data , pool ) ) ; if ( cached_node ) child = cached_node ; else SVN_ERR ( svn_fs_x__dag_open ( & child , here , entry , pool , iterpool ) ) ; if ( child == NULL ) { if ( ( flags & open_path_last_optional ) && ( ! next || * next == ' \0 ' ) ) { parent_path = make_parent_path ( NULL , entry , parent_path , pool ) ; break ; } else if ( flags & open_path_allow_null ) { parent_path = NULL ; break ; } else { return SVN_FS__NOT_FOUND ( root , path ) ; } } if ( flags & open_path_node_only ) { parent_path -> node = svn_fs_x__dag_copy_into_pool ( child , pool ) ; } else { parent_path = make_parent_path ( child , entry , parent_path , pool ) ; if ( is_txn_path ) { SVN_ERR ( get_copy_inheritance ( & inherit , & copy_path , fs , parent_path , iterpool ) ) ; parent_path -> copy_inherit = inherit ; parent_path -> copy_src_path = apr_pstrdup ( pool , copy_path ) ; } } if ( ! cached_node ) SVN_ERR ( dag_node_cache_set ( root , path_so_far -> data , child , iterpool ) ) ; } if ( ! next ) break ; if ( svn_fs_x__dag_node_kind ( child ) != svn_node_dir ) SVN_ERR_W ( SVN_FS__ERR_NOT_DIRECTORY ( fs , path_so_far -> data ) , apr_psprintf ( iterpool , _ ( " " ) , path ) ) ; rest = next ; } svn_pool_destroy ( iterpool ) ; * parent_path_p = parent_path ; return SVN_NO_ERROR ; }
Open the node identified by PATH in ROOT, allocating in POOL.
Open the node identified by PATH in ROOT allocating in POOL .
/* The directory we're currently looking at.  */ /* The path from HERE up to the root. */ /* The portion of PATH we haven't traversed yet. */ /* path to the currently processed entry without trailing '/'.
     We will reuse this across iterations by simply putting a NUL terminator
     at the respective position and replacing that with a '/' in the next
     iteration.  This is correct as we assert() PATH to be canonical. */ /* Callers often traverse the DAG in some path-based order or along the
     history segments.  That allows us to try a few guesses about where to
     find the next item.  This is only useful if the caller didn't request
     the full parent chain. */ /* "" */ /* First attempt: Assume that we access the DAG for the same path as
         in the last lookup but for a different revision that happens to be
         the last revision that touched the respective node.  This is a
         common pattern when e.g. checking out over ra_serf.  Note that this
         will only work for committed data as the revision info for nodes in
         txns is bogus.

         This shortcut is quick and will exit this function upon success.
         So, try it first. */ /* Did the shortcut work? */ /* Construct and return the result. */ /* Second attempt: Try starting the lookup immediately at the parent
         node.  We will often have recently accessed either a sibling or
         said parent DIRECTORY itself for the same revision. */ /* root nodes are covered anyway */ /* Did the shortcut work? */ /* did the shortcut work? */ /* Make a parent_path item for the root node, using its own current
         copy id.  */ /* skip the leading '/', it saves in iteration */ /* Whenever we are at the top of this loop:
     - HERE is our current directory,
     - ID is the node revision ID of HERE,
     - REST is the path we're going to find in HERE, and
     - PARENT_PATH includes HERE and all its parents.  */ /* The NODE in PARENT_PATH always lives in POOL, i.e. it will
       * survive the cleanup of ITERPOOL and the DAG cache.*/ /* Parse out the next entry from the path.  */ /* Update the path traversed thus far. */ /* Given the behavior of svn_fs__next_entry_name(), ENTRY may be an
         empty string when the path either starts or ends with a slash.
         In either case, we stay put: the current directory stays the
         same, and we add nothing to the parent path.  We only need to
         process non-empty path segments. */ /* If we found a directory entry, follow it.  First, we
             check our node cache, and, failing that, we hit the DAG
             layer.  Don't bother to contact the cache for the last
             element if we already know the lookup to fail for the
             complete path. */ /* "file not found" requires special handling.  */ /* If this was the last path component, and the caller
                 said it was optional, then don't return an error;
                 just put a NULL node pointer in the path.  */ /* Build a better error message than svn_fs_x__dag_open
                     can provide, giving the root and full path name.  */ /* Shortcut: the caller only wants the final DAG node. */ /* Now, make a parent_path item for CHILD. */ /* Cache the node we found (if it wasn't already cached). */ /* Are we finished traversing the path?  */ /* The path isn't finished yet; we'd better be in a directory.  */