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