blob: 886ac9e8fad7d01df3891cde176561f9c4113ee4 [file] [log] [blame]
/*
* entries-dump.c : dump pre-1.6 svn_wc_* output for python
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <apr_pools.h>
#include <apr_general.h>
#define SVN_DEPRECATED
#include "svn_types.h"
#include "svn_cmdline.h"
#include "svn_pools.h"
#include "svn_wc.h"
#include "svn_dirent_uri.h"
#include "svn_xml.h"
#include "private/svn_wc_private.h"
#include "../../libsvn_wc/wc.h"
#include "../../libsvn_wc/lock.h"
static void
print_prefix(void)
{
puts("if b'' == '':\n"
" def _to_str(s):\n"
" return s\n"
"else:\n"
" def _to_str(s):\n"
" return s.decode('utf-8', 'surrogateescape')\n"
"\n"
"class Entry(object):\n"
" \"\"\"An Entry object represents one node's entry in a pre-1.6"
" .svn/entries file.\n\n"
"Similar to #svn_wc_entry_t, but not all fields are populated.\n\n"
"Entry objects are generated by the 'entries-dump'"
" test helper tool.\"\"\"\n\n"
" if b'' == '':\n"
" def set_strval(self, name, val):\n"
" self.__setattr__(name, val)\n"
" else:\n"
" def set_strval(self, name, val):\n"
" global _to_str\n"
" self.__setattr__(name, _to_str(val))\n");
}
static void
print_as_bytes(const char *val)
{
printf("b'");
while(*val)
{
printf("\\x%02x", (unsigned int)(unsigned char)*val++);
}
printf("'");
}
static void
str_value(const char *name, const char *value, apr_pool_t *pool)
{
if (value == NULL)
printf("e.%s = None\n", name);
else
{
svn_stringbuf_t *escaped_value = NULL;
svn_xml_escape_attr_cstring(&escaped_value, value, pool);
/* Print the human-readable value. */
assert(NULL == strchr(escaped_value->data, '\n'));
printf("# e.%s = '%s'\n", name, escaped_value->data);
/* Print the machine-readable value. */
printf("e.set_strval('%s', ", name);
print_as_bytes(value);
printf(")\n");
}
}
static void
int_value(const char *name, long int value)
{
printf("e.%s = %ld\n", name, value);
}
static void
bool_value(const char *name, svn_boolean_t value)
{
if (value)
printf("e.%s = True\n", name);
else
printf("e.%s = False\n", name);
}
static svn_error_t *
entries_dump(const char *dir_path, svn_wc_adm_access_t *related, apr_pool_t *pool)
{
svn_wc_adm_access_t *adm_access = NULL;
apr_hash_t *entries;
apr_hash_index_t *hi;
svn_boolean_t locked;
svn_error_t *err;
svn_wc_context_t *wc_ctx = NULL;
const char *dir_abspath;
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_dirent_get_absolute(&dir_abspath, dir_path, pool));
err = svn_wc_adm_open3(&adm_access, related, dir_path, FALSE, 0,
NULL, NULL, pool);
if (!err)
{
SVN_ERR(svn_wc__context_create_with_db(&wc_ctx, NULL,
svn_wc__adm_get_db(adm_access),
pool));
SVN_ERR(svn_wc_locked2(NULL, &locked, wc_ctx, dir_abspath, pool));
SVN_ERR(svn_wc_entries_read(&entries, adm_access, TRUE, pool));
}
else if (err && err->apr_err == SVN_ERR_WC_LOCKED
&& related
&& ! strcmp(dir_path, svn_wc_adm_access_path(related)))
{
/* Common caller error: Can't open a baton when there is one. */
svn_error_clear(err);
SVN_ERR(svn_wc__context_create_with_db(&wc_ctx, NULL,
svn_wc__adm_get_db(related),
pool));
SVN_ERR(svn_wc_locked2(NULL, &locked, wc_ctx, dir_abspath, pool));
SVN_ERR(svn_wc_entries_read(&entries, related, TRUE, pool));
}
else
{
const char *lockfile_path;
svn_node_kind_t kind;
/* ### Should svn_wc_adm_open3 be returning UPGRADE_REQUIRED? */
if (err->apr_err != SVN_ERR_WC_NOT_DIRECTORY)
return err;
svn_error_clear(err);
adm_access = NULL;
SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath, pool, pool));
lockfile_path = svn_dirent_join_many(pool, dir_path,
svn_wc_get_adm_dir(pool),
"lock", SVN_VA_NULL);
SVN_ERR(svn_io_check_path(lockfile_path, &kind, pool));
locked = (kind == svn_node_file);
}
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
svn_stringbuf_t *escaped_key;
const char *key = apr_hash_this_key(hi);
const svn_wc_entry_t *entry = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
SVN_ERR_ASSERT(strcmp(key, entry->name) == 0);
printf("e = Entry()\n");
str_value("name", entry->name, iterpool);
int_value("revision", entry->revision);
str_value("url", entry->url, iterpool);
str_value("repos", entry->repos, iterpool);
str_value("uuid", entry->uuid, iterpool);
int_value("kind", entry->kind);
int_value("schedule", entry->schedule);
bool_value("copied", entry->copied);
bool_value("deleted", entry->deleted);
bool_value("absent", entry->absent);
bool_value("incomplete", entry->incomplete);
str_value("copyfrom_url", entry->copyfrom_url, iterpool);
int_value("copyfrom_rev", entry->copyfrom_rev);
str_value("conflict_old", entry->conflict_old, iterpool);
str_value("conflict_new", entry->conflict_new, iterpool);
str_value("conflict_wrk", entry->conflict_wrk, iterpool);
str_value("prejfile", entry->prejfile, iterpool);
/* skip: text_time */
/* skip: prop_time */
/* skip: checksum */
int_value("cmt_rev", entry->cmt_rev);
/* skip: cmt_date */
str_value("cmt_author", entry->cmt_author, iterpool);
str_value("lock_token", entry->lock_token, iterpool);
str_value("lock_owner", entry->lock_owner, iterpool);
str_value("lock_comment", entry->lock_comment, iterpool);
/* skip: lock_creation_date */
/* skip: has_props */
/* skip: has_prop_mods */
/* skip: cachable_props */
/* skip: present_props */
str_value("changelist", entry->changelist, iterpool);
/* skip: working_size */
/* skip: keep_local */
int_value("depth", entry->depth);
/* skip: tree_conflict_data */
bool_value("file_external", entry->file_external_path != NULL);
/* skip: file_external_peg_rev */
/* skip: file_external_rev */
bool_value("locked", locked && *entry->name == '\0');
/* Print the human-readable value. */
escaped_key = NULL;
svn_xml_escape_attr_cstring(&escaped_key, key, iterpool);
assert(NULL == strchr(escaped_key->data, '\n'));
printf("# entries['%s'] = e\n", escaped_key->data);
/* Print the machine-readable value. */
printf("entries[_to_str(");
print_as_bytes(key);
printf(")] = e\n");
}
svn_pool_destroy(iterpool);
if (wc_ctx)
SVN_ERR(svn_wc_context_destroy(wc_ctx));
if (adm_access)
SVN_ERR(svn_wc_adm_close2(adm_access, pool));
return SVN_NO_ERROR;
}
/* baton for print_dir */
struct directory_walk_baton
{
svn_wc_context_t *wc_ctx;
const char *root_abspath;
const char *prefix_path;
svn_wc_adm_access_t *adm_access;
};
/* svn_wc__node_found_func_t implementation for directory_dump */
static svn_error_t *
print_dir(const char *local_abspath,
svn_node_kind_t kind,
void *walk_baton,
apr_pool_t *scratch_pool)
{
struct directory_walk_baton *bt = walk_baton;
const char *path;
if (kind != svn_node_dir)
return SVN_NO_ERROR;
/* If LOCAL_ABSPATH a child of or equal to ROOT_ABSPATH, then display
a relative path starting with PREFIX_PATH. */
path = svn_dirent_skip_ancestor(bt->root_abspath, local_abspath);
if (path)
path = svn_dirent_join(bt->prefix_path, path, scratch_pool);
else
path = local_abspath;
printf("%s\n", svn_dirent_local_style(path, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
directory_dump_old(struct directory_walk_baton *bt,
const char *dir_abspath,
apr_pool_t *scratch_pool)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
scratch_pool, scratch_pool));
for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi))
{
const svn_wc_entry_t *entry = apr_hash_this_val(hi);
const char *local_abspath;
if (entry->deleted || entry->absent || entry->kind != svn_node_dir)
continue;
local_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
SVN_ERR(directory_dump_old(bt, local_abspath, scratch_pool));
else
SVN_ERR(print_dir(local_abspath, entry->kind, bt, scratch_pool));
}
return SVN_NO_ERROR;
}
/* Print all not-hidden subdirectories in the working copy, starting by path */
static svn_error_t *
directory_dump(const char *path,
apr_pool_t *scratch_pool)
{
struct directory_walk_baton bt;
svn_error_t *err;
SVN_ERR(svn_wc_context_create(&bt.wc_ctx, NULL, scratch_pool, scratch_pool));
SVN_ERR(svn_dirent_get_absolute(&bt.root_abspath, path, scratch_pool));
bt.prefix_path = path;
err = svn_wc__internal_walk_children(bt.wc_ctx->db, bt.root_abspath, FALSE,
NULL, print_dir, &bt, svn_depth_infinity,
NULL, NULL, scratch_pool);
if (err)
{
const char *dir_abspath;
if (err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
return err;
svn_error_clear(err);
SVN_ERR(svn_dirent_get_absolute(&dir_abspath, path, scratch_pool));
SVN_ERR(directory_dump_old(&bt, dir_abspath, scratch_pool));
}
return svn_error_trace(svn_wc_context_destroy(bt.wc_ctx));
}
static svn_error_t *
tree_dump_dir(const char *local_abspath,
svn_node_kind_t kind,
void *walk_baton,
apr_pool_t *scratch_pool)
{
struct directory_walk_baton *bt = walk_baton;
const char *path;
svn_stringbuf_t *escaped_path;
if (kind != svn_node_dir)
return SVN_NO_ERROR;
if (strcmp(local_abspath, bt->root_abspath) != 0)
{
svn_boolean_t is_wcroot;
SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, bt->wc_ctx->db,
local_abspath, scratch_pool));
if (is_wcroot)
return SVN_NO_ERROR; /* Report the stub, but not the data */
}
/* If LOCAL_ABSPATH a child of or equal to ROOT_ABSPATH, then display
a relative path starting with PREFIX_PATH. */
path = svn_dirent_skip_ancestor(bt->root_abspath, local_abspath);
if (path)
path = svn_dirent_join(bt->prefix_path, path, scratch_pool);
else
path = local_abspath;
printf("entries = {}\n");
SVN_ERR(entries_dump(path, bt->adm_access, scratch_pool));
/* Print the human-readable value. */
escaped_path = NULL;
svn_xml_escape_attr_cstring(&escaped_path, path, scratch_pool);
assert(NULL == strchr(escaped_path->data, '\n'));
printf("# dirs['%s'] = entries\n", escaped_path->data);
/* Print the machine-readable value. */
printf("dirs[_to_str(");
print_as_bytes(path);
printf(")] = entries\n");
return SVN_NO_ERROR;
}
static svn_error_t *
tree_dump(const char *path,
apr_pool_t *scratch_pool)
{
struct directory_walk_baton bt;
svn_sqlite__db_t *sdb;
svn_wc__db_t *db;
bt.prefix_path = path;
/* Obtain an access baton to allow re-using the same wc_db for all access */
SVN_ERR(svn_wc_adm_open3(&bt.adm_access, NULL, path, FALSE, 0, NULL, NULL,
scratch_pool));
db = svn_wc__adm_get_db(bt.adm_access);
SVN_ERR(svn_wc__context_create_with_db(&bt.wc_ctx, NULL, db, scratch_pool));
SVN_ERR(svn_dirent_get_absolute(&bt.root_abspath, path, scratch_pool));
/* And now get us a transaction on the database to avoid obtaining and
releasing locks all the time */
SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, bt.wc_ctx->db, bt.root_abspath,
scratch_pool));
SVN_SQLITE__WITH_LOCK(
svn_wc__internal_walk_children(db, bt.root_abspath, FALSE,
NULL, tree_dump_dir, &bt,
svn_depth_infinity,
NULL, NULL, scratch_pool),
sdb);
/* And close everything we've opened */
SVN_ERR(svn_wc_context_destroy(bt.wc_ctx));
SVN_ERR(svn_wc_adm_close2(bt.adm_access, scratch_pool));
return SVN_NO_ERROR;
}
int
main(int argc, const char *argv[])
{
apr_pool_t *pool;
int exit_code = EXIT_SUCCESS;
svn_error_t *err;
const char *path;
const char *cmd;
if (argc < 2 || argc > 4)
{
fprintf(stderr, "USAGE: entries-dump [--entries|--subdirs|--tree-dump] DIR_PATH\n");
exit(1);
}
if (svn_cmdline_init("entries-dump", stderr) != EXIT_SUCCESS)
{
return EXIT_FAILURE;
}
/* Create our top-level pool. Use a separate mutexless allocator,
* given this application is single threaded.
*/
pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
path = svn_dirent_internal_style(argv[argc-1], pool);
if (argc > 2)
cmd = argv[1];
else
cmd = NULL;
if (!cmd || !strcmp(cmd, "--entries"))
{
print_prefix();
err = entries_dump(path, NULL, pool);
}
else if (!strcmp(cmd, "--subdirs"))
{
err = directory_dump(path, pool);
}
else if (!strcmp(cmd, "--tree-dump"))
{
print_prefix();
err = tree_dump(path, pool);
}
else
{
err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
"Invalid command '%s'",
cmd);
}
if (err)
{
svn_handle_error2(err, stderr, FALSE, "entries-dump: ");
svn_error_clear(err);
exit_code = EXIT_FAILURE;
}
/* Clean up, and get outta here */
svn_pool_destroy(pool);
apr_terminate();
return exit_code;
}