2
* ====================================================================
3
* Licensed to the Apache Software Foundation (ASF) under one
4
* or more contributor license agreements. See the NOTICE file
5
* distributed with this work for additional information
6
* regarding copyright ownership. The ASF licenses this file
7
* to you under the Apache License, Version 2.0 (the
8
* "License"); you may not use this file except in compliance
9
* with the License. You may obtain a copy of the License at
11
* http://www.apache.org/licenses/LICENSE-2.0
13
* Unless required by applicable law or agreed to in writing,
14
* software distributed under the License is distributed on an
15
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
* KIND, either express or implied. See the License for the
17
* specific language governing permissions and limitations
19
* ====================================================================
22
#include "svn_cmdline.h"
23
#include "svn_dirent_uri.h"
24
#include "svn_pools.h"
28
#include "svn_version.h"
30
#include "private/svn_opt_private.h"
32
#include "svn_private_config.h"
34
#define SVNVERSION_OPT_VERSION SVN_OPT_FIRST_LONGOPT_ID
38
version(svn_boolean_t quiet, apr_pool_t *pool)
40
return svn_opt_print_help3(NULL, "svnversion", TRUE, quiet, NULL, NULL,
41
NULL, NULL, NULL, NULL, pool);
45
usage(apr_pool_t *pool)
47
svn_error_clear(svn_cmdline_fprintf
48
(stderr, pool, _("Type 'svnversion --help' for usage.\n")));
54
help(const apr_getopt_option_t *options, apr_pool_t *pool)
59
_("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n\n"
60
" Produce a compact 'version number' for the working copy path\n"
61
" WC_PATH. TRAIL_URL is the trailing portion of the URL used to\n"
62
" determine if WC_PATH itself is switched (detection of switches\n"
63
" within WC_PATH does not rely on TRAIL_URL). The version number\n"
64
" is written to standard output. For example:\n"
66
" $ svnversion . /repos/svn/trunk\n"
69
" The version number will be a single number if the working\n"
70
" copy is single revision, unmodified, not switched and with\n"
71
" an URL that matches the TRAIL_URL argument. If the working\n"
72
" copy is unusual the version number will be more complex:\n"
74
" 4123:4168 mixed revision working copy\n"
75
" 4168M modified working copy\n"
76
" 4123S switched working copy\n"
77
" 4123P partial working copy, from a sparse checkout\n"
78
" 4123:4168MS mixed revision, modified, switched working copy\n"
80
" If WC_PATH is an unversioned path, the program will output\n"
81
" 'Unversioned directory' or 'Unversioned file'. If WC_PATH is\n"
82
" an added or copied or moved path, the program will output\n"
83
" 'Uncommitted local addition, copy or move'.\n"
85
" If invoked without arguments WC_PATH will be the current directory.\n"
87
"Valid options:\n")));
88
while (options->description)
91
svn_opt_format_option(&optstr, options, TRUE, pool);
92
svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
95
svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
100
/* Version compatibility check */
102
check_lib_versions(void)
104
static const svn_version_checklist_t checklist[] =
106
{ "svn_subr", svn_subr_version },
107
{ "svn_wc", svn_wc_version },
111
SVN_VERSION_DEFINE(my_version);
112
return svn_ver_check_list(&my_version, checklist);
116
* Why is this not an svn subcommand? I have this vague idea that it could
117
* be run as part of the build process, with the output embedded in the svn
118
* program. Obviously we don't want to have to run svn when building svn.
121
main(int argc, const char *argv[])
123
const char *wc_path, *trail_url;
124
const char *local_abspath;
125
apr_allocator_t *allocator;
127
svn_wc_revision_status_t *res;
128
svn_boolean_t no_newline = FALSE, committed = FALSE;
131
svn_wc_context_t *wc_ctx;
132
svn_boolean_t quiet = FALSE;
133
svn_boolean_t is_version = FALSE;
134
const apr_getopt_option_t options[] =
136
{"no-newline", 'n', 0, N_("do not output the trailing newline")},
137
{"committed", 'c', 0, N_("last changed rather than current revisions")},
138
{"help", 'h', 0, N_("display this help")},
139
{"version", SVNVERSION_OPT_VERSION, 0,
140
N_("show program version information")},
142
N_("no progress (only errors) to stderr")},
146
/* Initialize the app. */
147
if (svn_cmdline_init("svnversion", stderr) != EXIT_SUCCESS)
150
/* Create our top-level pool. Use a separate mutexless allocator,
151
* given this application is single threaded.
153
if (apr_allocator_create(&allocator))
156
apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
158
pool = svn_pool_create_ex(NULL, allocator);
159
apr_allocator_owner_set(allocator, pool);
161
/* Check library versions */
162
err = check_lib_versions();
164
return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
166
#if defined(WIN32) || defined(__CYGWIN__)
167
/* Set the working copy administrative directory name. */
168
if (getenv("SVN_ASP_DOT_NET_HACK"))
170
err = svn_wc_set_adm_dir("_svn", pool);
172
return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
176
err = svn_cmdline__getopt_init(&os, argc, argv, pool);
178
return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
185
apr_status_t status = apr_getopt_long(os, options, &opt, &arg);
186
if (APR_STATUS_IS_EOF(status))
188
if (status != APR_SUCCESS)
207
case SVNVERSION_OPT_VERSION:
218
SVN_INT_ERR(version(quiet, pool));
221
if (os->ind > argc || os->ind < argc - 2)
227
SVN_INT_ERR(svn_utf_cstring_to_utf8(&wc_path,
228
(os->ind < argc) ? os->argv[os->ind]
232
SVN_INT_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool));
233
SVN_INT_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool));
234
SVN_INT_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool));
236
if (os->ind+1 < argc)
237
SVN_INT_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1],
242
err = svn_wc_revision_status2(&res, wc_ctx, local_abspath, trail_url,
243
committed, NULL, NULL, pool, pool);
245
if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
246
|| err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
248
svn_node_kind_t kind;
249
svn_boolean_t special;
251
svn_error_clear(err);
253
SVN_INT_ERR(svn_io_check_special_path(local_abspath, &kind, &special,
257
SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"),
258
no_newline ? "" : "\n"));
259
else if (kind == svn_node_dir)
260
SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"),
261
no_newline ? "" : "\n"));
262
else if (kind == svn_node_file)
263
SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"),
264
no_newline ? "" : "\n"));
267
SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
268
kind == svn_node_none
269
? _("'%s' doesn't exist\n")
270
: _("'%s' is of unknown type\n"),
271
svn_dirent_local_style(local_abspath,
273
svn_pool_destroy(pool);
276
svn_pool_destroy(pool);
282
if (! SVN_IS_VALID_REVNUM(res->min_rev))
284
/* Local uncommitted modifications, no revision info was found. */
285
SVN_INT_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, "
287
no_newline ? "" : "\n"));
288
svn_pool_destroy(pool);
292
/* Build compact '123[:456]M?S?' string. */
293
SVN_INT_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev));
294
if (res->min_rev != res->max_rev)
295
SVN_INT_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev));
297
SVN_INT_ERR(svn_cmdline_fputs("M", stdout, pool));
299
SVN_INT_ERR(svn_cmdline_fputs("S", stdout, pool));
300
if (res->sparse_checkout)
301
SVN_INT_ERR(svn_cmdline_fputs("P", stdout, pool));
304
SVN_INT_ERR(svn_cmdline_fputs("\n", stdout, pool));
306
svn_pool_destroy(pool);
308
/* Flush stdout to make sure that the user will see any printing errors. */
309
SVN_INT_ERR(svn_cmdline_fflush(stdout));