1251881Speter/* 2251881Speter * svnlook.c: Subversion server inspection tool main file. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter#include <assert.h> 25251881Speter#include <stdlib.h> 26251881Speter 27251881Speter#include <apr_general.h> 28251881Speter#include <apr_pools.h> 29251881Speter#include <apr_time.h> 30251881Speter#include <apr_file_io.h> 31251881Speter#include <apr_signal.h> 32251881Speter 33251881Speter#define APR_WANT_STDIO 34251881Speter#define APR_WANT_STRFUNC 35251881Speter#include <apr_want.h> 36251881Speter 37251881Speter#include "svn_hash.h" 38251881Speter#include "svn_cmdline.h" 39251881Speter#include "svn_types.h" 40251881Speter#include "svn_pools.h" 41251881Speter#include "svn_error.h" 42251881Speter#include "svn_error_codes.h" 43251881Speter#include "svn_dirent_uri.h" 44251881Speter#include "svn_path.h" 45251881Speter#include "svn_repos.h" 46251881Speter#include "svn_fs.h" 47251881Speter#include "svn_time.h" 48251881Speter#include "svn_utf.h" 49251881Speter#include "svn_subst.h" 50251881Speter#include "svn_sorts.h" 51251881Speter#include "svn_opt.h" 52251881Speter#include "svn_props.h" 53251881Speter#include "svn_diff.h" 54251881Speter#include "svn_version.h" 55251881Speter#include "svn_xml.h" 56251881Speter 57251881Speter#include "private/svn_diff_private.h" 58251881Speter#include "private/svn_cmdline_private.h" 59251881Speter#include "private/svn_fspath.h" 60253734Speter#include "private/svn_io_private.h" 61251881Speter 62251881Speter#include "svn_private_config.h" 63251881Speter 64251881Speter 65251881Speter/*** Some convenience macros and types. ***/ 66251881Speter 67251881Speter 68251881Speter/* Option handling. */ 69251881Speter 70251881Speterstatic svn_opt_subcommand_t 71251881Speter subcommand_author, 72251881Speter subcommand_cat, 73251881Speter subcommand_changed, 74251881Speter subcommand_date, 75251881Speter subcommand_diff, 76251881Speter subcommand_dirschanged, 77251881Speter subcommand_filesize, 78251881Speter subcommand_help, 79251881Speter subcommand_history, 80251881Speter subcommand_info, 81251881Speter subcommand_lock, 82251881Speter subcommand_log, 83251881Speter subcommand_pget, 84251881Speter subcommand_plist, 85251881Speter subcommand_tree, 86251881Speter subcommand_uuid, 87251881Speter subcommand_youngest; 88251881Speter 89251881Speter/* Option codes and descriptions. */ 90251881Speterenum 91251881Speter { 92251881Speter svnlook__version = SVN_OPT_FIRST_LONGOPT_ID, 93251881Speter svnlook__show_ids, 94251881Speter svnlook__no_diff_deleted, 95251881Speter svnlook__no_diff_added, 96251881Speter svnlook__diff_copy_from, 97251881Speter svnlook__revprop_opt, 98251881Speter svnlook__full_paths, 99251881Speter svnlook__copy_info, 100251881Speter svnlook__xml_opt, 101251881Speter svnlook__ignore_properties, 102251881Speter svnlook__properties_only, 103251881Speter svnlook__diff_cmd, 104251881Speter svnlook__show_inherited_props 105251881Speter }; 106251881Speter 107251881Speter/* 108251881Speter * The entire list must be terminated with an entry of nulls. 109251881Speter */ 110251881Speterstatic const apr_getopt_option_t options_table[] = 111251881Speter{ 112251881Speter {NULL, '?', 0, 113251881Speter N_("show help on a subcommand")}, 114251881Speter 115251881Speter {"copy-info", svnlook__copy_info, 0, 116251881Speter N_("show details for copies")}, 117251881Speter 118251881Speter {"diff-copy-from", svnlook__diff_copy_from, 0, 119251881Speter N_("print differences against the copy source")}, 120251881Speter 121251881Speter {"full-paths", svnlook__full_paths, 0, 122251881Speter N_("show full paths instead of indenting them")}, 123251881Speter 124251881Speter {"help", 'h', 0, 125251881Speter N_("show help on a subcommand")}, 126251881Speter 127251881Speter {"limit", 'l', 1, 128251881Speter N_("maximum number of history entries")}, 129251881Speter 130251881Speter {"no-diff-added", svnlook__no_diff_added, 0, 131251881Speter N_("do not print differences for added files")}, 132251881Speter 133251881Speter {"no-diff-deleted", svnlook__no_diff_deleted, 0, 134251881Speter N_("do not print differences for deleted files")}, 135251881Speter 136251881Speter {"diff-cmd", svnlook__diff_cmd, 1, 137251881Speter N_("use ARG as diff command")}, 138251881Speter 139251881Speter {"ignore-properties", svnlook__ignore_properties, 0, 140251881Speter N_("ignore properties during the operation")}, 141251881Speter 142251881Speter {"properties-only", svnlook__properties_only, 0, 143251881Speter N_("show only properties during the operation")}, 144251881Speter 145251881Speter {"non-recursive", 'N', 0, 146251881Speter N_("operate on single directory only")}, 147251881Speter 148251881Speter {"revision", 'r', 1, 149251881Speter N_("specify revision number ARG")}, 150251881Speter 151251881Speter {"revprop", svnlook__revprop_opt, 0, 152251881Speter N_("operate on a revision property (use with -r or -t)")}, 153251881Speter 154251881Speter {"show-ids", svnlook__show_ids, 0, 155251881Speter N_("show node revision ids for each path")}, 156251881Speter 157251881Speter {"show-inherited-props", svnlook__show_inherited_props, 0, 158251881Speter N_("show path's inherited properties")}, 159251881Speter 160251881Speter {"transaction", 't', 1, 161251881Speter N_("specify transaction name ARG")}, 162251881Speter 163251881Speter {"verbose", 'v', 0, 164251881Speter N_("be verbose")}, 165251881Speter 166251881Speter {"version", svnlook__version, 0, 167251881Speter N_("show program version information")}, 168251881Speter 169251881Speter {"xml", svnlook__xml_opt, 0, 170251881Speter N_("output in XML")}, 171251881Speter 172251881Speter {"extensions", 'x', 1, 173251881Speter N_("Specify differencing options for external diff or\n" 174251881Speter " " 175251881Speter "internal diff. Default: '-u'. Options are\n" 176251881Speter " " 177251881Speter "separated by spaces. Internal diff takes:\n" 178251881Speter " " 179251881Speter " -u, --unified: Show 3 lines of unified context\n" 180251881Speter " " 181251881Speter " -b, --ignore-space-change: Ignore changes in\n" 182251881Speter " " 183251881Speter " amount of white space\n" 184251881Speter " " 185251881Speter " -w, --ignore-all-space: Ignore all white space\n" 186251881Speter " " 187251881Speter " --ignore-eol-style: Ignore changes in EOL style\n" 188251881Speter " " 189251881Speter " -p, --show-c-function: Show C function name")}, 190251881Speter 191251881Speter {"quiet", 'q', 0, 192251881Speter N_("no progress (only errors) to stderr")}, 193251881Speter 194251881Speter {0, 0, 0, 0} 195251881Speter}; 196251881Speter 197251881Speter 198251881Speter/* Array of available subcommands. 199251881Speter * The entire list must be terminated with an entry of nulls. 200251881Speter */ 201251881Speterstatic const svn_opt_subcommand_desc2_t cmd_table[] = 202251881Speter{ 203251881Speter {"author", subcommand_author, {0}, 204251881Speter N_("usage: svnlook author REPOS_PATH\n\n" 205251881Speter "Print the author.\n"), 206251881Speter {'r', 't'} }, 207251881Speter 208251881Speter {"cat", subcommand_cat, {0}, 209251881Speter N_("usage: svnlook cat REPOS_PATH FILE_PATH\n\n" 210251881Speter "Print the contents of a file. Leading '/' on FILE_PATH is optional.\n"), 211251881Speter {'r', 't'} }, 212251881Speter 213251881Speter {"changed", subcommand_changed, {0}, 214251881Speter N_("usage: svnlook changed REPOS_PATH\n\n" 215251881Speter "Print the paths that were changed.\n"), 216251881Speter {'r', 't', svnlook__copy_info} }, 217251881Speter 218251881Speter {"date", subcommand_date, {0}, 219251881Speter N_("usage: svnlook date REPOS_PATH\n\n" 220251881Speter "Print the datestamp.\n"), 221251881Speter {'r', 't'} }, 222251881Speter 223251881Speter {"diff", subcommand_diff, {0}, 224251881Speter N_("usage: svnlook diff REPOS_PATH\n\n" 225251881Speter "Print GNU-style diffs of changed files and properties.\n"), 226251881Speter {'r', 't', svnlook__no_diff_deleted, svnlook__no_diff_added, 227251881Speter svnlook__diff_copy_from, svnlook__diff_cmd, 'x', 228251881Speter svnlook__ignore_properties, svnlook__properties_only} }, 229251881Speter 230251881Speter {"dirs-changed", subcommand_dirschanged, {0}, 231251881Speter N_("usage: svnlook dirs-changed REPOS_PATH\n\n" 232251881Speter "Print the directories that were themselves changed (property edits)\n" 233251881Speter "or whose file children were changed.\n"), 234251881Speter {'r', 't'} }, 235251881Speter 236251881Speter {"filesize", subcommand_filesize, {0}, 237251881Speter N_("usage: svnlook filesize REPOS_PATH PATH_IN_REPOS\n\n" 238251881Speter "Print the size (in bytes) of the file located at PATH_IN_REPOS as\n" 239251881Speter "it is represented in the repository.\n"), 240251881Speter {'r', 't'} }, 241251881Speter 242251881Speter {"help", subcommand_help, {"?", "h"}, 243251881Speter N_("usage: svnlook help [SUBCOMMAND...]\n\n" 244251881Speter "Describe the usage of this program or its subcommands.\n"), 245251881Speter {0} }, 246251881Speter 247251881Speter {"history", subcommand_history, {0}, 248251881Speter N_("usage: svnlook history REPOS_PATH [PATH_IN_REPOS]\n\n" 249251881Speter "Print information about the history of a path in the repository (or\n" 250251881Speter "the root directory if no path is supplied).\n"), 251251881Speter {'r', svnlook__show_ids, 'l'} }, 252251881Speter 253251881Speter {"info", subcommand_info, {0}, 254251881Speter N_("usage: svnlook info REPOS_PATH\n\n" 255251881Speter "Print the author, datestamp, log message size, and log message.\n"), 256251881Speter {'r', 't'} }, 257251881Speter 258251881Speter {"lock", subcommand_lock, {0}, 259251881Speter N_("usage: svnlook lock REPOS_PATH PATH_IN_REPOS\n\n" 260251881Speter "If a lock exists on a path in the repository, describe it.\n"), 261251881Speter {0} }, 262251881Speter 263251881Speter {"log", subcommand_log, {0}, 264251881Speter N_("usage: svnlook log REPOS_PATH\n\n" 265251881Speter "Print the log message.\n"), 266251881Speter {'r', 't'} }, 267251881Speter 268251881Speter {"propget", subcommand_pget, {"pget", "pg"}, 269251881Speter N_("usage: 1. svnlook propget REPOS_PATH PROPNAME PATH_IN_REPOS\n" 270251881Speter " " 271251881Speter /* The line above is actually needed, so do NOT delete it! */ 272251881Speter " 2. svnlook propget --revprop REPOS_PATH PROPNAME\n\n" 273251881Speter "Print the raw value of a property on a path in the repository.\n" 274251881Speter "With --revprop, print the raw value of a revision property.\n"), 275251881Speter {'r', 't', 'v', svnlook__revprop_opt, svnlook__show_inherited_props} }, 276251881Speter 277251881Speter {"proplist", subcommand_plist, {"plist", "pl"}, 278251881Speter N_("usage: 1. svnlook proplist REPOS_PATH PATH_IN_REPOS\n" 279251881Speter " " 280251881Speter /* The line above is actually needed, so do NOT delete it! */ 281251881Speter " 2. svnlook proplist --revprop REPOS_PATH\n\n" 282251881Speter "List the properties of a path in the repository, or\n" 283251881Speter "with the --revprop option, revision properties.\n" 284251881Speter "With -v, show the property values too.\n"), 285251881Speter {'r', 't', 'v', svnlook__revprop_opt, svnlook__xml_opt, 286251881Speter svnlook__show_inherited_props} }, 287251881Speter 288251881Speter {"tree", subcommand_tree, {0}, 289251881Speter N_("usage: svnlook tree REPOS_PATH [PATH_IN_REPOS]\n\n" 290251881Speter "Print the tree, starting at PATH_IN_REPOS (if supplied, at the root\n" 291251881Speter "of the tree otherwise), optionally showing node revision ids.\n"), 292251881Speter {'r', 't', 'N', svnlook__show_ids, svnlook__full_paths} }, 293251881Speter 294251881Speter {"uuid", subcommand_uuid, {0}, 295251881Speter N_("usage: svnlook uuid REPOS_PATH\n\n" 296251881Speter "Print the repository's UUID.\n"), 297251881Speter {0} }, 298251881Speter 299251881Speter {"youngest", subcommand_youngest, {0}, 300251881Speter N_("usage: svnlook youngest REPOS_PATH\n\n" 301251881Speter "Print the youngest revision number.\n"), 302251881Speter {0} }, 303251881Speter 304251881Speter { NULL, NULL, {0}, NULL, {0} } 305251881Speter}; 306251881Speter 307251881Speter 308251881Speter/* Baton for passing option/argument state to a subcommand function. */ 309251881Speterstruct svnlook_opt_state 310251881Speter{ 311251881Speter const char *repos_path; /* 'arg0' is always the path to the repository. */ 312251881Speter const char *arg1; /* Usually an fs path, a propname, or NULL. */ 313251881Speter const char *arg2; /* Usually an fs path or NULL. */ 314251881Speter svn_revnum_t rev; 315251881Speter const char *txn; 316251881Speter svn_boolean_t version; /* --version */ 317251881Speter svn_boolean_t show_ids; /* --show-ids */ 318251881Speter apr_size_t limit; /* --limit */ 319251881Speter svn_boolean_t help; /* --help */ 320251881Speter svn_boolean_t no_diff_deleted; /* --no-diff-deleted */ 321251881Speter svn_boolean_t no_diff_added; /* --no-diff-added */ 322251881Speter svn_boolean_t diff_copy_from; /* --diff-copy-from */ 323251881Speter svn_boolean_t verbose; /* --verbose */ 324251881Speter svn_boolean_t revprop; /* --revprop */ 325251881Speter svn_boolean_t full_paths; /* --full-paths */ 326251881Speter svn_boolean_t copy_info; /* --copy-info */ 327251881Speter svn_boolean_t non_recursive; /* --non-recursive */ 328251881Speter svn_boolean_t xml; /* --xml */ 329251881Speter const char *extensions; /* diff extension args (UTF-8!) */ 330251881Speter svn_boolean_t quiet; /* --quiet */ 331251881Speter svn_boolean_t ignore_properties; /* --ignore_properties */ 332251881Speter svn_boolean_t properties_only; /* --properties-only */ 333251881Speter const char *diff_cmd; /* --diff-cmd */ 334251881Speter svn_boolean_t show_inherited_props; /* --show-inherited-props */ 335251881Speter}; 336251881Speter 337251881Speter 338251881Spetertypedef struct svnlook_ctxt_t 339251881Speter{ 340251881Speter svn_repos_t *repos; 341251881Speter svn_fs_t *fs; 342251881Speter svn_boolean_t is_revision; 343251881Speter svn_boolean_t show_ids; 344251881Speter apr_size_t limit; 345251881Speter svn_boolean_t no_diff_deleted; 346251881Speter svn_boolean_t no_diff_added; 347251881Speter svn_boolean_t diff_copy_from; 348251881Speter svn_boolean_t full_paths; 349251881Speter svn_boolean_t copy_info; 350251881Speter svn_revnum_t rev_id; 351251881Speter svn_fs_txn_t *txn; 352251881Speter const char *txn_name /* UTF-8! */; 353251881Speter const apr_array_header_t *diff_options; 354251881Speter svn_boolean_t ignore_properties; 355251881Speter svn_boolean_t properties_only; 356251881Speter const char *diff_cmd; 357251881Speter 358251881Speter} svnlook_ctxt_t; 359251881Speter 360251881Speter/* A flag to see if we've been cancelled by the client or not. */ 361251881Speterstatic volatile sig_atomic_t cancelled = FALSE; 362251881Speter 363251881Speter 364251881Speter/*** Helper functions. ***/ 365251881Speter 366251881Speter/* A signal handler to support cancellation. */ 367251881Speterstatic void 368251881Spetersignal_handler(int signum) 369251881Speter{ 370251881Speter apr_signal(signum, SIG_IGN); 371251881Speter cancelled = TRUE; 372251881Speter} 373251881Speter 374251881Speter/* Our cancellation callback. */ 375251881Speterstatic svn_error_t * 376251881Spetercheck_cancel(void *baton) 377251881Speter{ 378251881Speter if (cancelled) 379251881Speter return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); 380251881Speter else 381251881Speter return SVN_NO_ERROR; 382251881Speter} 383251881Speter 384251881Speter 385251881Speter/* Version compatibility check */ 386251881Speterstatic svn_error_t * 387251881Spetercheck_lib_versions(void) 388251881Speter{ 389251881Speter static const svn_version_checklist_t checklist[] = 390251881Speter { 391251881Speter { "svn_subr", svn_subr_version }, 392251881Speter { "svn_repos", svn_repos_version }, 393251881Speter { "svn_fs", svn_fs_version }, 394251881Speter { "svn_delta", svn_delta_version }, 395251881Speter { "svn_diff", svn_diff_version }, 396251881Speter { NULL, NULL } 397251881Speter }; 398251881Speter SVN_VERSION_DEFINE(my_version); 399251881Speter 400251881Speter return svn_ver_check_list(&my_version, checklist); 401251881Speter} 402251881Speter 403251881Speter 404251881Speter/* Get revision or transaction property PROP_NAME for the revision or 405251881Speter transaction specified in C, allocating in in POOL and placing it in 406251881Speter *PROP_VALUE. */ 407251881Speterstatic svn_error_t * 408251881Speterget_property(svn_string_t **prop_value, 409251881Speter svnlook_ctxt_t *c, 410251881Speter const char *prop_name, 411251881Speter apr_pool_t *pool) 412251881Speter{ 413251881Speter svn_string_t *raw_value; 414251881Speter 415251881Speter /* Fetch transaction property... */ 416251881Speter if (! c->is_revision) 417251881Speter SVN_ERR(svn_fs_txn_prop(&raw_value, c->txn, prop_name, pool)); 418251881Speter 419251881Speter /* ...or revision property -- it's your call. */ 420251881Speter else 421251881Speter SVN_ERR(svn_fs_revision_prop(&raw_value, c->fs, c->rev_id, 422251881Speter prop_name, pool)); 423251881Speter 424251881Speter *prop_value = raw_value; 425251881Speter 426251881Speter return SVN_NO_ERROR; 427251881Speter} 428251881Speter 429251881Speter 430251881Speterstatic svn_error_t * 431251881Speterget_root(svn_fs_root_t **root, 432251881Speter svnlook_ctxt_t *c, 433251881Speter apr_pool_t *pool) 434251881Speter{ 435251881Speter /* Open up the appropriate root (revision or transaction). */ 436251881Speter if (c->is_revision) 437251881Speter { 438251881Speter /* If we didn't get a valid revision number, we'll look at the 439251881Speter youngest revision. */ 440251881Speter if (! SVN_IS_VALID_REVNUM(c->rev_id)) 441251881Speter SVN_ERR(svn_fs_youngest_rev(&(c->rev_id), c->fs, pool)); 442251881Speter 443251881Speter SVN_ERR(svn_fs_revision_root(root, c->fs, c->rev_id, pool)); 444251881Speter } 445251881Speter else 446251881Speter { 447251881Speter SVN_ERR(svn_fs_txn_root(root, c->txn, pool)); 448251881Speter } 449251881Speter 450251881Speter return SVN_NO_ERROR; 451251881Speter} 452251881Speter 453251881Speter 454251881Speter 455251881Speter/*** Tree Routines ***/ 456251881Speter 457251881Speter/* Generate a generic delta tree. */ 458251881Speterstatic svn_error_t * 459251881Spetergenerate_delta_tree(svn_repos_node_t **tree, 460251881Speter svn_repos_t *repos, 461251881Speter svn_fs_root_t *root, 462251881Speter svn_revnum_t base_rev, 463251881Speter apr_pool_t *pool) 464251881Speter{ 465251881Speter svn_fs_root_t *base_root; 466251881Speter const svn_delta_editor_t *editor; 467251881Speter void *edit_baton; 468251881Speter apr_pool_t *edit_pool = svn_pool_create(pool); 469251881Speter svn_fs_t *fs = svn_repos_fs(repos); 470251881Speter 471251881Speter /* Get the base root. */ 472251881Speter SVN_ERR(svn_fs_revision_root(&base_root, fs, base_rev, pool)); 473251881Speter 474251881Speter /* Request our editor. */ 475251881Speter SVN_ERR(svn_repos_node_editor(&editor, &edit_baton, repos, 476251881Speter base_root, root, pool, edit_pool)); 477251881Speter 478251881Speter /* Drive our editor. */ 479251881Speter SVN_ERR(svn_repos_replay2(root, "", SVN_INVALID_REVNUM, TRUE, 480251881Speter editor, edit_baton, NULL, NULL, edit_pool)); 481251881Speter 482251881Speter /* Return the tree we just built. */ 483251881Speter *tree = svn_repos_node_from_baton(edit_baton); 484251881Speter svn_pool_destroy(edit_pool); 485251881Speter return SVN_NO_ERROR; 486251881Speter} 487251881Speter 488251881Speter 489251881Speter 490251881Speter/*** Tree Printing Routines ***/ 491251881Speter 492251881Speter/* Recursively print only directory nodes that either a) have property 493251881Speter mods, or b) contains files that have changed, or c) has added or deleted 494251881Speter children. NODE is the root node of the tree delta, so every node in it 495251881Speter is either changed or is a directory with a changed node somewhere in the 496251881Speter subtree below it. 497251881Speter */ 498251881Speterstatic svn_error_t * 499251881Speterprint_dirs_changed_tree(svn_repos_node_t *node, 500251881Speter const char *path /* UTF-8! */, 501251881Speter apr_pool_t *pool) 502251881Speter{ 503251881Speter svn_repos_node_t *tmp_node; 504251881Speter svn_boolean_t print_me = FALSE; 505251881Speter const char *full_path; 506251881Speter apr_pool_t *iterpool; 507251881Speter 508251881Speter SVN_ERR(check_cancel(NULL)); 509251881Speter 510251881Speter if (! node) 511251881Speter return SVN_NO_ERROR; 512251881Speter 513251881Speter /* Not a directory? We're not interested. */ 514251881Speter if (node->kind != svn_node_dir) 515251881Speter return SVN_NO_ERROR; 516251881Speter 517251881Speter /* Got prop mods? Excellent. */ 518251881Speter if (node->prop_mod) 519251881Speter print_me = TRUE; 520251881Speter 521251881Speter /* Fly through the list of children, checking for modified files. */ 522251881Speter tmp_node = node->child; 523251881Speter while (tmp_node && (! print_me)) 524251881Speter { 525251881Speter if ((tmp_node->kind == svn_node_file) 526251881Speter || (tmp_node->action == 'A') 527251881Speter || (tmp_node->action == 'D')) 528251881Speter { 529251881Speter print_me = TRUE; 530251881Speter } 531251881Speter tmp_node = tmp_node->sibling; 532251881Speter } 533251881Speter 534251881Speter /* Print the node if it qualifies. */ 535251881Speter if (print_me) 536251881Speter { 537251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s/\n", path)); 538251881Speter } 539251881Speter 540251881Speter /* Return here if the node has no children. */ 541251881Speter tmp_node = node->child; 542251881Speter if (! tmp_node) 543251881Speter return SVN_NO_ERROR; 544251881Speter 545251881Speter /* Recursively handle the node's children. */ 546251881Speter iterpool = svn_pool_create(pool); 547251881Speter while (tmp_node) 548251881Speter { 549251881Speter svn_pool_clear(iterpool); 550251881Speter full_path = svn_dirent_join(path, tmp_node->name, iterpool); 551251881Speter SVN_ERR(print_dirs_changed_tree(tmp_node, full_path, iterpool)); 552251881Speter tmp_node = tmp_node->sibling; 553251881Speter } 554251881Speter svn_pool_destroy(iterpool); 555251881Speter 556251881Speter return SVN_NO_ERROR; 557251881Speter} 558251881Speter 559251881Speter 560251881Speter/* Recursively print all nodes in the tree that have been modified 561251881Speter (do not include directories affected only by "bubble-up"). */ 562251881Speterstatic svn_error_t * 563251881Speterprint_changed_tree(svn_repos_node_t *node, 564251881Speter const char *path /* UTF-8! */, 565251881Speter svn_boolean_t copy_info, 566251881Speter apr_pool_t *pool) 567251881Speter{ 568251881Speter const char *full_path; 569251881Speter char status[4] = "_ "; 570251881Speter svn_boolean_t print_me = TRUE; 571251881Speter apr_pool_t *iterpool; 572251881Speter 573251881Speter SVN_ERR(check_cancel(NULL)); 574251881Speter 575251881Speter if (! node) 576251881Speter return SVN_NO_ERROR; 577251881Speter 578251881Speter /* Print the node. */ 579251881Speter if (node->action == 'A') 580251881Speter { 581251881Speter status[0] = 'A'; 582251881Speter if (copy_info && node->copyfrom_path) 583251881Speter status[2] = '+'; 584251881Speter } 585251881Speter else if (node->action == 'D') 586251881Speter status[0] = 'D'; 587251881Speter else if (node->action == 'R') 588251881Speter { 589251881Speter if ((! node->text_mod) && (! node->prop_mod)) 590251881Speter print_me = FALSE; 591251881Speter if (node->text_mod) 592251881Speter status[0] = 'U'; 593251881Speter if (node->prop_mod) 594251881Speter status[1] = 'U'; 595251881Speter } 596251881Speter else 597251881Speter print_me = FALSE; 598251881Speter 599251881Speter /* Print this node unless told to skip it. */ 600251881Speter if (print_me) 601251881Speter { 602251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s %s%s\n", 603251881Speter status, 604251881Speter path, 605251881Speter node->kind == svn_node_dir ? "/" : "")); 606251881Speter if (copy_info && node->copyfrom_path) 607251881Speter /* Remove the leading slash from the copyfrom path for consistency 608251881Speter with the rest of the output. */ 609251881Speter SVN_ERR(svn_cmdline_printf(pool, " (from %s%s:r%ld)\n", 610251881Speter (node->copyfrom_path[0] == '/' 611251881Speter ? node->copyfrom_path + 1 612251881Speter : node->copyfrom_path), 613251881Speter (node->kind == svn_node_dir ? "/" : ""), 614251881Speter node->copyfrom_rev)); 615251881Speter } 616251881Speter 617251881Speter /* Return here if the node has no children. */ 618251881Speter node = node->child; 619251881Speter if (! node) 620251881Speter return SVN_NO_ERROR; 621251881Speter 622251881Speter /* Recursively handle the node's children. */ 623251881Speter iterpool = svn_pool_create(pool); 624251881Speter while (node) 625251881Speter { 626251881Speter svn_pool_clear(iterpool); 627251881Speter full_path = svn_dirent_join(path, node->name, iterpool); 628251881Speter SVN_ERR(print_changed_tree(node, full_path, copy_info, iterpool)); 629251881Speter node = node->sibling; 630251881Speter } 631251881Speter svn_pool_destroy(iterpool); 632251881Speter 633251881Speter return SVN_NO_ERROR; 634251881Speter} 635251881Speter 636251881Speter 637251881Speterstatic svn_error_t * 638251881Speterdump_contents(svn_stream_t *stream, 639251881Speter svn_fs_root_t *root, 640251881Speter const char *path /* UTF-8! */, 641251881Speter apr_pool_t *pool) 642251881Speter{ 643251881Speter if (root == NULL) 644251881Speter SVN_ERR(svn_stream_close(stream)); /* leave an empty file */ 645251881Speter else 646251881Speter { 647251881Speter svn_stream_t *contents; 648251881Speter 649251881Speter /* Grab the contents and copy them into the given stream. */ 650251881Speter SVN_ERR(svn_fs_file_contents(&contents, root, path, pool)); 651251881Speter SVN_ERR(svn_stream_copy3(contents, stream, NULL, NULL, pool)); 652251881Speter } 653251881Speter 654251881Speter return SVN_NO_ERROR; 655251881Speter} 656251881Speter 657251881Speter 658251881Speter/* Prepare temporary files *TMPFILE1 and *TMPFILE2 for diffing 659251881Speter PATH1@ROOT1 versus PATH2@ROOT2. If either ROOT1 or ROOT2 is NULL, 660251881Speter the temporary file for its path/root will be an empty one. 661251881Speter Otherwise, its temporary file will contain the contents of that 662251881Speter path/root in the repository. 663251881Speter 664251881Speter An exception to this is when either path/root has an svn:mime-type 665251881Speter property set on it which indicates that the file contains 666251881Speter non-textual data -- in this case, the *IS_BINARY flag is set and no 667251881Speter temporary files are created. 668251881Speter 669251881Speter Use POOL for all that allocation goodness. */ 670251881Speterstatic svn_error_t * 671251881Speterprepare_tmpfiles(const char **tmpfile1, 672251881Speter const char **tmpfile2, 673251881Speter svn_boolean_t *is_binary, 674251881Speter svn_fs_root_t *root1, 675251881Speter const char *path1, 676251881Speter svn_fs_root_t *root2, 677251881Speter const char *path2, 678251881Speter const char *tmpdir, 679251881Speter apr_pool_t *pool) 680251881Speter{ 681251881Speter svn_string_t *mimetype; 682251881Speter svn_stream_t *stream; 683251881Speter 684251881Speter /* Init the return values. */ 685251881Speter *tmpfile1 = NULL; 686251881Speter *tmpfile2 = NULL; 687251881Speter *is_binary = FALSE; 688251881Speter 689251881Speter assert(path1 && path2); 690251881Speter 691251881Speter /* Check for binary mimetypes. If either file has a binary 692251881Speter mimetype, get outta here. */ 693251881Speter if (root1) 694251881Speter { 695251881Speter SVN_ERR(svn_fs_node_prop(&mimetype, root1, path1, 696251881Speter SVN_PROP_MIME_TYPE, pool)); 697251881Speter if (mimetype && svn_mime_type_is_binary(mimetype->data)) 698251881Speter { 699251881Speter *is_binary = TRUE; 700251881Speter return SVN_NO_ERROR; 701251881Speter } 702251881Speter } 703251881Speter if (root2) 704251881Speter { 705251881Speter SVN_ERR(svn_fs_node_prop(&mimetype, root2, path2, 706251881Speter SVN_PROP_MIME_TYPE, pool)); 707251881Speter if (mimetype && svn_mime_type_is_binary(mimetype->data)) 708251881Speter { 709251881Speter *is_binary = TRUE; 710251881Speter return SVN_NO_ERROR; 711251881Speter } 712251881Speter } 713251881Speter 714251881Speter /* Now, prepare the two temporary files, each of which will either 715251881Speter be empty, or will have real contents. */ 716251881Speter SVN_ERR(svn_stream_open_unique(&stream, tmpfile1, 717251881Speter tmpdir, 718251881Speter svn_io_file_del_none, 719251881Speter pool, pool)); 720251881Speter SVN_ERR(dump_contents(stream, root1, path1, pool)); 721251881Speter 722251881Speter SVN_ERR(svn_stream_open_unique(&stream, tmpfile2, 723251881Speter tmpdir, 724251881Speter svn_io_file_del_none, 725251881Speter pool, pool)); 726251881Speter SVN_ERR(dump_contents(stream, root2, path2, pool)); 727251881Speter 728251881Speter return SVN_NO_ERROR; 729251881Speter} 730251881Speter 731251881Speter 732251881Speter/* Generate a diff label for PATH in ROOT, allocating in POOL. 733251881Speter ROOT may be NULL, in which case revision 0 is used. */ 734251881Speterstatic svn_error_t * 735251881Spetergenerate_label(const char **label, 736251881Speter svn_fs_root_t *root, 737251881Speter const char *path, 738251881Speter apr_pool_t *pool) 739251881Speter{ 740251881Speter svn_string_t *date; 741251881Speter const char *datestr; 742251881Speter const char *name = NULL; 743251881Speter svn_revnum_t rev = SVN_INVALID_REVNUM; 744251881Speter 745251881Speter if (root) 746251881Speter { 747251881Speter svn_fs_t *fs = svn_fs_root_fs(root); 748251881Speter if (svn_fs_is_revision_root(root)) 749251881Speter { 750251881Speter rev = svn_fs_revision_root_revision(root); 751251881Speter SVN_ERR(svn_fs_revision_prop(&date, fs, rev, 752251881Speter SVN_PROP_REVISION_DATE, pool)); 753251881Speter } 754251881Speter else 755251881Speter { 756251881Speter svn_fs_txn_t *txn; 757251881Speter name = svn_fs_txn_root_name(root, pool); 758251881Speter SVN_ERR(svn_fs_open_txn(&txn, fs, name, pool)); 759251881Speter SVN_ERR(svn_fs_txn_prop(&date, txn, SVN_PROP_REVISION_DATE, pool)); 760251881Speter } 761251881Speter } 762251881Speter else 763251881Speter { 764251881Speter rev = 0; 765251881Speter date = NULL; 766251881Speter } 767251881Speter 768251881Speter if (date) 769251881Speter datestr = apr_psprintf(pool, "%.10s %.8s UTC", date->data, date->data + 11); 770251881Speter else 771251881Speter datestr = " "; 772251881Speter 773251881Speter if (name) 774251881Speter *label = apr_psprintf(pool, "%s\t%s (txn %s)", 775251881Speter path, datestr, name); 776251881Speter else 777251881Speter *label = apr_psprintf(pool, "%s\t%s (rev %ld)", 778251881Speter path, datestr, rev); 779251881Speter return SVN_NO_ERROR; 780251881Speter} 781251881Speter 782251881Speter 783251881Speter/* Helper function to display differences in properties of a file */ 784251881Speterstatic svn_error_t * 785251881Speterdisplay_prop_diffs(svn_stream_t *outstream, 786251881Speter const char *encoding, 787251881Speter const apr_array_header_t *propchanges, 788251881Speter apr_hash_t *original_props, 789251881Speter const char *path, 790251881Speter apr_pool_t *pool) 791251881Speter{ 792251881Speter 793251881Speter SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool, 794251881Speter _("%sProperty changes on: %s%s"), 795251881Speter APR_EOL_STR, 796251881Speter path, 797251881Speter APR_EOL_STR)); 798251881Speter 799251881Speter SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool, 800251881Speter SVN_DIFF__UNDER_STRING APR_EOL_STR)); 801251881Speter 802251881Speter SVN_ERR(check_cancel(NULL)); 803251881Speter 804251881Speter SVN_ERR(svn_diff__display_prop_diffs( 805251881Speter outstream, encoding, propchanges, original_props, 806251881Speter FALSE /* pretty_print_mergeinfo */, pool)); 807251881Speter 808251881Speter return SVN_NO_ERROR; 809251881Speter} 810251881Speter 811251881Speter 812251881Speter/* Recursively print all nodes in the tree that have been modified 813251881Speter (do not include directories affected only by "bubble-up"). */ 814251881Speterstatic svn_error_t * 815251881Speterprint_diff_tree(svn_stream_t *out_stream, 816251881Speter const char *encoding, 817251881Speter svn_fs_root_t *root, 818251881Speter svn_fs_root_t *base_root, 819251881Speter svn_repos_node_t *node, 820251881Speter const char *path /* UTF-8! */, 821251881Speter const char *base_path /* UTF-8! */, 822251881Speter const svnlook_ctxt_t *c, 823251881Speter const char *tmpdir, 824251881Speter apr_pool_t *pool) 825251881Speter{ 826251881Speter const char *orig_path = NULL, *new_path = NULL; 827251881Speter svn_boolean_t do_diff = FALSE; 828251881Speter svn_boolean_t orig_empty = FALSE; 829251881Speter svn_boolean_t is_copy = FALSE; 830251881Speter svn_boolean_t binary = FALSE; 831251881Speter svn_boolean_t diff_header_printed = FALSE; 832251881Speter apr_pool_t *subpool; 833251881Speter svn_stringbuf_t *header; 834251881Speter 835251881Speter SVN_ERR(check_cancel(NULL)); 836251881Speter 837251881Speter if (! node) 838251881Speter return SVN_NO_ERROR; 839251881Speter 840251881Speter header = svn_stringbuf_create_empty(pool); 841251881Speter 842251881Speter /* Print copyfrom history for the top node of a copied tree. */ 843251881Speter if ((SVN_IS_VALID_REVNUM(node->copyfrom_rev)) 844251881Speter && (node->copyfrom_path != NULL)) 845251881Speter { 846251881Speter /* This is ... a copy. */ 847251881Speter is_copy = TRUE; 848251881Speter 849251881Speter /* Propagate the new base. Copyfrom paths usually start with a 850251881Speter slash; we remove it for consistency with the target path. 851251881Speter ### Yes, it would be *much* better for something in the path 852251881Speter library to be taking care of this! */ 853251881Speter if (node->copyfrom_path[0] == '/') 854251881Speter base_path = apr_pstrdup(pool, node->copyfrom_path + 1); 855251881Speter else 856251881Speter base_path = apr_pstrdup(pool, node->copyfrom_path); 857251881Speter 858251881Speter svn_stringbuf_appendcstr 859251881Speter (header, 860251881Speter apr_psprintf(pool, _("Copied: %s (from rev %ld, %s)\n"), 861251881Speter path, node->copyfrom_rev, base_path)); 862251881Speter 863251881Speter SVN_ERR(svn_fs_revision_root(&base_root, 864251881Speter svn_fs_root_fs(base_root), 865251881Speter node->copyfrom_rev, pool)); 866251881Speter } 867251881Speter 868251881Speter /*** First, we'll just print file content diffs. ***/ 869251881Speter if (node->kind == svn_node_file) 870251881Speter { 871251881Speter /* Here's the generalized way we do our diffs: 872251881Speter 873251881Speter - First, we'll check for svn:mime-type properties on the old 874251881Speter and new files. If either has such a property, and it 875251881Speter represents a binary type, we won't actually be doing a real 876251881Speter diff. 877251881Speter 878251881Speter - Second, dump the contents of the new version of the file 879251881Speter into the temporary directory. 880251881Speter 881251881Speter - Then, dump the contents of the old version of the file into 882251881Speter the temporary directory. 883251881Speter 884251881Speter - Next, we run 'diff', passing the repository paths as the 885251881Speter labels. 886251881Speter 887251881Speter - Finally, we delete the temporary files. */ 888251881Speter if (node->action == 'R' && node->text_mod) 889251881Speter { 890251881Speter do_diff = TRUE; 891251881Speter SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, 892251881Speter base_root, base_path, root, path, 893251881Speter tmpdir, pool)); 894251881Speter } 895251881Speter else if (c->diff_copy_from && node->action == 'A' && is_copy) 896251881Speter { 897251881Speter if (node->text_mod) 898251881Speter { 899251881Speter do_diff = TRUE; 900251881Speter SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, 901251881Speter base_root, base_path, root, path, 902251881Speter tmpdir, pool)); 903251881Speter } 904251881Speter } 905251881Speter else if (! c->no_diff_added && node->action == 'A') 906251881Speter { 907251881Speter do_diff = TRUE; 908251881Speter orig_empty = TRUE; 909251881Speter SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, 910251881Speter NULL, base_path, root, path, 911251881Speter tmpdir, pool)); 912251881Speter } 913251881Speter else if (! c->no_diff_deleted && node->action == 'D') 914251881Speter { 915251881Speter do_diff = TRUE; 916251881Speter SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, 917251881Speter base_root, base_path, NULL, path, 918251881Speter tmpdir, pool)); 919251881Speter } 920251881Speter 921251881Speter /* The header for the copy case has already been created, and we don't 922251881Speter want a header here for files with only property modifications. */ 923251881Speter if (header->len == 0 924251881Speter && (node->action != 'R' || node->text_mod)) 925251881Speter { 926251881Speter svn_stringbuf_appendcstr 927251881Speter (header, apr_psprintf(pool, "%s: %s\n", 928251881Speter ((node->action == 'A') ? _("Added") : 929251881Speter ((node->action == 'D') ? _("Deleted") : 930251881Speter ((node->action == 'R') ? _("Modified") 931251881Speter : _("Index")))), 932251881Speter path)); 933251881Speter } 934251881Speter } 935251881Speter 936251881Speter if (do_diff && (! c->properties_only)) 937251881Speter { 938251881Speter svn_stringbuf_appendcstr(header, SVN_DIFF__EQUAL_STRING "\n"); 939251881Speter 940251881Speter if (binary) 941251881Speter { 942251881Speter svn_stringbuf_appendcstr(header, _("(Binary files differ)\n\n")); 943251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 944251881Speter "%s", header->data)); 945251881Speter } 946251881Speter else 947251881Speter { 948251881Speter if (c->diff_cmd) 949251881Speter { 950251881Speter apr_file_t *outfile; 951251881Speter apr_file_t *errfile; 952251881Speter const char *outfilename; 953251881Speter const char *errfilename; 954251881Speter svn_stream_t *stream; 955251881Speter svn_stream_t *err_stream; 956251881Speter const char **diff_cmd_argv; 957251881Speter int diff_cmd_argc; 958251881Speter int exitcode; 959251881Speter const char *orig_label; 960251881Speter const char *new_label; 961251881Speter 962251881Speter diff_cmd_argv = NULL; 963251881Speter diff_cmd_argc = c->diff_options->nelts; 964251881Speter if (diff_cmd_argc) 965251881Speter { 966251881Speter int i; 967251881Speter diff_cmd_argv = apr_palloc(pool, 968251881Speter diff_cmd_argc * sizeof(char *)); 969251881Speter for (i = 0; i < diff_cmd_argc; i++) 970251881Speter SVN_ERR(svn_utf_cstring_to_utf8(&diff_cmd_argv[i], 971251881Speter APR_ARRAY_IDX(c->diff_options, i, const char *), 972251881Speter pool)); 973251881Speter } 974251881Speter 975251881Speter /* Print diff header. */ 976251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 977251881Speter "%s", header->data)); 978251881Speter 979251881Speter if (orig_empty) 980251881Speter SVN_ERR(generate_label(&orig_label, NULL, path, pool)); 981251881Speter else 982251881Speter SVN_ERR(generate_label(&orig_label, base_root, 983251881Speter base_path, pool)); 984251881Speter SVN_ERR(generate_label(&new_label, root, path, pool)); 985251881Speter 986251881Speter /* We deal in streams, but svn_io_run_diff2() deals in file 987253734Speter handles, so we may need to make temporary files and then 988253734Speter copy the contents to our stream. */ 989253734Speter outfile = svn_stream__aprfile(out_stream); 990253734Speter if (outfile) 991253734Speter outfilename = NULL; 992253734Speter else 993253734Speter SVN_ERR(svn_io_open_unique_file3(&outfile, &outfilename, NULL, 994253734Speter svn_io_file_del_on_pool_cleanup, pool, pool)); 995253734Speter SVN_ERR(svn_stream_for_stderr(&err_stream, pool)); 996253734Speter errfile = svn_stream__aprfile(err_stream); 997253734Speter if (errfile) 998253734Speter errfilename = NULL; 999253734Speter else 1000253734Speter SVN_ERR(svn_io_open_unique_file3(&errfile, &errfilename, NULL, 1001253734Speter svn_io_file_del_on_pool_cleanup, pool, pool)); 1002251881Speter 1003251881Speter SVN_ERR(svn_io_run_diff2(".", 1004251881Speter diff_cmd_argv, 1005251881Speter diff_cmd_argc, 1006251881Speter orig_label, new_label, 1007251881Speter orig_path, new_path, 1008251881Speter &exitcode, outfile, errfile, 1009251881Speter c->diff_cmd, pool)); 1010251881Speter 1011251881Speter /* Now, open and copy our files to our output streams. */ 1012253734Speter if (outfilename) 1013253734Speter { 1014253734Speter SVN_ERR(svn_io_file_close(outfile, pool)); 1015253734Speter SVN_ERR(svn_stream_open_readonly(&stream, outfilename, 1016253734Speter pool, pool)); 1017253734Speter SVN_ERR(svn_stream_copy3(stream, 1018253734Speter svn_stream_disown(out_stream, pool), 1019253734Speter NULL, NULL, pool)); 1020253734Speter } 1021253734Speter if (errfilename) 1022253734Speter { 1023253734Speter SVN_ERR(svn_io_file_close(errfile, pool)); 1024253734Speter SVN_ERR(svn_stream_open_readonly(&stream, errfilename, 1025253734Speter pool, pool)); 1026253734Speter SVN_ERR(svn_stream_copy3(stream, 1027253734Speter svn_stream_disown(err_stream, pool), 1028253734Speter NULL, NULL, pool)); 1029253734Speter } 1030251881Speter 1031251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 1032251881Speter "\n")); 1033251881Speter diff_header_printed = TRUE; 1034251881Speter } 1035251881Speter else 1036251881Speter { 1037251881Speter svn_diff_t *diff; 1038251881Speter svn_diff_file_options_t *opts = svn_diff_file_options_create(pool); 1039251881Speter 1040251881Speter if (c->diff_options) 1041251881Speter SVN_ERR(svn_diff_file_options_parse(opts, c->diff_options, pool)); 1042251881Speter 1043251881Speter SVN_ERR(svn_diff_file_diff_2(&diff, orig_path, 1044251881Speter new_path, opts, pool)); 1045251881Speter 1046251881Speter if (svn_diff_contains_diffs(diff)) 1047251881Speter { 1048251881Speter const char *orig_label, *new_label; 1049251881Speter 1050251881Speter /* Print diff header. */ 1051251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 1052251881Speter "%s", header->data)); 1053251881Speter 1054251881Speter if (orig_empty) 1055251881Speter SVN_ERR(generate_label(&orig_label, NULL, path, pool)); 1056251881Speter else 1057251881Speter SVN_ERR(generate_label(&orig_label, base_root, 1058251881Speter base_path, pool)); 1059251881Speter SVN_ERR(generate_label(&new_label, root, path, pool)); 1060251881Speter SVN_ERR(svn_diff_file_output_unified3 1061251881Speter (out_stream, diff, orig_path, new_path, 1062251881Speter orig_label, new_label, 1063251881Speter svn_cmdline_output_encoding(pool), NULL, 1064251881Speter opts->show_c_function, pool)); 1065251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 1066251881Speter "\n")); 1067251881Speter diff_header_printed = TRUE; 1068251881Speter } 1069251881Speter else if (! node->prop_mod && 1070251881Speter ((! c->no_diff_added && node->action == 'A') || 1071251881Speter (! c->no_diff_deleted && node->action == 'D'))) 1072251881Speter { 1073251881Speter /* There was an empty file added or deleted in this revision. 1074251881Speter * We can't print a diff, but we can at least print 1075251881Speter * a diff header since we know what happened to this file. */ 1076251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 1077251881Speter "%s", header->data)); 1078251881Speter } 1079251881Speter } 1080251881Speter } 1081251881Speter } 1082251881Speter 1083251881Speter /* Make sure we delete any temporary files. */ 1084251881Speter if (orig_path) 1085251881Speter SVN_ERR(svn_io_remove_file2(orig_path, FALSE, pool)); 1086251881Speter if (new_path) 1087251881Speter SVN_ERR(svn_io_remove_file2(new_path, FALSE, pool)); 1088251881Speter 1089251881Speter /*** Now handle property diffs ***/ 1090251881Speter if ((node->prop_mod) && (node->action != 'D') && (! c->ignore_properties)) 1091251881Speter { 1092251881Speter apr_hash_t *local_proptable; 1093251881Speter apr_hash_t *base_proptable; 1094251881Speter apr_array_header_t *propchanges, *props; 1095251881Speter 1096251881Speter SVN_ERR(svn_fs_node_proplist(&local_proptable, root, path, pool)); 1097251881Speter if (c->diff_copy_from && node->action == 'A' && is_copy) 1098251881Speter SVN_ERR(svn_fs_node_proplist(&base_proptable, base_root, 1099251881Speter base_path, pool)); 1100251881Speter else if (node->action == 'A') 1101251881Speter base_proptable = apr_hash_make(pool); 1102251881Speter else /* node->action == 'R' */ 1103251881Speter SVN_ERR(svn_fs_node_proplist(&base_proptable, base_root, 1104251881Speter base_path, pool)); 1105251881Speter SVN_ERR(svn_prop_diffs(&propchanges, local_proptable, 1106251881Speter base_proptable, pool)); 1107251881Speter SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, pool)); 1108251881Speter if (props->nelts > 0) 1109251881Speter { 1110251881Speter /* We print a diff header for the case when we only have property 1111251881Speter * mods. */ 1112251881Speter if (! diff_header_printed) 1113251881Speter { 1114251881Speter const char *orig_label, *new_label; 1115251881Speter 1116251881Speter SVN_ERR(generate_label(&orig_label, base_root, base_path, 1117251881Speter pool)); 1118251881Speter SVN_ERR(generate_label(&new_label, root, path, pool)); 1119251881Speter 1120251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 1121251881Speter "Index: %s\n", path)); 1122251881Speter SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, 1123251881Speter SVN_DIFF__EQUAL_STRING "\n")); 1124251881Speter /* --- <label1> 1125251881Speter * +++ <label2> */ 1126251881Speter SVN_ERR(svn_diff__unidiff_write_header( 1127251881Speter out_stream, encoding, orig_label, new_label, pool)); 1128251881Speter } 1129251881Speter SVN_ERR(display_prop_diffs(out_stream, encoding, 1130251881Speter props, base_proptable, path, pool)); 1131251881Speter } 1132251881Speter } 1133251881Speter 1134251881Speter /* Return here if the node has no children. */ 1135251881Speter node = node->child; 1136251881Speter if (! node) 1137251881Speter return SVN_NO_ERROR; 1138251881Speter 1139251881Speter /* Recursively handle the node's children. */ 1140251881Speter subpool = svn_pool_create(pool); 1141251881Speter SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node, 1142251881Speter svn_dirent_join(path, node->name, subpool), 1143251881Speter svn_dirent_join(base_path, node->name, subpool), 1144251881Speter c, tmpdir, subpool)); 1145251881Speter while (node->sibling) 1146251881Speter { 1147251881Speter svn_pool_clear(subpool); 1148251881Speter node = node->sibling; 1149251881Speter SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node, 1150251881Speter svn_dirent_join(path, node->name, subpool), 1151251881Speter svn_dirent_join(base_path, node->name, subpool), 1152251881Speter c, tmpdir, subpool)); 1153251881Speter } 1154251881Speter svn_pool_destroy(subpool); 1155251881Speter 1156251881Speter return SVN_NO_ERROR; 1157251881Speter} 1158251881Speter 1159251881Speter 1160251881Speter/* Print a repository directory, maybe recursively, possibly showing 1161251881Speter the node revision ids, and optionally using full paths. 1162251881Speter 1163251881Speter ROOT is the revision or transaction root used to build that tree. 1164251881Speter PATH and ID are the current path and node revision id being 1165251881Speter printed, and INDENTATION the number of spaces to prepent to that 1166251881Speter path's printed output. ID may be NULL if SHOW_IDS is FALSE (in 1167251881Speter which case, ids won't be printed at all). If RECURSE is TRUE, 1168251881Speter then print the tree recursively; otherwise, we'll stop after the 1169251881Speter first level (and use INDENTATION to keep track of how deep we are). 1170251881Speter 1171251881Speter Use POOL for all allocations. */ 1172251881Speterstatic svn_error_t * 1173251881Speterprint_tree(svn_fs_root_t *root, 1174251881Speter const char *path /* UTF-8! */, 1175251881Speter const svn_fs_id_t *id, 1176251881Speter svn_boolean_t is_dir, 1177251881Speter int indentation, 1178251881Speter svn_boolean_t show_ids, 1179251881Speter svn_boolean_t full_paths, 1180251881Speter svn_boolean_t recurse, 1181251881Speter apr_pool_t *pool) 1182251881Speter{ 1183251881Speter apr_pool_t *subpool; 1184251881Speter apr_hash_t *entries; 1185251881Speter const char* name; 1186251881Speter 1187251881Speter SVN_ERR(check_cancel(NULL)); 1188251881Speter 1189251881Speter /* Print the indentation. */ 1190251881Speter if (!full_paths) 1191251881Speter { 1192251881Speter int i; 1193251881Speter for (i = 0; i < indentation; i++) 1194251881Speter SVN_ERR(svn_cmdline_fputs(" ", stdout, pool)); 1195251881Speter } 1196251881Speter 1197251881Speter /* ### The path format is inconsistent.. needs fix */ 1198251881Speter if (full_paths) 1199251881Speter name = path; 1200251881Speter else if (*path == '/') 1201251881Speter name = svn_fspath__basename(path, pool); 1202251881Speter else 1203251881Speter name = svn_relpath_basename(path, NULL); 1204251881Speter 1205251881Speter if (svn_path_is_empty(name)) 1206251881Speter name = "/"; /* basename of '/' is "" */ 1207251881Speter 1208251881Speter /* Print the node. */ 1209251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s%s", 1210251881Speter name, 1211251881Speter is_dir && strcmp(name, "/") ? "/" : "")); 1212251881Speter 1213251881Speter if (show_ids) 1214251881Speter { 1215251881Speter svn_string_t *unparsed_id = NULL; 1216251881Speter if (id) 1217251881Speter unparsed_id = svn_fs_unparse_id(id, pool); 1218251881Speter SVN_ERR(svn_cmdline_printf(pool, " <%s>", 1219251881Speter unparsed_id 1220251881Speter ? unparsed_id->data 1221251881Speter : _("unknown"))); 1222251881Speter } 1223251881Speter SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); 1224251881Speter 1225251881Speter /* Return here if PATH is not a directory. */ 1226251881Speter if (! is_dir) 1227251881Speter return SVN_NO_ERROR; 1228251881Speter 1229251881Speter /* Recursively handle the node's children. */ 1230251881Speter if (recurse || (indentation == 0)) 1231251881Speter { 1232251881Speter apr_array_header_t *sorted_entries; 1233251881Speter int i; 1234251881Speter 1235251881Speter SVN_ERR(svn_fs_dir_entries(&entries, root, path, pool)); 1236251881Speter subpool = svn_pool_create(pool); 1237251881Speter sorted_entries = svn_sort__hash(entries, 1238251881Speter svn_sort_compare_items_lexically, pool); 1239251881Speter for (i = 0; i < sorted_entries->nelts; i++) 1240251881Speter { 1241251881Speter svn_sort__item_t item = APR_ARRAY_IDX(sorted_entries, i, 1242251881Speter svn_sort__item_t); 1243251881Speter svn_fs_dirent_t *entry = item.value; 1244251881Speter 1245251881Speter svn_pool_clear(subpool); 1246251881Speter SVN_ERR(print_tree(root, 1247251881Speter (*path == '/') 1248251881Speter ? svn_fspath__join(path, entry->name, pool) 1249251881Speter : svn_relpath_join(path, entry->name, pool), 1250251881Speter entry->id, (entry->kind == svn_node_dir), 1251251881Speter indentation + 1, show_ids, full_paths, 1252251881Speter recurse, subpool)); 1253251881Speter } 1254251881Speter svn_pool_destroy(subpool); 1255251881Speter } 1256251881Speter 1257251881Speter return SVN_NO_ERROR; 1258251881Speter} 1259251881Speter 1260251881Speter 1261251881Speter/* Set *BASE_REV to the revision on which the target root specified in 1262251881Speter C is based, or to SVN_INVALID_REVNUM when C represents "revision 1263251881Speter 0" (because that revision isn't based on another revision). */ 1264251881Speterstatic svn_error_t * 1265251881Speterget_base_rev(svn_revnum_t *base_rev, svnlook_ctxt_t *c, apr_pool_t *pool) 1266251881Speter{ 1267251881Speter if (c->is_revision) 1268251881Speter { 1269251881Speter *base_rev = c->rev_id - 1; 1270251881Speter } 1271251881Speter else 1272251881Speter { 1273251881Speter *base_rev = svn_fs_txn_base_revision(c->txn); 1274251881Speter 1275251881Speter if (! SVN_IS_VALID_REVNUM(*base_rev)) 1276251881Speter return svn_error_createf 1277251881Speter (SVN_ERR_FS_NO_SUCH_REVISION, NULL, 1278251881Speter _("Transaction '%s' is not based on a revision; how odd"), 1279251881Speter c->txn_name); 1280251881Speter } 1281251881Speter return SVN_NO_ERROR; 1282251881Speter} 1283251881Speter 1284251881Speter 1285251881Speter 1286251881Speter/*** Subcommand handlers. ***/ 1287251881Speter 1288251881Speter/* Print the revision's log message to stdout, followed by a newline. */ 1289251881Speterstatic svn_error_t * 1290251881Speterdo_log(svnlook_ctxt_t *c, svn_boolean_t print_size, apr_pool_t *pool) 1291251881Speter{ 1292251881Speter svn_string_t *prop_value; 1293251881Speter const char *prop_value_eol, *prop_value_native; 1294251881Speter svn_stream_t *stream; 1295251881Speter svn_error_t *err; 1296251881Speter apr_size_t len; 1297251881Speter 1298251881Speter SVN_ERR(get_property(&prop_value, c, SVN_PROP_REVISION_LOG, pool)); 1299251881Speter if (! (prop_value && prop_value->data)) 1300251881Speter { 1301251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s\n", print_size ? "0" : "")); 1302251881Speter return SVN_NO_ERROR; 1303251881Speter } 1304251881Speter 1305251881Speter /* We immitate what svn_cmdline_printf does here, since we need the byte 1306251881Speter size of what we are going to print. */ 1307251881Speter 1308251881Speter SVN_ERR(svn_subst_translate_cstring2(prop_value->data, &prop_value_eol, 1309251881Speter APR_EOL_STR, TRUE, 1310251881Speter NULL, FALSE, pool)); 1311251881Speter 1312251881Speter err = svn_cmdline_cstring_from_utf8(&prop_value_native, prop_value_eol, 1313251881Speter pool); 1314251881Speter if (err) 1315251881Speter { 1316251881Speter svn_error_clear(err); 1317251881Speter prop_value_native = svn_cmdline_cstring_from_utf8_fuzzy(prop_value_eol, 1318251881Speter pool); 1319251881Speter } 1320251881Speter 1321251881Speter len = strlen(prop_value_native); 1322251881Speter 1323251881Speter if (print_size) 1324251881Speter SVN_ERR(svn_cmdline_printf(pool, "%" APR_SIZE_T_FMT "\n", len)); 1325251881Speter 1326251881Speter /* Use a stream to bypass all stdio translations. */ 1327251881Speter SVN_ERR(svn_cmdline_fflush(stdout)); 1328251881Speter SVN_ERR(svn_stream_for_stdout(&stream, pool)); 1329251881Speter SVN_ERR(svn_stream_write(stream, prop_value_native, &len)); 1330251881Speter SVN_ERR(svn_stream_close(stream)); 1331251881Speter 1332251881Speter SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); 1333251881Speter 1334251881Speter return SVN_NO_ERROR; 1335251881Speter} 1336251881Speter 1337251881Speter 1338251881Speter/* Print the timestamp of the commit (in the revision case) or the 1339251881Speter empty string (in the transaction case) to stdout, followed by a 1340251881Speter newline. */ 1341251881Speterstatic svn_error_t * 1342251881Speterdo_date(svnlook_ctxt_t *c, apr_pool_t *pool) 1343251881Speter{ 1344251881Speter svn_string_t *prop_value; 1345251881Speter 1346251881Speter SVN_ERR(get_property(&prop_value, c, SVN_PROP_REVISION_DATE, pool)); 1347251881Speter if (prop_value && prop_value->data) 1348251881Speter { 1349251881Speter /* Convert the date for humans. */ 1350251881Speter apr_time_t aprtime; 1351251881Speter const char *time_utf8; 1352251881Speter 1353251881Speter SVN_ERR(svn_time_from_cstring(&aprtime, prop_value->data, pool)); 1354251881Speter 1355251881Speter time_utf8 = svn_time_to_human_cstring(aprtime, pool); 1356251881Speter 1357251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s", time_utf8)); 1358251881Speter } 1359251881Speter 1360251881Speter SVN_ERR(svn_cmdline_printf(pool, "\n")); 1361251881Speter return SVN_NO_ERROR; 1362251881Speter} 1363251881Speter 1364251881Speter 1365251881Speter/* Print the author of the commit to stdout, followed by a newline. */ 1366251881Speterstatic svn_error_t * 1367251881Speterdo_author(svnlook_ctxt_t *c, apr_pool_t *pool) 1368251881Speter{ 1369251881Speter svn_string_t *prop_value; 1370251881Speter 1371251881Speter SVN_ERR(get_property(&prop_value, c, 1372251881Speter SVN_PROP_REVISION_AUTHOR, pool)); 1373251881Speter if (prop_value && prop_value->data) 1374251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s", prop_value->data)); 1375251881Speter 1376251881Speter SVN_ERR(svn_cmdline_printf(pool, "\n")); 1377251881Speter return SVN_NO_ERROR; 1378251881Speter} 1379251881Speter 1380251881Speter 1381251881Speter/* Print a list of all directories in which files, or directory 1382251881Speter properties, have been modified. */ 1383251881Speterstatic svn_error_t * 1384251881Speterdo_dirs_changed(svnlook_ctxt_t *c, apr_pool_t *pool) 1385251881Speter{ 1386251881Speter svn_fs_root_t *root; 1387251881Speter svn_revnum_t base_rev_id; 1388251881Speter svn_repos_node_t *tree; 1389251881Speter 1390251881Speter SVN_ERR(get_root(&root, c, pool)); 1391251881Speter SVN_ERR(get_base_rev(&base_rev_id, c, pool)); 1392251881Speter if (base_rev_id == SVN_INVALID_REVNUM) 1393251881Speter return SVN_NO_ERROR; 1394251881Speter 1395251881Speter SVN_ERR(generate_delta_tree(&tree, c->repos, root, base_rev_id, pool)); 1396251881Speter if (tree) 1397251881Speter SVN_ERR(print_dirs_changed_tree(tree, "", pool)); 1398251881Speter 1399251881Speter return SVN_NO_ERROR; 1400251881Speter} 1401251881Speter 1402251881Speter 1403251881Speter/* Set *KIND to PATH's kind, if PATH exists. 1404251881Speter * 1405251881Speter * If PATH does not exist, then error; the text of the error depends 1406251881Speter * on whether PATH looks like a URL or not. 1407251881Speter */ 1408251881Speterstatic svn_error_t * 1409251881Speterverify_path(svn_node_kind_t *kind, 1410251881Speter svn_fs_root_t *root, 1411251881Speter const char *path, 1412251881Speter apr_pool_t *pool) 1413251881Speter{ 1414251881Speter SVN_ERR(svn_fs_check_path(kind, root, path, pool)); 1415251881Speter 1416251881Speter if (*kind == svn_node_none) 1417251881Speter { 1418251881Speter if (svn_path_is_url(path)) /* check for a common mistake. */ 1419251881Speter return svn_error_createf 1420251881Speter (SVN_ERR_FS_NOT_FOUND, NULL, 1421251881Speter _("'%s' is a URL, probably should be a path"), path); 1422251881Speter else 1423251881Speter return svn_error_createf 1424251881Speter (SVN_ERR_FS_NOT_FOUND, NULL, _("Path '%s' does not exist"), path); 1425251881Speter } 1426251881Speter 1427251881Speter return SVN_NO_ERROR; 1428251881Speter} 1429251881Speter 1430251881Speter 1431251881Speter/* Print the size (in bytes) of a file. */ 1432251881Speterstatic svn_error_t * 1433251881Speterdo_filesize(svnlook_ctxt_t *c, const char *path, apr_pool_t *pool) 1434251881Speter{ 1435251881Speter svn_fs_root_t *root; 1436251881Speter svn_node_kind_t kind; 1437251881Speter svn_filesize_t length; 1438251881Speter 1439251881Speter SVN_ERR(get_root(&root, c, pool)); 1440251881Speter SVN_ERR(verify_path(&kind, root, path, pool)); 1441251881Speter 1442251881Speter if (kind != svn_node_file) 1443251881Speter return svn_error_createf 1444251881Speter (SVN_ERR_FS_NOT_FILE, NULL, _("Path '%s' is not a file"), path); 1445251881Speter 1446251881Speter /* Else. */ 1447251881Speter 1448251881Speter SVN_ERR(svn_fs_file_length(&length, root, path, pool)); 1449251881Speter return svn_cmdline_printf(pool, "%" SVN_FILESIZE_T_FMT "\n", length); 1450251881Speter} 1451251881Speter 1452251881Speter/* Print the contents of the file at PATH in the repository. 1453251881Speter Error with SVN_ERR_FS_NOT_FOUND if PATH does not exist, or with 1454251881Speter SVN_ERR_FS_NOT_FILE if PATH exists but is not a file. */ 1455251881Speterstatic svn_error_t * 1456251881Speterdo_cat(svnlook_ctxt_t *c, const char *path, apr_pool_t *pool) 1457251881Speter{ 1458251881Speter svn_fs_root_t *root; 1459251881Speter svn_node_kind_t kind; 1460251881Speter svn_stream_t *fstream, *stdout_stream; 1461251881Speter 1462251881Speter SVN_ERR(get_root(&root, c, pool)); 1463251881Speter SVN_ERR(verify_path(&kind, root, path, pool)); 1464251881Speter 1465251881Speter if (kind != svn_node_file) 1466251881Speter return svn_error_createf 1467251881Speter (SVN_ERR_FS_NOT_FILE, NULL, _("Path '%s' is not a file"), path); 1468251881Speter 1469251881Speter /* Else. */ 1470251881Speter 1471251881Speter SVN_ERR(svn_fs_file_contents(&fstream, root, path, pool)); 1472251881Speter SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); 1473251881Speter 1474251881Speter return svn_stream_copy3(fstream, svn_stream_disown(stdout_stream, pool), 1475251881Speter check_cancel, NULL, pool); 1476251881Speter} 1477251881Speter 1478251881Speter 1479251881Speter/* Print a list of all paths modified in a format compatible with `svn 1480251881Speter update'. */ 1481251881Speterstatic svn_error_t * 1482251881Speterdo_changed(svnlook_ctxt_t *c, apr_pool_t *pool) 1483251881Speter{ 1484251881Speter svn_fs_root_t *root; 1485251881Speter svn_revnum_t base_rev_id; 1486251881Speter svn_repos_node_t *tree; 1487251881Speter 1488251881Speter SVN_ERR(get_root(&root, c, pool)); 1489251881Speter SVN_ERR(get_base_rev(&base_rev_id, c, pool)); 1490251881Speter if (base_rev_id == SVN_INVALID_REVNUM) 1491251881Speter return SVN_NO_ERROR; 1492251881Speter 1493251881Speter SVN_ERR(generate_delta_tree(&tree, c->repos, root, base_rev_id, pool)); 1494251881Speter if (tree) 1495251881Speter SVN_ERR(print_changed_tree(tree, "", c->copy_info, pool)); 1496251881Speter 1497251881Speter return SVN_NO_ERROR; 1498251881Speter} 1499251881Speter 1500251881Speter 1501251881Speter/* Print some diff-y stuff in a TBD way. :-) */ 1502251881Speterstatic svn_error_t * 1503251881Speterdo_diff(svnlook_ctxt_t *c, apr_pool_t *pool) 1504251881Speter{ 1505251881Speter svn_fs_root_t *root, *base_root; 1506251881Speter svn_revnum_t base_rev_id; 1507251881Speter svn_repos_node_t *tree; 1508251881Speter 1509251881Speter SVN_ERR(get_root(&root, c, pool)); 1510251881Speter SVN_ERR(get_base_rev(&base_rev_id, c, pool)); 1511251881Speter if (base_rev_id == SVN_INVALID_REVNUM) 1512251881Speter return SVN_NO_ERROR; 1513251881Speter 1514251881Speter SVN_ERR(generate_delta_tree(&tree, c->repos, root, base_rev_id, pool)); 1515251881Speter if (tree) 1516251881Speter { 1517251881Speter const char *tmpdir; 1518251881Speter svn_stream_t *out_stream; 1519251881Speter const char *encoding = svn_cmdline_output_encoding(pool); 1520251881Speter 1521251881Speter SVN_ERR(svn_fs_revision_root(&base_root, c->fs, base_rev_id, pool)); 1522251881Speter SVN_ERR(svn_io_temp_dir(&tmpdir, pool)); 1523251881Speter 1524251881Speter /* This fflush() might seem odd, but it was added to deal 1525251881Speter with this bug report: 1526251881Speter 1527251881Speter http://subversion.tigris.org/servlets/ReadMsg?\ 1528251881Speter list=dev&msgNo=140782 1529251881Speter 1530251881Speter From: "Steve Hay" <SteveHay{_AT_}planit.com> 1531251881Speter To: <dev@subversion.tigris.org> 1532251881Speter Subject: svnlook diff output in wrong order when redirected 1533251881Speter Date: Fri, 4 Jul 2008 16:34:15 +0100 1534251881Speter Message-ID: <1B32FF956ABF414C9BCE5E487A1497E702014F62@\ 1535251881Speter ukmail02.planit.group> 1536251881Speter 1537251881Speter Adding the fflush() fixed the bug (not everyone could 1538251881Speter reproduce it, but those who could confirmed the fix). 1539251881Speter Later in the thread, Daniel Shahaf speculated as to 1540251881Speter why the fix works: 1541251881Speter 1542251881Speter "Because svn_cmdline_printf() uses the standard 1543251881Speter 'FILE *stdout' to write to stdout, while 1544251881Speter svn_stream_for_stdout() uses (through 1545251881Speter apr_file_open_stdout()) Windows API's to get a 1546251881Speter handle for stdout?" */ 1547251881Speter SVN_ERR(svn_cmdline_fflush(stdout)); 1548251881Speter SVN_ERR(svn_stream_for_stdout(&out_stream, pool)); 1549251881Speter 1550251881Speter SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, tree, 1551251881Speter "", "", c, tmpdir, pool)); 1552251881Speter } 1553251881Speter return SVN_NO_ERROR; 1554251881Speter} 1555251881Speter 1556251881Speter 1557251881Speter 1558251881Speter/* Callback baton for print_history() (and do_history()). */ 1559251881Speterstruct print_history_baton 1560251881Speter{ 1561251881Speter svn_fs_t *fs; 1562251881Speter svn_boolean_t show_ids; /* whether to show node IDs */ 1563251881Speter apr_size_t limit; /* max number of history items */ 1564251881Speter apr_size_t count; /* number of history items processed */ 1565251881Speter}; 1566251881Speter 1567251881Speter/* Implements svn_repos_history_func_t interface. Print the history 1568251881Speter that's reported through this callback, possibly finding and 1569251881Speter displaying node-rev-ids. */ 1570251881Speterstatic svn_error_t * 1571251881Speterprint_history(void *baton, 1572251881Speter const char *path, 1573251881Speter svn_revnum_t revision, 1574251881Speter apr_pool_t *pool) 1575251881Speter{ 1576251881Speter struct print_history_baton *phb = baton; 1577251881Speter 1578251881Speter SVN_ERR(check_cancel(NULL)); 1579251881Speter 1580251881Speter if (phb->show_ids) 1581251881Speter { 1582251881Speter const svn_fs_id_t *node_id; 1583251881Speter svn_fs_root_t *rev_root; 1584251881Speter svn_string_t *id_string; 1585251881Speter 1586251881Speter SVN_ERR(svn_fs_revision_root(&rev_root, phb->fs, revision, pool)); 1587251881Speter SVN_ERR(svn_fs_node_id(&node_id, rev_root, path, pool)); 1588251881Speter id_string = svn_fs_unparse_id(node_id, pool); 1589251881Speter SVN_ERR(svn_cmdline_printf(pool, "%8ld %s <%s>\n", 1590251881Speter revision, path, id_string->data)); 1591251881Speter } 1592251881Speter else 1593251881Speter { 1594251881Speter SVN_ERR(svn_cmdline_printf(pool, "%8ld %s\n", revision, path)); 1595251881Speter } 1596251881Speter 1597251881Speter if (phb->limit > 0) 1598251881Speter { 1599251881Speter phb->count++; 1600251881Speter if (phb->count >= phb->limit) 1601251881Speter /* Not L10N'd, since this error is supressed by the caller. */ 1602251881Speter return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, 1603251881Speter _("History item limit reached")); 1604251881Speter } 1605251881Speter 1606251881Speter return SVN_NO_ERROR; 1607251881Speter} 1608251881Speter 1609251881Speter 1610251881Speter/* Print a tabular display of history location points for PATH in 1611251881Speter revision C->rev_id. Optionally, SHOW_IDS. Use POOL for 1612251881Speter allocations. */ 1613251881Speterstatic svn_error_t * 1614251881Speterdo_history(svnlook_ctxt_t *c, 1615251881Speter const char *path, 1616251881Speter apr_pool_t *pool) 1617251881Speter{ 1618251881Speter struct print_history_baton args; 1619251881Speter 1620251881Speter if (c->show_ids) 1621251881Speter { 1622251881Speter SVN_ERR(svn_cmdline_printf(pool, _("REVISION PATH <ID>\n" 1623251881Speter "-------- ---------\n"))); 1624251881Speter } 1625251881Speter else 1626251881Speter { 1627251881Speter SVN_ERR(svn_cmdline_printf(pool, _("REVISION PATH\n" 1628251881Speter "-------- ----\n"))); 1629251881Speter } 1630251881Speter 1631251881Speter /* Call our history crawler. We want the whole lifetime of the path 1632251881Speter (prior to the user-supplied revision, of course), across all 1633251881Speter copies. */ 1634251881Speter args.fs = c->fs; 1635251881Speter args.show_ids = c->show_ids; 1636251881Speter args.limit = c->limit; 1637251881Speter args.count = 0; 1638251881Speter SVN_ERR(svn_repos_history2(c->fs, path, print_history, &args, 1639251881Speter NULL, NULL, 0, c->rev_id, TRUE, pool)); 1640251881Speter return SVN_NO_ERROR; 1641251881Speter} 1642251881Speter 1643251881Speter 1644251881Speter/* Print the value of property PROPNAME on PATH in the repository. 1645251881Speter 1646251881Speter If VERBOSE, print their values too. If SHOW_INHERITED_PROPS, print 1647251881Speter PATH's inherited props too. 1648251881Speter 1649251881Speter Error with SVN_ERR_FS_NOT_FOUND if PATH does not exist. If 1650251881Speter SHOW_INHERITED_PROPS is FALSE,then error with SVN_ERR_PROPERTY_NOT_FOUND 1651251881Speter if there is no such property on PATH. If SHOW_INHERITED_PROPS is TRUE, 1652251881Speter then error with SVN_ERR_PROPERTY_NOT_FOUND only if there is no such 1653251881Speter property on PATH nor inherited by path. 1654251881Speter 1655251881Speter If PATH is NULL, operate on a revision property. */ 1656251881Speterstatic svn_error_t * 1657251881Speterdo_pget(svnlook_ctxt_t *c, 1658251881Speter const char *propname, 1659251881Speter const char *path, 1660251881Speter svn_boolean_t verbose, 1661251881Speter svn_boolean_t show_inherited_props, 1662251881Speter apr_pool_t *pool) 1663251881Speter{ 1664251881Speter svn_fs_root_t *root; 1665251881Speter svn_string_t *prop; 1666251881Speter svn_node_kind_t kind; 1667251881Speter svn_stream_t *stdout_stream; 1668251881Speter apr_size_t len; 1669251881Speter apr_array_header_t *inherited_props = NULL; 1670251881Speter 1671251881Speter SVN_ERR(get_root(&root, c, pool)); 1672251881Speter if (path != NULL) 1673251881Speter { 1674251881Speter path = svn_fspath__canonicalize(path, pool); 1675251881Speter SVN_ERR(verify_path(&kind, root, path, pool)); 1676251881Speter SVN_ERR(svn_fs_node_prop(&prop, root, path, propname, pool)); 1677251881Speter 1678251881Speter if (show_inherited_props) 1679251881Speter { 1680251881Speter SVN_ERR(svn_repos_fs_get_inherited_props(&inherited_props, root, 1681251881Speter path, propname, NULL, 1682251881Speter NULL, pool, pool)); 1683251881Speter } 1684251881Speter } 1685251881Speter else /* --revprop */ 1686251881Speter { 1687251881Speter SVN_ERR(get_property(&prop, c, propname, pool)); 1688251881Speter } 1689251881Speter 1690251881Speter /* Did we find nothing? */ 1691251881Speter if (prop == NULL 1692251881Speter && (!show_inherited_props || inherited_props->nelts == 0)) 1693251881Speter { 1694251881Speter const char *err_msg; 1695251881Speter if (path == NULL) 1696251881Speter { 1697251881Speter /* We're operating on a revprop (e.g. c->is_revision). */ 1698251881Speter err_msg = apr_psprintf(pool, 1699251881Speter _("Property '%s' not found on revision %ld"), 1700251881Speter propname, c->rev_id); 1701251881Speter } 1702251881Speter else 1703251881Speter { 1704251881Speter if (SVN_IS_VALID_REVNUM(c->rev_id)) 1705251881Speter { 1706251881Speter if (show_inherited_props) 1707251881Speter err_msg = apr_psprintf(pool, 1708251881Speter _("Property '%s' not found on path '%s' " 1709251881Speter "or inherited from a parent " 1710251881Speter "in revision %ld"), 1711251881Speter propname, path, c->rev_id); 1712251881Speter else 1713251881Speter err_msg = apr_psprintf(pool, 1714251881Speter _("Property '%s' not found on path '%s' " 1715251881Speter "in revision %ld"), 1716251881Speter propname, path, c->rev_id); 1717251881Speter } 1718251881Speter else 1719251881Speter { 1720251881Speter if (show_inherited_props) 1721251881Speter err_msg = apr_psprintf(pool, 1722251881Speter _("Property '%s' not found on path '%s' " 1723251881Speter "or inherited from a parent " 1724251881Speter "in transaction %s"), 1725251881Speter propname, path, c->txn_name); 1726251881Speter else 1727251881Speter err_msg = apr_psprintf(pool, 1728251881Speter _("Property '%s' not found on path '%s' " 1729251881Speter "in transaction %s"), 1730251881Speter propname, path, c->txn_name); 1731251881Speter } 1732251881Speter } 1733251881Speter return svn_error_create(SVN_ERR_PROPERTY_NOT_FOUND, NULL, err_msg); 1734251881Speter } 1735251881Speter 1736251881Speter SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); 1737251881Speter 1738251881Speter if (verbose || show_inherited_props) 1739251881Speter { 1740251881Speter if (inherited_props) 1741251881Speter { 1742251881Speter int i; 1743251881Speter 1744251881Speter for (i = 0; i < inherited_props->nelts; i++) 1745251881Speter { 1746251881Speter svn_prop_inherited_item_t *elt = 1747251881Speter APR_ARRAY_IDX(inherited_props, i, 1748251881Speter svn_prop_inherited_item_t *); 1749251881Speter 1750251881Speter if (verbose) 1751251881Speter { 1752251881Speter SVN_ERR(svn_stream_printf(stdout_stream, pool, 1753251881Speter _("Inherited properties on '%s',\nfrom '%s':\n"), 1754251881Speter path, svn_fspath__canonicalize(elt->path_or_url, 1755251881Speter pool))); 1756251881Speter SVN_ERR(svn_cmdline__print_prop_hash(stdout_stream, 1757251881Speter elt->prop_hash, 1758251881Speter !verbose, pool)); 1759251881Speter } 1760251881Speter else 1761251881Speter { 1762251881Speter svn_string_t *propval = 1763251881Speter svn__apr_hash_index_val(apr_hash_first(pool, 1764251881Speter elt->prop_hash)); 1765251881Speter 1766251881Speter SVN_ERR(svn_stream_printf( 1767251881Speter stdout_stream, pool, "%s - ", 1768251881Speter svn_fspath__canonicalize(elt->path_or_url, pool))); 1769251881Speter len = propval->len; 1770251881Speter SVN_ERR(svn_stream_write(stdout_stream, propval->data, &len)); 1771251881Speter /* If we have more than one property to write, then add a newline*/ 1772251881Speter if (inherited_props->nelts > 1 || prop) 1773251881Speter { 1774251881Speter len = strlen(APR_EOL_STR); 1775251881Speter SVN_ERR(svn_stream_write(stdout_stream, APR_EOL_STR, &len)); 1776251881Speter } 1777251881Speter } 1778251881Speter } 1779251881Speter } 1780251881Speter 1781251881Speter if (prop) 1782251881Speter { 1783251881Speter if (verbose) 1784251881Speter { 1785251881Speter apr_hash_t *hash = apr_hash_make(pool); 1786251881Speter 1787251881Speter svn_hash_sets(hash, propname, prop); 1788251881Speter SVN_ERR(svn_stream_printf(stdout_stream, pool, 1789251881Speter _("Properties on '%s':\n"), path)); 1790251881Speter SVN_ERR(svn_cmdline__print_prop_hash(stdout_stream, hash, 1791251881Speter FALSE, pool)); 1792251881Speter } 1793251881Speter else 1794251881Speter { 1795251881Speter SVN_ERR(svn_stream_printf(stdout_stream, pool, "%s - ", path)); 1796251881Speter len = prop->len; 1797251881Speter SVN_ERR(svn_stream_write(stdout_stream, prop->data, &len)); 1798251881Speter } 1799251881Speter } 1800251881Speter } 1801251881Speter else /* Raw single prop output, i.e. non-verbose output with no 1802251881Speter inherited props. */ 1803251881Speter { 1804251881Speter /* Unlike the command line client, we don't translate the property 1805251881Speter value or print a trailing newline here. We just output the raw 1806251881Speter bytes of whatever's in the repository, as svnlook is more likely 1807251881Speter to be used for automated inspections. */ 1808251881Speter len = prop->len; 1809251881Speter SVN_ERR(svn_stream_write(stdout_stream, prop->data, &len)); 1810251881Speter } 1811251881Speter 1812251881Speter return SVN_NO_ERROR; 1813251881Speter} 1814251881Speter 1815251881Speter 1816251881Speter/* Print the property names of all properties on PATH in the repository. 1817251881Speter 1818251881Speter If VERBOSE, print their values too. If XML, print as XML rather than as 1819251881Speter plain text. If SHOW_INHERITED_PROPS, print PATH's inherited props too. 1820251881Speter 1821251881Speter Error with SVN_ERR_FS_NOT_FOUND if PATH does not exist. 1822251881Speter 1823251881Speter If PATH is NULL, operate on a revision properties. */ 1824251881Speterstatic svn_error_t * 1825251881Speterdo_plist(svnlook_ctxt_t *c, 1826251881Speter const char *path, 1827251881Speter svn_boolean_t verbose, 1828251881Speter svn_boolean_t xml, 1829251881Speter svn_boolean_t show_inherited_props, 1830251881Speter apr_pool_t *pool) 1831251881Speter{ 1832251881Speter svn_fs_root_t *root; 1833251881Speter apr_hash_t *props; 1834251881Speter apr_hash_index_t *hi; 1835251881Speter svn_node_kind_t kind; 1836251881Speter svn_stringbuf_t *sb = NULL; 1837251881Speter svn_boolean_t revprop = FALSE; 1838251881Speter apr_array_header_t *inherited_props = NULL; 1839251881Speter 1840251881Speter if (path != NULL) 1841251881Speter { 1842251881Speter /* PATH might be the root of the repsository and we accept both 1843251881Speter "" and "/". But to avoid the somewhat cryptic output like this: 1844251881Speter 1845251881Speter >svnlook pl repos-path "" 1846251881Speter Properties on '': 1847251881Speter svn:auto-props 1848251881Speter svn:global-ignores 1849251881Speter 1850251881Speter We canonicalize PATH so that is has a leading slash. */ 1851251881Speter path = svn_fspath__canonicalize(path, pool); 1852251881Speter 1853251881Speter SVN_ERR(get_root(&root, c, pool)); 1854251881Speter SVN_ERR(verify_path(&kind, root, path, pool)); 1855251881Speter SVN_ERR(svn_fs_node_proplist(&props, root, path, pool)); 1856251881Speter 1857251881Speter if (show_inherited_props) 1858251881Speter SVN_ERR(svn_repos_fs_get_inherited_props(&inherited_props, root, 1859251881Speter path, NULL, NULL, NULL, 1860251881Speter pool, pool)); 1861251881Speter } 1862251881Speter else if (c->is_revision) 1863251881Speter { 1864251881Speter SVN_ERR(svn_fs_revision_proplist(&props, c->fs, c->rev_id, pool)); 1865251881Speter revprop = TRUE; 1866251881Speter } 1867251881Speter else 1868251881Speter { 1869251881Speter SVN_ERR(svn_fs_txn_proplist(&props, c->txn, pool)); 1870251881Speter revprop = TRUE; 1871251881Speter } 1872251881Speter 1873251881Speter if (xml) 1874251881Speter { 1875251881Speter /* <?xml version="1.0" encoding="UTF-8"?> */ 1876251881Speter svn_xml_make_header2(&sb, "UTF-8", pool); 1877251881Speter 1878251881Speter /* "<properties>" */ 1879251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "properties", NULL); 1880251881Speter } 1881251881Speter 1882251881Speter if (inherited_props) 1883251881Speter { 1884251881Speter int i; 1885251881Speter 1886251881Speter for (i = 0; i < inherited_props->nelts; i++) 1887251881Speter { 1888251881Speter svn_prop_inherited_item_t *elt = 1889251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1890251881Speter 1891251881Speter /* Canonicalize the inherited parent paths for consistency 1892251881Speter with PATH. */ 1893251881Speter if (xml) 1894251881Speter { 1895251881Speter svn_xml_make_open_tag( 1896251881Speter &sb, pool, svn_xml_normal, "target", "path", 1897251881Speter svn_fspath__canonicalize(elt->path_or_url, pool), 1898251881Speter NULL); 1899251881Speter SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, elt->prop_hash, 1900251881Speter !verbose, TRUE, 1901251881Speter pool)); 1902251881Speter svn_xml_make_close_tag(&sb, pool, "target"); 1903251881Speter } 1904251881Speter else 1905251881Speter { 1906251881Speter SVN_ERR(svn_cmdline_printf( 1907251881Speter pool, _("Inherited properties on '%s',\nfrom '%s':\n"), 1908251881Speter path, svn_fspath__canonicalize(elt->path_or_url, pool))); 1909251881Speter SVN_ERR(svn_cmdline__print_prop_hash(NULL, elt->prop_hash, 1910251881Speter !verbose, pool)); 1911251881Speter } 1912251881Speter } 1913251881Speter } 1914251881Speter 1915251881Speter if (xml) 1916251881Speter { 1917251881Speter if (revprop) 1918251881Speter { 1919251881Speter /* "<revprops ...>" */ 1920251881Speter if (c->is_revision) 1921251881Speter { 1922251881Speter char *revstr = apr_psprintf(pool, "%ld", c->rev_id); 1923251881Speter 1924251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", 1925251881Speter "rev", revstr, NULL); 1926251881Speter } 1927251881Speter else 1928251881Speter { 1929251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", 1930251881Speter "txn", c->txn_name, NULL); 1931251881Speter } 1932251881Speter } 1933251881Speter else 1934251881Speter { 1935251881Speter /* "<target ...>" */ 1936251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target", 1937251881Speter "path", path, NULL); 1938251881Speter } 1939251881Speter } 1940251881Speter 1941251881Speter if (!xml && path /* Not a --revprop */) 1942251881Speter SVN_ERR(svn_cmdline_printf(pool, _("Properties on '%s':\n"), path)); 1943251881Speter 1944251881Speter for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) 1945251881Speter { 1946251881Speter const char *pname = svn__apr_hash_index_key(hi); 1947251881Speter svn_string_t *propval = svn__apr_hash_index_val(hi); 1948251881Speter 1949251881Speter SVN_ERR(check_cancel(NULL)); 1950251881Speter 1951251881Speter /* Since we're already adding a trailing newline (and possible a 1952251881Speter colon and some spaces) anyway, just mimic the output of the 1953251881Speter command line client proplist. Compare to 'svnlook propget', 1954251881Speter which sends the raw bytes to stdout, untranslated. */ 1955251881Speter /* We leave printf calls here, since we don't always know the encoding 1956251881Speter of the prop value. */ 1957251881Speter if (svn_prop_needs_translation(pname)) 1958251881Speter SVN_ERR(svn_subst_detranslate_string(&propval, propval, TRUE, pool)); 1959251881Speter 1960251881Speter if (verbose) 1961251881Speter { 1962251881Speter if (xml) 1963251881Speter svn_cmdline__print_xml_prop(&sb, pname, propval, FALSE, pool); 1964251881Speter else 1965251881Speter { 1966251881Speter const char *pname_stdout; 1967251881Speter const char *indented_newval; 1968251881Speter 1969251881Speter SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, 1970251881Speter pool)); 1971251881Speter printf(" %s\n", pname_stdout); 1972251881Speter /* Add an extra newline to the value before indenting, so that 1973251881Speter every line of output has the indentation whether the value 1974251881Speter already ended in a newline or not. */ 1975251881Speter indented_newval = 1976251881Speter svn_cmdline__indent_string(apr_psprintf(pool, "%s\n", 1977251881Speter propval->data), 1978251881Speter " ", pool); 1979251881Speter printf("%s", indented_newval); 1980251881Speter } 1981251881Speter } 1982251881Speter else if (xml) 1983251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_self_closing, "property", 1984251881Speter "name", pname, NULL); 1985251881Speter else 1986251881Speter printf(" %s\n", pname); 1987251881Speter } 1988251881Speter if (xml) 1989251881Speter { 1990251881Speter errno = 0; 1991251881Speter if (revprop) 1992251881Speter { 1993251881Speter /* "</revprops>" */ 1994251881Speter svn_xml_make_close_tag(&sb, pool, "revprops"); 1995251881Speter } 1996251881Speter else 1997251881Speter { 1998251881Speter /* "</target>" */ 1999251881Speter svn_xml_make_close_tag(&sb, pool, "target"); 2000251881Speter } 2001251881Speter 2002251881Speter /* "</properties>" */ 2003251881Speter svn_xml_make_close_tag(&sb, pool, "properties"); 2004251881Speter 2005251881Speter if (fputs(sb->data, stdout) == EOF) 2006251881Speter { 2007251881Speter if (errno) 2008251881Speter return svn_error_wrap_apr(errno, _("Write error")); 2009251881Speter else 2010251881Speter return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL); 2011251881Speter } 2012251881Speter } 2013251881Speter 2014251881Speter return SVN_NO_ERROR; 2015251881Speter} 2016251881Speter 2017251881Speter 2018251881Speterstatic svn_error_t * 2019251881Speterdo_tree(svnlook_ctxt_t *c, 2020251881Speter const char *path, 2021251881Speter svn_boolean_t show_ids, 2022251881Speter svn_boolean_t full_paths, 2023251881Speter svn_boolean_t recurse, 2024251881Speter apr_pool_t *pool) 2025251881Speter{ 2026251881Speter svn_fs_root_t *root; 2027251881Speter const svn_fs_id_t *id; 2028251881Speter svn_boolean_t is_dir; 2029251881Speter 2030251881Speter SVN_ERR(get_root(&root, c, pool)); 2031251881Speter SVN_ERR(svn_fs_node_id(&id, root, path, pool)); 2032251881Speter SVN_ERR(svn_fs_is_dir(&is_dir, root, path, pool)); 2033251881Speter SVN_ERR(print_tree(root, path, id, is_dir, 0, show_ids, full_paths, 2034251881Speter recurse, pool)); 2035251881Speter return SVN_NO_ERROR; 2036251881Speter} 2037251881Speter 2038251881Speter 2039251881Speter/* Custom filesystem warning function. */ 2040251881Speterstatic void 2041251881Speterwarning_func(void *baton, 2042251881Speter svn_error_t *err) 2043251881Speter{ 2044251881Speter if (! err) 2045251881Speter return; 2046251881Speter svn_handle_error2(err, stderr, FALSE, "svnlook: "); 2047251881Speter} 2048251881Speter 2049251881Speter 2050251881Speter/* Return an error if the number of arguments (excluding the repository 2051251881Speter * argument) is not NUM_ARGS. NUM_ARGS must be 0 or 1. The arguments 2052251881Speter * are assumed to be found in OPT_STATE->arg1 and OPT_STATE->arg2. */ 2053251881Speterstatic svn_error_t * 2054251881Spetercheck_number_of_args(struct svnlook_opt_state *opt_state, 2055251881Speter int num_args) 2056251881Speter{ 2057251881Speter if ((num_args == 0 && opt_state->arg1 != NULL) 2058251881Speter || (num_args == 1 && opt_state->arg2 != NULL)) 2059251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2060251881Speter _("Too many arguments given")); 2061251881Speter if ((num_args == 1 && opt_state->arg1 == NULL)) 2062251881Speter return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, 2063251881Speter _("Missing repository path argument")); 2064251881Speter return SVN_NO_ERROR; 2065251881Speter} 2066251881Speter 2067251881Speter 2068251881Speter/* Factory function for the context baton. */ 2069251881Speterstatic svn_error_t * 2070251881Speterget_ctxt_baton(svnlook_ctxt_t **baton_p, 2071251881Speter struct svnlook_opt_state *opt_state, 2072251881Speter apr_pool_t *pool) 2073251881Speter{ 2074251881Speter svnlook_ctxt_t *baton = apr_pcalloc(pool, sizeof(*baton)); 2075251881Speter 2076251881Speter SVN_ERR(svn_repos_open2(&(baton->repos), opt_state->repos_path, NULL, 2077251881Speter pool)); 2078251881Speter baton->fs = svn_repos_fs(baton->repos); 2079251881Speter svn_fs_set_warning_func(baton->fs, warning_func, NULL); 2080251881Speter baton->show_ids = opt_state->show_ids; 2081251881Speter baton->limit = opt_state->limit; 2082251881Speter baton->no_diff_deleted = opt_state->no_diff_deleted; 2083251881Speter baton->no_diff_added = opt_state->no_diff_added; 2084251881Speter baton->diff_copy_from = opt_state->diff_copy_from; 2085251881Speter baton->full_paths = opt_state->full_paths; 2086251881Speter baton->copy_info = opt_state->copy_info; 2087251881Speter baton->is_revision = opt_state->txn == NULL; 2088251881Speter baton->rev_id = opt_state->rev; 2089251881Speter baton->txn_name = apr_pstrdup(pool, opt_state->txn); 2090251881Speter baton->diff_options = svn_cstring_split(opt_state->extensions 2091251881Speter ? opt_state->extensions : "", 2092251881Speter " \t\n\r", TRUE, pool); 2093251881Speter baton->ignore_properties = opt_state->ignore_properties; 2094251881Speter baton->properties_only = opt_state->properties_only; 2095251881Speter baton->diff_cmd = opt_state->diff_cmd; 2096251881Speter 2097251881Speter if (baton->txn_name) 2098251881Speter SVN_ERR(svn_fs_open_txn(&(baton->txn), baton->fs, 2099251881Speter baton->txn_name, pool)); 2100251881Speter else if (baton->rev_id == SVN_INVALID_REVNUM) 2101251881Speter SVN_ERR(svn_fs_youngest_rev(&(baton->rev_id), baton->fs, pool)); 2102251881Speter 2103251881Speter *baton_p = baton; 2104251881Speter return SVN_NO_ERROR; 2105251881Speter} 2106251881Speter 2107251881Speter 2108251881Speter 2109251881Speter/*** Subcommands. ***/ 2110251881Speter 2111251881Speter/* This implements `svn_opt_subcommand_t'. */ 2112251881Speterstatic svn_error_t * 2113251881Spetersubcommand_author(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2114251881Speter{ 2115251881Speter struct svnlook_opt_state *opt_state = baton; 2116251881Speter svnlook_ctxt_t *c; 2117251881Speter 2118251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2119251881Speter 2120251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2121251881Speter SVN_ERR(do_author(c, pool)); 2122251881Speter return SVN_NO_ERROR; 2123251881Speter} 2124251881Speter 2125251881Speter/* This implements `svn_opt_subcommand_t'. */ 2126251881Speterstatic svn_error_t * 2127251881Spetersubcommand_cat(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2128251881Speter{ 2129251881Speter struct svnlook_opt_state *opt_state = baton; 2130251881Speter svnlook_ctxt_t *c; 2131251881Speter 2132251881Speter SVN_ERR(check_number_of_args(opt_state, 1)); 2133251881Speter 2134251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2135251881Speter SVN_ERR(do_cat(c, opt_state->arg1, pool)); 2136251881Speter return SVN_NO_ERROR; 2137251881Speter} 2138251881Speter 2139251881Speter/* This implements `svn_opt_subcommand_t'. */ 2140251881Speterstatic svn_error_t * 2141251881Spetersubcommand_changed(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2142251881Speter{ 2143251881Speter struct svnlook_opt_state *opt_state = baton; 2144251881Speter svnlook_ctxt_t *c; 2145251881Speter 2146251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2147251881Speter 2148251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2149251881Speter SVN_ERR(do_changed(c, pool)); 2150251881Speter return SVN_NO_ERROR; 2151251881Speter} 2152251881Speter 2153251881Speter/* This implements `svn_opt_subcommand_t'. */ 2154251881Speterstatic svn_error_t * 2155251881Spetersubcommand_date(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2156251881Speter{ 2157251881Speter struct svnlook_opt_state *opt_state = baton; 2158251881Speter svnlook_ctxt_t *c; 2159251881Speter 2160251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2161251881Speter 2162251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2163251881Speter SVN_ERR(do_date(c, pool)); 2164251881Speter return SVN_NO_ERROR; 2165251881Speter} 2166251881Speter 2167251881Speter/* This implements `svn_opt_subcommand_t'. */ 2168251881Speterstatic svn_error_t * 2169251881Spetersubcommand_diff(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2170251881Speter{ 2171251881Speter struct svnlook_opt_state *opt_state = baton; 2172251881Speter svnlook_ctxt_t *c; 2173251881Speter 2174251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2175251881Speter 2176251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2177251881Speter SVN_ERR(do_diff(c, pool)); 2178251881Speter return SVN_NO_ERROR; 2179251881Speter} 2180251881Speter 2181251881Speter/* This implements `svn_opt_subcommand_t'. */ 2182251881Speterstatic svn_error_t * 2183251881Spetersubcommand_dirschanged(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2184251881Speter{ 2185251881Speter struct svnlook_opt_state *opt_state = baton; 2186251881Speter svnlook_ctxt_t *c; 2187251881Speter 2188251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2189251881Speter 2190251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2191251881Speter SVN_ERR(do_dirs_changed(c, pool)); 2192251881Speter return SVN_NO_ERROR; 2193251881Speter} 2194251881Speter 2195251881Speter/* This implements `svn_opt_subcommand_t'. */ 2196251881Speterstatic svn_error_t * 2197251881Spetersubcommand_filesize(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2198251881Speter{ 2199251881Speter struct svnlook_opt_state *opt_state = baton; 2200251881Speter svnlook_ctxt_t *c; 2201251881Speter 2202251881Speter SVN_ERR(check_number_of_args(opt_state, 1)); 2203251881Speter 2204251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2205251881Speter SVN_ERR(do_filesize(c, opt_state->arg1, pool)); 2206251881Speter return SVN_NO_ERROR; 2207251881Speter} 2208251881Speter 2209251881Speter/* This implements `svn_opt_subcommand_t'. */ 2210251881Speterstatic svn_error_t * 2211251881Spetersubcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2212251881Speter{ 2213251881Speter struct svnlook_opt_state *opt_state = baton; 2214251881Speter const char *header = 2215251881Speter _("general usage: svnlook SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n" 2216251881Speter "Note: any subcommand which takes the '--revision' and '--transaction'\n" 2217251881Speter " options will, if invoked without one of those options, act on\n" 2218251881Speter " the repository's youngest revision.\n" 2219251881Speter "Type 'svnlook help <subcommand>' for help on a specific subcommand.\n" 2220251881Speter "Type 'svnlook --version' to see the program version and FS modules.\n" 2221251881Speter "\n" 2222251881Speter "Available subcommands:\n"); 2223251881Speter 2224251881Speter const char *fs_desc_start 2225251881Speter = _("The following repository back-end (FS) modules are available:\n\n"); 2226251881Speter 2227251881Speter svn_stringbuf_t *version_footer; 2228251881Speter 2229251881Speter version_footer = svn_stringbuf_create(fs_desc_start, pool); 2230251881Speter SVN_ERR(svn_fs_print_modules(version_footer, pool)); 2231251881Speter 2232251881Speter SVN_ERR(svn_opt_print_help4(os, "svnlook", 2233251881Speter opt_state ? opt_state->version : FALSE, 2234251881Speter opt_state ? opt_state->quiet : FALSE, 2235251881Speter opt_state ? opt_state->verbose : FALSE, 2236251881Speter version_footer->data, 2237251881Speter header, cmd_table, options_table, NULL, 2238251881Speter NULL, pool)); 2239251881Speter 2240251881Speter return SVN_NO_ERROR; 2241251881Speter} 2242251881Speter 2243251881Speter/* This implements `svn_opt_subcommand_t'. */ 2244251881Speterstatic svn_error_t * 2245251881Spetersubcommand_history(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2246251881Speter{ 2247251881Speter struct svnlook_opt_state *opt_state = baton; 2248251881Speter svnlook_ctxt_t *c; 2249251881Speter const char *path = (opt_state->arg1 ? opt_state->arg1 : "/"); 2250251881Speter 2251251881Speter if (opt_state->arg2 != NULL) 2252251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2253251881Speter _("Too many arguments given")); 2254251881Speter 2255251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2256251881Speter SVN_ERR(do_history(c, path, pool)); 2257251881Speter return SVN_NO_ERROR; 2258251881Speter} 2259251881Speter 2260251881Speter 2261251881Speter/* This implements `svn_opt_subcommand_t'. */ 2262251881Speterstatic svn_error_t * 2263251881Spetersubcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2264251881Speter{ 2265251881Speter struct svnlook_opt_state *opt_state = baton; 2266251881Speter svnlook_ctxt_t *c; 2267251881Speter svn_lock_t *lock; 2268251881Speter 2269251881Speter SVN_ERR(check_number_of_args(opt_state, 1)); 2270251881Speter 2271251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2272251881Speter 2273251881Speter SVN_ERR(svn_fs_get_lock(&lock, c->fs, opt_state->arg1, pool)); 2274251881Speter 2275251881Speter if (lock) 2276251881Speter { 2277251881Speter const char *cr_date, *exp_date = ""; 2278251881Speter int comment_lines = 0; 2279251881Speter 2280251881Speter cr_date = svn_time_to_human_cstring(lock->creation_date, pool); 2281251881Speter 2282251881Speter if (lock->expiration_date) 2283251881Speter exp_date = svn_time_to_human_cstring(lock->expiration_date, pool); 2284251881Speter 2285251881Speter if (lock->comment) 2286251881Speter comment_lines = svn_cstring_count_newlines(lock->comment) + 1; 2287251881Speter 2288251881Speter SVN_ERR(svn_cmdline_printf(pool, _("UUID Token: %s\n"), lock->token)); 2289251881Speter SVN_ERR(svn_cmdline_printf(pool, _("Owner: %s\n"), lock->owner)); 2290251881Speter SVN_ERR(svn_cmdline_printf(pool, _("Created: %s\n"), cr_date)); 2291251881Speter SVN_ERR(svn_cmdline_printf(pool, _("Expires: %s\n"), exp_date)); 2292251881Speter SVN_ERR(svn_cmdline_printf(pool, 2293251881Speter Q_("Comment (%i line):\n%s\n", 2294251881Speter "Comment (%i lines):\n%s\n", 2295251881Speter comment_lines), 2296251881Speter comment_lines, 2297251881Speter lock->comment ? lock->comment : "")); 2298251881Speter } 2299251881Speter 2300251881Speter return SVN_NO_ERROR; 2301251881Speter} 2302251881Speter 2303251881Speter 2304251881Speter/* This implements `svn_opt_subcommand_t'. */ 2305251881Speterstatic svn_error_t * 2306251881Spetersubcommand_info(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2307251881Speter{ 2308251881Speter struct svnlook_opt_state *opt_state = baton; 2309251881Speter svnlook_ctxt_t *c; 2310251881Speter 2311251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2312251881Speter 2313251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2314251881Speter SVN_ERR(do_author(c, pool)); 2315251881Speter SVN_ERR(do_date(c, pool)); 2316251881Speter SVN_ERR(do_log(c, TRUE, pool)); 2317251881Speter return SVN_NO_ERROR; 2318251881Speter} 2319251881Speter 2320251881Speter/* This implements `svn_opt_subcommand_t'. */ 2321251881Speterstatic svn_error_t * 2322251881Spetersubcommand_log(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2323251881Speter{ 2324251881Speter struct svnlook_opt_state *opt_state = baton; 2325251881Speter svnlook_ctxt_t *c; 2326251881Speter 2327251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2328251881Speter 2329251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2330251881Speter SVN_ERR(do_log(c, FALSE, pool)); 2331251881Speter return SVN_NO_ERROR; 2332251881Speter} 2333251881Speter 2334251881Speter/* This implements `svn_opt_subcommand_t'. */ 2335251881Speterstatic svn_error_t * 2336251881Spetersubcommand_pget(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2337251881Speter{ 2338251881Speter struct svnlook_opt_state *opt_state = baton; 2339251881Speter svnlook_ctxt_t *c; 2340251881Speter 2341251881Speter if (opt_state->arg1 == NULL) 2342251881Speter { 2343251881Speter return svn_error_createf 2344251881Speter (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, 2345251881Speter opt_state->revprop ? _("Missing propname argument") : 2346251881Speter _("Missing propname and repository path arguments")); 2347251881Speter } 2348251881Speter else if (!opt_state->revprop && opt_state->arg2 == NULL) 2349251881Speter { 2350251881Speter return svn_error_create 2351251881Speter (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, 2352251881Speter _("Missing propname or repository path argument")); 2353251881Speter } 2354251881Speter if ((opt_state->revprop && opt_state->arg2 != NULL) 2355251881Speter || os->ind < os->argc) 2356251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2357251881Speter _("Too many arguments given")); 2358251881Speter 2359251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2360251881Speter SVN_ERR(do_pget(c, opt_state->arg1, 2361251881Speter opt_state->revprop ? NULL : opt_state->arg2, 2362251881Speter opt_state->verbose, opt_state->show_inherited_props, 2363251881Speter pool)); 2364251881Speter return SVN_NO_ERROR; 2365251881Speter} 2366251881Speter 2367251881Speter/* This implements `svn_opt_subcommand_t'. */ 2368251881Speterstatic svn_error_t * 2369251881Spetersubcommand_plist(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2370251881Speter{ 2371251881Speter struct svnlook_opt_state *opt_state = baton; 2372251881Speter svnlook_ctxt_t *c; 2373251881Speter 2374251881Speter SVN_ERR(check_number_of_args(opt_state, opt_state->revprop ? 0 : 1)); 2375251881Speter 2376251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2377251881Speter SVN_ERR(do_plist(c, opt_state->revprop ? NULL : opt_state->arg1, 2378251881Speter opt_state->verbose, opt_state->xml, 2379251881Speter opt_state->show_inherited_props, pool)); 2380251881Speter return SVN_NO_ERROR; 2381251881Speter} 2382251881Speter 2383251881Speter/* This implements `svn_opt_subcommand_t'. */ 2384251881Speterstatic svn_error_t * 2385251881Spetersubcommand_tree(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2386251881Speter{ 2387251881Speter struct svnlook_opt_state *opt_state = baton; 2388251881Speter svnlook_ctxt_t *c; 2389251881Speter 2390251881Speter if (opt_state->arg2 != NULL) 2391251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2392251881Speter _("Too many arguments given")); 2393251881Speter 2394251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2395251881Speter SVN_ERR(do_tree(c, opt_state->arg1 ? opt_state->arg1 : "", 2396251881Speter opt_state->show_ids, opt_state->full_paths, 2397251881Speter ! opt_state->non_recursive, pool)); 2398251881Speter return SVN_NO_ERROR; 2399251881Speter} 2400251881Speter 2401251881Speter/* This implements `svn_opt_subcommand_t'. */ 2402251881Speterstatic svn_error_t * 2403251881Spetersubcommand_youngest(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2404251881Speter{ 2405251881Speter struct svnlook_opt_state *opt_state = baton; 2406251881Speter svnlook_ctxt_t *c; 2407251881Speter 2408251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2409251881Speter 2410251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2411251881Speter SVN_ERR(svn_cmdline_printf(pool, "%ld\n", c->rev_id)); 2412251881Speter return SVN_NO_ERROR; 2413251881Speter} 2414251881Speter 2415251881Speter/* This implements `svn_opt_subcommand_t'. */ 2416251881Speterstatic svn_error_t * 2417251881Spetersubcommand_uuid(apr_getopt_t *os, void *baton, apr_pool_t *pool) 2418251881Speter{ 2419251881Speter struct svnlook_opt_state *opt_state = baton; 2420251881Speter svnlook_ctxt_t *c; 2421251881Speter const char *uuid; 2422251881Speter 2423251881Speter SVN_ERR(check_number_of_args(opt_state, 0)); 2424251881Speter 2425251881Speter SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); 2426251881Speter SVN_ERR(svn_fs_get_uuid(c->fs, &uuid, pool)); 2427251881Speter SVN_ERR(svn_cmdline_printf(pool, "%s\n", uuid)); 2428251881Speter return SVN_NO_ERROR; 2429251881Speter} 2430251881Speter 2431251881Speter 2432251881Speter 2433251881Speter/*** Main. ***/ 2434251881Speter 2435251881Speterint 2436251881Spetermain(int argc, const char *argv[]) 2437251881Speter{ 2438251881Speter svn_error_t *err; 2439251881Speter apr_status_t apr_err; 2440251881Speter apr_pool_t *pool; 2441251881Speter 2442251881Speter const svn_opt_subcommand_desc2_t *subcommand = NULL; 2443251881Speter struct svnlook_opt_state opt_state; 2444251881Speter apr_getopt_t *os; 2445251881Speter int opt_id; 2446251881Speter apr_array_header_t *received_opts; 2447251881Speter int i; 2448251881Speter 2449251881Speter /* Initialize the app. */ 2450251881Speter if (svn_cmdline_init("svnlook", stderr) != EXIT_SUCCESS) 2451251881Speter return EXIT_FAILURE; 2452251881Speter 2453251881Speter /* Create our top-level pool. Use a separate mutexless allocator, 2454251881Speter * given this application is single threaded. 2455251881Speter */ 2456251881Speter pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); 2457251881Speter 2458251881Speter received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); 2459251881Speter 2460251881Speter /* Check library versions */ 2461251881Speter err = check_lib_versions(); 2462251881Speter if (err) 2463251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); 2464251881Speter 2465251881Speter /* Initialize the FS library. */ 2466251881Speter err = svn_fs_initialize(pool); 2467251881Speter if (err) 2468251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); 2469251881Speter 2470251881Speter if (argc <= 1) 2471251881Speter { 2472251881Speter SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); 2473251881Speter svn_pool_destroy(pool); 2474251881Speter return EXIT_FAILURE; 2475251881Speter } 2476251881Speter 2477251881Speter /* Initialize opt_state. */ 2478251881Speter memset(&opt_state, 0, sizeof(opt_state)); 2479251881Speter opt_state.rev = SVN_INVALID_REVNUM; 2480251881Speter 2481251881Speter /* Parse options. */ 2482251881Speter err = svn_cmdline__getopt_init(&os, argc, argv, pool); 2483251881Speter if (err) 2484251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); 2485251881Speter 2486251881Speter os->interleave = 1; 2487251881Speter while (1) 2488251881Speter { 2489251881Speter const char *opt_arg; 2490251881Speter 2491251881Speter /* Parse the next option. */ 2492251881Speter apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg); 2493251881Speter if (APR_STATUS_IS_EOF(apr_err)) 2494251881Speter break; 2495251881Speter else if (apr_err) 2496251881Speter { 2497251881Speter SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); 2498251881Speter svn_pool_destroy(pool); 2499251881Speter return EXIT_FAILURE; 2500251881Speter } 2501251881Speter 2502251881Speter /* Stash the option code in an array before parsing it. */ 2503251881Speter APR_ARRAY_PUSH(received_opts, int) = opt_id; 2504251881Speter 2505251881Speter switch (opt_id) 2506251881Speter { 2507251881Speter case 'r': 2508251881Speter { 2509251881Speter char *digits_end = NULL; 2510251881Speter opt_state.rev = strtol(opt_arg, &digits_end, 10); 2511251881Speter if ((! SVN_IS_VALID_REVNUM(opt_state.rev)) 2512251881Speter || (! digits_end) 2513251881Speter || *digits_end) 2514251881Speter SVN_INT_ERR(svn_error_create 2515251881Speter (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2516251881Speter _("Invalid revision number supplied"))); 2517251881Speter } 2518251881Speter break; 2519251881Speter 2520251881Speter case 't': 2521251881Speter opt_state.txn = opt_arg; 2522251881Speter break; 2523251881Speter 2524251881Speter case 'N': 2525251881Speter opt_state.non_recursive = TRUE; 2526251881Speter break; 2527251881Speter 2528251881Speter case 'v': 2529251881Speter opt_state.verbose = TRUE; 2530251881Speter break; 2531251881Speter 2532251881Speter case 'h': 2533251881Speter case '?': 2534251881Speter opt_state.help = TRUE; 2535251881Speter break; 2536251881Speter 2537251881Speter case 'q': 2538251881Speter opt_state.quiet = TRUE; 2539251881Speter break; 2540251881Speter 2541251881Speter case svnlook__revprop_opt: 2542251881Speter opt_state.revprop = TRUE; 2543251881Speter break; 2544251881Speter 2545251881Speter case svnlook__xml_opt: 2546251881Speter opt_state.xml = TRUE; 2547251881Speter break; 2548251881Speter 2549251881Speter case svnlook__version: 2550251881Speter opt_state.version = TRUE; 2551251881Speter break; 2552251881Speter 2553251881Speter case svnlook__show_ids: 2554251881Speter opt_state.show_ids = TRUE; 2555251881Speter break; 2556251881Speter 2557251881Speter case 'l': 2558251881Speter { 2559251881Speter char *end; 2560251881Speter opt_state.limit = strtol(opt_arg, &end, 10); 2561251881Speter if (end == opt_arg || *end != '\0') 2562251881Speter { 2563251881Speter err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 2564251881Speter _("Non-numeric limit argument given")); 2565251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); 2566251881Speter } 2567251881Speter if (opt_state.limit <= 0) 2568251881Speter { 2569251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 2570251881Speter _("Argument to --limit must be positive")); 2571251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); 2572251881Speter } 2573251881Speter } 2574251881Speter break; 2575251881Speter 2576251881Speter case svnlook__no_diff_deleted: 2577251881Speter opt_state.no_diff_deleted = TRUE; 2578251881Speter break; 2579251881Speter 2580251881Speter case svnlook__no_diff_added: 2581251881Speter opt_state.no_diff_added = TRUE; 2582251881Speter break; 2583251881Speter 2584251881Speter case svnlook__diff_copy_from: 2585251881Speter opt_state.diff_copy_from = TRUE; 2586251881Speter break; 2587251881Speter 2588251881Speter case svnlook__full_paths: 2589251881Speter opt_state.full_paths = TRUE; 2590251881Speter break; 2591251881Speter 2592251881Speter case svnlook__copy_info: 2593251881Speter opt_state.copy_info = TRUE; 2594251881Speter break; 2595251881Speter 2596251881Speter case 'x': 2597251881Speter opt_state.extensions = opt_arg; 2598251881Speter break; 2599251881Speter 2600251881Speter case svnlook__ignore_properties: 2601251881Speter opt_state.ignore_properties = TRUE; 2602251881Speter break; 2603251881Speter 2604251881Speter case svnlook__properties_only: 2605251881Speter opt_state.properties_only = TRUE; 2606251881Speter break; 2607251881Speter 2608251881Speter case svnlook__diff_cmd: 2609251881Speter opt_state.diff_cmd = opt_arg; 2610251881Speter break; 2611251881Speter 2612251881Speter case svnlook__show_inherited_props: 2613251881Speter opt_state.show_inherited_props = TRUE; 2614251881Speter break; 2615251881Speter 2616251881Speter default: 2617251881Speter SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); 2618251881Speter svn_pool_destroy(pool); 2619251881Speter return EXIT_FAILURE; 2620251881Speter 2621251881Speter } 2622251881Speter } 2623251881Speter 2624251881Speter /* The --transaction and --revision options may not co-exist. */ 2625251881Speter if ((opt_state.rev != SVN_INVALID_REVNUM) && opt_state.txn) 2626251881Speter SVN_INT_ERR(svn_error_create 2627251881Speter (SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 2628251881Speter _("The '--transaction' (-t) and '--revision' (-r) arguments " 2629251881Speter "cannot co-exist"))); 2630251881Speter 2631251881Speter /* The --show-inherited-props and --revprop options may not co-exist. */ 2632251881Speter if (opt_state.show_inherited_props && opt_state.revprop) 2633251881Speter SVN_INT_ERR(svn_error_create 2634251881Speter (SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 2635251881Speter _("Cannot use the '--show-inherited-props' option with the " 2636251881Speter "'--revprop' option"))); 2637251881Speter 2638251881Speter /* If the user asked for help, then the rest of the arguments are 2639251881Speter the names of subcommands to get help on (if any), or else they're 2640251881Speter just typos/mistakes. Whatever the case, the subcommand to 2641251881Speter actually run is subcommand_help(). */ 2642251881Speter if (opt_state.help) 2643251881Speter subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help"); 2644251881Speter 2645251881Speter /* If we're not running the `help' subcommand, then look for a 2646251881Speter subcommand in the first argument. */ 2647251881Speter if (subcommand == NULL) 2648251881Speter { 2649251881Speter if (os->ind >= os->argc) 2650251881Speter { 2651251881Speter if (opt_state.version) 2652251881Speter { 2653251881Speter /* Use the "help" subcommand to handle the "--version" option. */ 2654251881Speter static const svn_opt_subcommand_desc2_t pseudo_cmd = 2655251881Speter { "--version", subcommand_help, {0}, "", 2656251881Speter {svnlook__version, /* must accept its own option */ 2657251881Speter 'q', 'v', 2658251881Speter } }; 2659251881Speter 2660251881Speter subcommand = &pseudo_cmd; 2661251881Speter } 2662251881Speter else 2663251881Speter { 2664251881Speter svn_error_clear 2665251881Speter (svn_cmdline_fprintf(stderr, pool, 2666251881Speter _("Subcommand argument required\n"))); 2667251881Speter SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); 2668251881Speter svn_pool_destroy(pool); 2669251881Speter return EXIT_FAILURE; 2670251881Speter } 2671251881Speter } 2672251881Speter else 2673251881Speter { 2674251881Speter const char *first_arg = os->argv[os->ind++]; 2675251881Speter subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg); 2676251881Speter if (subcommand == NULL) 2677251881Speter { 2678251881Speter const char *first_arg_utf8; 2679251881Speter err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, 2680251881Speter pool); 2681251881Speter if (err) 2682251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); 2683251881Speter svn_error_clear( 2684251881Speter svn_cmdline_fprintf(stderr, pool, 2685251881Speter _("Unknown subcommand: '%s'\n"), 2686251881Speter first_arg_utf8)); 2687251881Speter SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); 2688251881Speter 2689251881Speter /* Be kind to people who try 'svnlook verify'. */ 2690251881Speter if (strcmp(first_arg_utf8, "verify") == 0) 2691251881Speter { 2692251881Speter svn_error_clear( 2693251881Speter svn_cmdline_fprintf(stderr, pool, 2694251881Speter _("Try 'svnadmin verify' instead.\n"))); 2695251881Speter } 2696251881Speter 2697251881Speter 2698251881Speter svn_pool_destroy(pool); 2699251881Speter return EXIT_FAILURE; 2700251881Speter } 2701251881Speter } 2702251881Speter } 2703251881Speter 2704251881Speter /* If there's a second argument, it's the repository. There may be 2705251881Speter more arguments following the repository; usually the next one is 2706251881Speter a path within the repository, or it's a propname and the one 2707251881Speter after that is the path. Since we don't know, we just call them 2708251881Speter arg1 and arg2, meaning the first and second arguments following 2709251881Speter the repository. */ 2710251881Speter if (subcommand->cmd_func != subcommand_help) 2711251881Speter { 2712251881Speter const char *repos_path = NULL; 2713251881Speter const char *arg1 = NULL, *arg2 = NULL; 2714251881Speter 2715251881Speter /* Get the repository. */ 2716251881Speter if (os->ind < os->argc) 2717251881Speter { 2718251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8(&repos_path, 2719251881Speter os->argv[os->ind++], 2720251881Speter pool)); 2721251881Speter repos_path = svn_dirent_internal_style(repos_path, pool); 2722251881Speter } 2723251881Speter 2724251881Speter if (repos_path == NULL) 2725251881Speter { 2726251881Speter svn_error_clear 2727251881Speter (svn_cmdline_fprintf(stderr, pool, 2728251881Speter _("Repository argument required\n"))); 2729251881Speter SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); 2730251881Speter svn_pool_destroy(pool); 2731251881Speter return EXIT_FAILURE; 2732251881Speter } 2733251881Speter else if (svn_path_is_url(repos_path)) 2734251881Speter { 2735251881Speter svn_error_clear 2736251881Speter (svn_cmdline_fprintf(stderr, pool, 2737251881Speter _("'%s' is a URL when it should be a path\n"), 2738251881Speter repos_path)); 2739251881Speter svn_pool_destroy(pool); 2740251881Speter return EXIT_FAILURE; 2741251881Speter } 2742251881Speter 2743251881Speter opt_state.repos_path = repos_path; 2744251881Speter 2745251881Speter /* Get next arg (arg1), if any. */ 2746251881Speter if (os->ind < os->argc) 2747251881Speter { 2748251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8 2749251881Speter (&arg1, os->argv[os->ind++], pool)); 2750251881Speter arg1 = svn_dirent_internal_style(arg1, pool); 2751251881Speter } 2752251881Speter opt_state.arg1 = arg1; 2753251881Speter 2754251881Speter /* Get next arg (arg2), if any. */ 2755251881Speter if (os->ind < os->argc) 2756251881Speter { 2757251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8 2758251881Speter (&arg2, os->argv[os->ind++], pool)); 2759251881Speter arg2 = svn_dirent_internal_style(arg2, pool); 2760251881Speter } 2761251881Speter opt_state.arg2 = arg2; 2762251881Speter } 2763251881Speter 2764251881Speter /* Check that the subcommand wasn't passed any inappropriate options. */ 2765251881Speter for (i = 0; i < received_opts->nelts; i++) 2766251881Speter { 2767251881Speter opt_id = APR_ARRAY_IDX(received_opts, i, int); 2768251881Speter 2769251881Speter /* All commands implicitly accept --help, so just skip over this 2770251881Speter when we see it. Note that we don't want to include this option 2771251881Speter in their "accepted options" list because it would be awfully 2772251881Speter redundant to display it in every commands' help text. */ 2773251881Speter if (opt_id == 'h' || opt_id == '?') 2774251881Speter continue; 2775251881Speter 2776251881Speter if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL)) 2777251881Speter { 2778251881Speter const char *optstr; 2779251881Speter const apr_getopt_option_t *badopt = 2780251881Speter svn_opt_get_option_from_code2(opt_id, options_table, subcommand, 2781251881Speter pool); 2782251881Speter svn_opt_format_option(&optstr, badopt, FALSE, pool); 2783251881Speter if (subcommand->name[0] == '-') 2784251881Speter SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); 2785251881Speter else 2786251881Speter svn_error_clear 2787251881Speter (svn_cmdline_fprintf 2788251881Speter (stderr, pool, 2789251881Speter _("Subcommand '%s' doesn't accept option '%s'\n" 2790251881Speter "Type 'svnlook help %s' for usage.\n"), 2791251881Speter subcommand->name, optstr, subcommand->name)); 2792251881Speter svn_pool_destroy(pool); 2793251881Speter return EXIT_FAILURE; 2794251881Speter } 2795251881Speter } 2796251881Speter 2797251881Speter /* Set up our cancellation support. */ 2798251881Speter apr_signal(SIGINT, signal_handler); 2799251881Speter#ifdef SIGBREAK 2800251881Speter /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ 2801251881Speter apr_signal(SIGBREAK, signal_handler); 2802251881Speter#endif 2803251881Speter#ifdef SIGHUP 2804251881Speter apr_signal(SIGHUP, signal_handler); 2805251881Speter#endif 2806251881Speter#ifdef SIGTERM 2807251881Speter apr_signal(SIGTERM, signal_handler); 2808251881Speter#endif 2809251881Speter 2810251881Speter#ifdef SIGPIPE 2811251881Speter /* Disable SIGPIPE generation for the platforms that have it. */ 2812251881Speter apr_signal(SIGPIPE, SIG_IGN); 2813251881Speter#endif 2814251881Speter 2815251881Speter#ifdef SIGXFSZ 2816251881Speter /* Disable SIGXFSZ generation for the platforms that have it, otherwise 2817251881Speter * working with large files when compiled against an APR that doesn't have 2818251881Speter * large file support will crash the program, which is uncool. */ 2819251881Speter apr_signal(SIGXFSZ, SIG_IGN); 2820251881Speter#endif 2821251881Speter 2822251881Speter /* Run the subcommand. */ 2823251881Speter err = (*subcommand->cmd_func)(os, &opt_state, pool); 2824251881Speter if (err) 2825251881Speter { 2826251881Speter /* For argument-related problems, suggest using the 'help' 2827251881Speter subcommand. */ 2828251881Speter if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS 2829251881Speter || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR) 2830251881Speter { 2831251881Speter err = svn_error_quick_wrap(err, 2832251881Speter _("Try 'svnlook help' for more info")); 2833251881Speter } 2834251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); 2835251881Speter } 2836251881Speter else 2837251881Speter { 2838251881Speter svn_pool_destroy(pool); 2839251881Speter /* Ensure everything is printed on stdout, so the user sees any 2840251881Speter print errors. */ 2841251881Speter SVN_INT_ERR(svn_cmdline_fflush(stdout)); 2842251881Speter return EXIT_SUCCESS; 2843251881Speter } 2844251881Speter} 2845