| /* |
| * 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; |
| } |