1251881Speter/* 2251881Speter * ==================================================================== 3251881Speter * Licensed to the Apache Software Foundation (ASF) under one 4251881Speter * or more contributor license agreements. See the NOTICE file 5251881Speter * distributed with this work for additional information 6251881Speter * regarding copyright ownership. The ASF licenses this file 7251881Speter * to you under the Apache License, Version 2.0 (the 8251881Speter * "License"); you may not use this file except in compliance 9251881Speter * with the License. You may obtain a copy of the License at 10251881Speter * 11251881Speter * http://www.apache.org/licenses/LICENSE-2.0 12251881Speter * 13251881Speter * Unless required by applicable law or agreed to in writing, 14251881Speter * software distributed under the License is distributed on an 15251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16251881Speter * KIND, either express or implied. See the License for the 17251881Speter * specific language governing permissions and limitations 18251881Speter * under the License. 19251881Speter * ==================================================================== 20251881Speter */ 21251881Speter 22251881Speter#include "svn_cmdline.h" 23251881Speter#include "svn_dirent_uri.h" 24251881Speter#include "svn_pools.h" 25251881Speter#include "svn_wc.h" 26251881Speter#include "svn_utf.h" 27251881Speter#include "svn_opt.h" 28251881Speter#include "svn_version.h" 29251881Speter 30251881Speter#include "private/svn_opt_private.h" 31251881Speter#include "private/svn_cmdline_private.h" 32262253Speter#include "private/svn_subr_private.h" 33251881Speter 34251881Speter#include "svn_private_config.h" 35251881Speter 36251881Speter#define SVNVERSION_OPT_VERSION SVN_OPT_FIRST_LONGOPT_ID 37251881Speter 38251881Speter 39251881Speterstatic svn_error_t * 40251881Speterversion(svn_boolean_t quiet, apr_pool_t *pool) 41251881Speter{ 42251881Speter return svn_opt_print_help4(NULL, "svnversion", TRUE, quiet, FALSE, 43251881Speter NULL, NULL, NULL, NULL, NULL, NULL, pool); 44251881Speter} 45251881Speter 46251881Speterstatic void 47251881Speterusage(apr_pool_t *pool) 48251881Speter{ 49251881Speter svn_error_clear(svn_cmdline_fprintf 50251881Speter (stderr, pool, _("Type 'svnversion --help' for usage.\n"))); 51251881Speter exit(1); 52251881Speter} 53251881Speter 54251881Speter 55251881Speterstatic void 56251881Speterhelp(const apr_getopt_option_t *options, apr_pool_t *pool) 57251881Speter{ 58251881Speter svn_error_clear 59251881Speter (svn_cmdline_fprintf 60251881Speter (stdout, pool, 61251881Speter _("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n\n" 62251881Speter " Produce a compact version identifier for the working copy path\n" 63251881Speter " WC_PATH. TRAIL_URL is the trailing portion of the URL used to\n" 64251881Speter " determine if WC_PATH itself is switched (detection of switches\n" 65251881Speter " within WC_PATH does not rely on TRAIL_URL). The version identifier\n" 66251881Speter " is written to standard output. For example:\n" 67251881Speter "\n" 68251881Speter " $ svnversion . /repos/svn/trunk\n" 69251881Speter " 4168\n" 70251881Speter "\n" 71251881Speter " The version identifier will be a single number if the working\n" 72251881Speter " copy is single revision, unmodified, not switched and with\n" 73251881Speter " a URL that matches the TRAIL_URL argument. If the working\n" 74251881Speter " copy is unusual the version identifier will be more complex:\n" 75251881Speter "\n" 76251881Speter " 4123:4168 mixed revision working copy\n" 77251881Speter " 4168M modified working copy\n" 78251881Speter " 4123S switched working copy\n" 79251881Speter " 4123P partial working copy, from a sparse checkout\n" 80251881Speter " 4123:4168MS mixed revision, modified, switched working copy\n" 81251881Speter "\n" 82251881Speter " If WC_PATH is an unversioned path, the program will output\n" 83251881Speter " 'Unversioned directory' or 'Unversioned file'. If WC_PATH is\n" 84251881Speter " an added or copied or moved path, the program will output\n" 85251881Speter " 'Uncommitted local addition, copy or move'.\n" 86251881Speter "\n" 87251881Speter " If invoked without arguments WC_PATH will be the current directory.\n" 88251881Speter "\n" 89251881Speter "Valid options:\n"))); 90251881Speter while (options->description) 91251881Speter { 92251881Speter const char *optstr; 93251881Speter svn_opt_format_option(&optstr, options, TRUE, pool); 94251881Speter svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr)); 95251881Speter ++options; 96251881Speter } 97251881Speter svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n")); 98251881Speter exit(0); 99251881Speter} 100251881Speter 101251881Speter 102251881Speter/* Version compatibility check */ 103251881Speterstatic svn_error_t * 104251881Spetercheck_lib_versions(void) 105251881Speter{ 106251881Speter static const svn_version_checklist_t checklist[] = 107251881Speter { 108251881Speter { "svn_subr", svn_subr_version }, 109251881Speter { "svn_wc", svn_wc_version }, 110251881Speter { NULL, NULL } 111251881Speter }; 112251881Speter SVN_VERSION_DEFINE(my_version); 113251881Speter 114262253Speter return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); 115251881Speter} 116251881Speter 117251881Speter/* 118251881Speter * Why is this not an svn subcommand? I have this vague idea that it could 119251881Speter * be run as part of the build process, with the output embedded in the svn 120251881Speter * program. Obviously we don't want to have to run svn when building svn. 121251881Speter */ 122251881Speterint 123251881Spetermain(int argc, const char *argv[]) 124251881Speter{ 125251881Speter const char *wc_path, *trail_url; 126251881Speter const char *local_abspath; 127251881Speter apr_pool_t *pool; 128251881Speter svn_wc_revision_status_t *res; 129251881Speter svn_boolean_t no_newline = FALSE, committed = FALSE; 130251881Speter svn_error_t *err; 131251881Speter apr_getopt_t *os; 132251881Speter svn_wc_context_t *wc_ctx; 133251881Speter svn_boolean_t quiet = FALSE; 134251881Speter svn_boolean_t is_version = FALSE; 135251881Speter const apr_getopt_option_t options[] = 136251881Speter { 137251881Speter {"no-newline", 'n', 0, N_("do not output the trailing newline")}, 138251881Speter {"committed", 'c', 0, N_("last changed rather than current revisions")}, 139251881Speter {"help", 'h', 0, N_("display this help")}, 140251881Speter {"version", SVNVERSION_OPT_VERSION, 0, 141251881Speter N_("show program version information")}, 142251881Speter {"quiet", 'q', 0, 143251881Speter N_("no progress (only errors) to stderr")}, 144251881Speter {0, 0, 0, 0} 145251881Speter }; 146251881Speter 147251881Speter /* Initialize the app. */ 148251881Speter if (svn_cmdline_init("svnversion", stderr) != EXIT_SUCCESS) 149251881Speter return EXIT_FAILURE; 150251881Speter 151251881Speter /* Create our top-level pool. Use a separate mutexless allocator, 152251881Speter * given this application is single threaded. 153251881Speter */ 154251881Speter pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); 155251881Speter 156251881Speter /* Check library versions */ 157251881Speter err = check_lib_versions(); 158251881Speter if (err) 159251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnversion: "); 160251881Speter 161251881Speter#if defined(WIN32) || defined(__CYGWIN__) 162251881Speter /* Set the working copy administrative directory name. */ 163251881Speter if (getenv("SVN_ASP_DOT_NET_HACK")) 164251881Speter { 165251881Speter err = svn_wc_set_adm_dir("_svn", pool); 166251881Speter if (err) 167251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnversion: "); 168251881Speter } 169251881Speter#endif 170251881Speter 171251881Speter err = svn_cmdline__getopt_init(&os, argc, argv, pool); 172251881Speter if (err) 173251881Speter return svn_cmdline_handle_exit_error(err, pool, "svnversion: "); 174251881Speter 175251881Speter os->interleave = 1; 176251881Speter while (1) 177251881Speter { 178251881Speter int opt; 179251881Speter const char *arg; 180251881Speter apr_status_t status = apr_getopt_long(os, options, &opt, &arg); 181251881Speter if (APR_STATUS_IS_EOF(status)) 182251881Speter break; 183251881Speter if (status != APR_SUCCESS) 184251881Speter usage(pool); /* this will exit() */ 185251881Speter 186251881Speter switch (opt) 187251881Speter { 188251881Speter case 'n': 189251881Speter no_newline = TRUE; 190251881Speter break; 191251881Speter case 'c': 192251881Speter committed = TRUE; 193251881Speter break; 194251881Speter case 'q': 195251881Speter quiet = TRUE; 196251881Speter break; 197251881Speter case 'h': 198251881Speter help(options, pool); 199251881Speter break; 200251881Speter case SVNVERSION_OPT_VERSION: 201251881Speter is_version = TRUE; 202251881Speter break; 203251881Speter default: 204251881Speter usage(pool); /* this will exit() */ 205251881Speter } 206251881Speter } 207251881Speter 208251881Speter if (is_version) 209251881Speter { 210251881Speter SVN_INT_ERR(version(quiet, pool)); 211251881Speter exit(0); 212251881Speter } 213251881Speter if (os->ind > argc || os->ind < argc - 2) 214251881Speter usage(pool); /* this will exit() */ 215251881Speter 216251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8(&wc_path, 217251881Speter (os->ind < argc) ? os->argv[os->ind] 218251881Speter : ".", 219251881Speter pool)); 220251881Speter 221251881Speter SVN_INT_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool)); 222251881Speter SVN_INT_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool)); 223251881Speter SVN_INT_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool)); 224251881Speter 225251881Speter if (os->ind+1 < argc) 226251881Speter SVN_INT_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1], 227251881Speter pool)); 228251881Speter else 229251881Speter trail_url = NULL; 230251881Speter 231251881Speter err = svn_wc_revision_status2(&res, wc_ctx, local_abspath, trail_url, 232251881Speter committed, NULL, NULL, pool, pool); 233251881Speter 234251881Speter if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND 235251881Speter || err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)) 236251881Speter { 237251881Speter svn_node_kind_t kind; 238251881Speter svn_boolean_t special; 239251881Speter 240251881Speter svn_error_clear(err); 241251881Speter 242251881Speter SVN_INT_ERR(svn_io_check_special_path(local_abspath, &kind, &special, 243251881Speter pool)); 244251881Speter 245251881Speter if (special) 246251881Speter SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"), 247251881Speter no_newline ? "" : "\n")); 248251881Speter else if (kind == svn_node_dir) 249251881Speter SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"), 250251881Speter no_newline ? "" : "\n")); 251251881Speter else if (kind == svn_node_file) 252251881Speter SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"), 253251881Speter no_newline ? "" : "\n")); 254251881Speter else 255251881Speter { 256251881Speter SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool, 257251881Speter kind == svn_node_none 258251881Speter ? _("'%s' doesn't exist\n") 259251881Speter : _("'%s' is of unknown type\n"), 260251881Speter svn_dirent_local_style(local_abspath, 261251881Speter pool))); 262251881Speter svn_pool_destroy(pool); 263251881Speter return EXIT_FAILURE; 264251881Speter } 265251881Speter svn_pool_destroy(pool); 266251881Speter return EXIT_SUCCESS; 267251881Speter } 268251881Speter 269251881Speter SVN_INT_ERR(err); 270251881Speter 271251881Speter if (! SVN_IS_VALID_REVNUM(res->min_rev)) 272251881Speter { 273251881Speter /* Local uncommitted modifications, no revision info was found. */ 274251881Speter SVN_INT_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, " 275251881Speter "copy or move%s"), 276251881Speter no_newline ? "" : "\n")); 277251881Speter svn_pool_destroy(pool); 278251881Speter return EXIT_SUCCESS; 279251881Speter } 280251881Speter 281251881Speter /* Build compact '123[:456]M?S?' string. */ 282251881Speter SVN_INT_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev)); 283251881Speter if (res->min_rev != res->max_rev) 284251881Speter SVN_INT_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev)); 285251881Speter if (res->modified) 286251881Speter SVN_INT_ERR(svn_cmdline_fputs("M", stdout, pool)); 287251881Speter if (res->switched) 288251881Speter SVN_INT_ERR(svn_cmdline_fputs("S", stdout, pool)); 289251881Speter if (res->sparse_checkout) 290251881Speter SVN_INT_ERR(svn_cmdline_fputs("P", stdout, pool)); 291251881Speter 292251881Speter if (! no_newline) 293251881Speter SVN_INT_ERR(svn_cmdline_fputs("\n", stdout, pool)); 294251881Speter 295251881Speter svn_pool_destroy(pool); 296251881Speter 297251881Speter /* Flush stdout to make sure that the user will see any printing errors. */ 298251881Speter SVN_INT_ERR(svn_cmdline_fflush(stdout)); 299251881Speter 300251881Speter return EXIT_SUCCESS; 301251881Speter} 302