]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svnversion/svnversion.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / svnversion / svnversion.c
1 /*
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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
18  *    under the License.
19  * ====================================================================
20  */
21
22 #include "svn_cmdline.h"
23 #include "svn_dirent_uri.h"
24 #include "svn_pools.h"
25 #include "svn_wc.h"
26 #include "svn_utf.h"
27 #include "svn_opt.h"
28 #include "svn_version.h"
29
30 #include "private/svn_opt_private.h"
31 #include "private/svn_cmdline_private.h"
32
33 #include "svn_private_config.h"
34
35 #define SVNVERSION_OPT_VERSION SVN_OPT_FIRST_LONGOPT_ID
36
37
38 static svn_error_t *
39 version(svn_boolean_t quiet, apr_pool_t *pool)
40 {
41   return svn_opt_print_help4(NULL, "svnversion", TRUE, quiet, FALSE,
42                              NULL, NULL, NULL, NULL, NULL, NULL, pool);
43 }
44
45 static void
46 usage(apr_pool_t *pool)
47 {
48   svn_error_clear(svn_cmdline_fprintf
49                   (stderr, pool, _("Type 'svnversion --help' for usage.\n")));
50 }
51
52
53 static void
54 help(const apr_getopt_option_t *options, apr_pool_t *pool)
55 {
56   svn_error_clear
57     (svn_cmdline_fprintf
58      (stdout, pool,
59       _("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n"
60         "Subversion working copy identification tool.\n"
61         "Type 'svnversion --version' to see the program version.\n"
62         "\n"
63         "  Produce a compact version identifier for the working copy path\n"
64         "  WC_PATH.  TRAIL_URL is the trailing portion of the URL used to\n"
65         "  determine if WC_PATH itself is switched (detection of switches\n"
66         "  within WC_PATH does not rely on TRAIL_URL).  The version identifier\n"
67         "  is written to standard output.  For example:\n"
68         "\n"
69         "    $ svnversion . /repos/svn/trunk\n"
70         "    4168\n"
71         "\n"
72         "  The version identifier will be a single number if the working\n"
73         "  copy is single revision, unmodified, not switched and with\n"
74         "  a URL that matches the TRAIL_URL argument.  If the working\n"
75         "  copy is unusual the version identifier will be more complex:\n"
76         "\n"
77         "   4123:4168     mixed revision working copy\n"
78         "   4168M         modified working copy\n"
79         "   4123S         switched working copy\n"
80         "   4123P         partial working copy, from a sparse checkout\n"
81         "   4123:4168MS   mixed revision, modified, switched working copy\n"
82         "\n"
83         "  If WC_PATH is an unversioned path, the program will output\n"
84         "  'Unversioned directory' or 'Unversioned file'.  If WC_PATH is\n"
85         "  an added or copied or moved path, the program will output\n"
86         "  'Uncommitted local addition, copy or move'.\n"
87         "\n"
88         "  If invoked without arguments WC_PATH will be the current directory.\n"
89         "\n"
90         "Valid options:\n")));
91   while (options->description)
92     {
93       const char *optstr;
94       svn_opt_format_option(&optstr, options, TRUE, pool);
95       svn_error_clear(svn_cmdline_fprintf(stdout, pool, "  %s\n", optstr));
96       ++options;
97     }
98   svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
99 }
100
101
102 /* Version compatibility check */
103 static svn_error_t *
104 check_lib_versions(void)
105 {
106   static const svn_version_checklist_t checklist[] =
107     {
108       { "svn_subr",   svn_subr_version },
109       { "svn_wc",     svn_wc_version },
110       { NULL, NULL }
111     };
112   SVN_VERSION_DEFINE(my_version);
113
114   return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
115 }
116
117 /*
118  * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
119  * either return an error to be displayed, or set *EXIT_CODE to non-zero and
120  * return SVN_NO_ERROR.
121  *
122  * Why is this not an svn subcommand?  I have this vague idea that it could
123  * be run as part of the build process, with the output embedded in the svn
124  * program.  Obviously we don't want to have to run svn when building svn.
125  */
126 static svn_error_t *
127 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
128 {
129   const char *wc_path, *trail_url;
130   const char *local_abspath;
131   svn_wc_revision_status_t *res;
132   svn_boolean_t no_newline = FALSE, committed = FALSE;
133   svn_error_t *err;
134   apr_getopt_t *os;
135   svn_wc_context_t *wc_ctx;
136   svn_boolean_t quiet = FALSE;
137   svn_boolean_t is_version = FALSE;
138   const apr_getopt_option_t options[] =
139     {
140       {"no-newline", 'n', 0, N_("do not output the trailing newline")},
141       {"committed",  'c', 0, N_("last changed rather than current revisions")},
142       {"help", 'h', 0, N_("display this help")},
143       {"version", SVNVERSION_OPT_VERSION, 0,
144        N_("show program version information")},
145       {"quiet",         'q', 0,
146        N_("no progress (only errors) to stderr")},
147       {0,             0,  0,  0}
148     };
149
150   /* Check library versions */
151   SVN_ERR(check_lib_versions());
152
153 #if defined(WIN32) || defined(__CYGWIN__)
154   /* Set the working copy administrative directory name. */
155   if (getenv("SVN_ASP_DOT_NET_HACK"))
156     {
157       SVN_ERR(svn_wc_set_adm_dir("_svn", pool));
158     }
159 #endif
160
161   SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
162
163   os->interleave = 1;
164   while (1)
165     {
166       int opt;
167       const char *arg;
168       apr_status_t status = apr_getopt_long(os, options, &opt, &arg);
169       if (APR_STATUS_IS_EOF(status))
170         break;
171       if (status != APR_SUCCESS)
172         {
173           *exit_code = EXIT_FAILURE;
174           usage(pool);
175           return SVN_NO_ERROR;
176         }
177
178       switch (opt)
179         {
180         case 'n':
181           no_newline = TRUE;
182           break;
183         case 'c':
184           committed = TRUE;
185           break;
186         case 'q':
187           quiet = TRUE;
188           break;
189         case 'h':
190           help(options, pool);
191           return SVN_NO_ERROR;
192         case SVNVERSION_OPT_VERSION:
193           is_version = TRUE;
194           break;
195         default:
196           *exit_code = EXIT_FAILURE;
197           usage(pool);
198           return SVN_NO_ERROR;
199         }
200     }
201
202   if (is_version)
203     {
204       SVN_ERR(version(quiet, pool));
205       return SVN_NO_ERROR;
206     }
207   if (os->ind > argc || os->ind < argc - 2)
208     {
209       *exit_code = EXIT_FAILURE;
210       usage(pool);
211       return SVN_NO_ERROR;
212     }
213
214   SVN_ERR(svn_utf_cstring_to_utf8(&wc_path,
215                                   (os->ind < argc) ? os->argv[os->ind] : ".",
216                                   pool));
217
218   SVN_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool));
219   SVN_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool));
220   SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool));
221
222   if (os->ind+1 < argc)
223     SVN_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1], pool));
224   else
225     trail_url = NULL;
226
227   err = svn_wc_revision_status2(&res, wc_ctx, local_abspath, trail_url,
228                                 committed, NULL, NULL, pool, pool);
229
230   if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
231               || err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
232     {
233       svn_node_kind_t kind;
234       svn_boolean_t special;
235
236       svn_error_clear(err);
237
238       SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &special, pool));
239
240       if (special)
241         SVN_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"),
242                                    no_newline ? "" : "\n"));
243       else if (kind == svn_node_dir)
244         SVN_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"),
245                                    no_newline ? "" : "\n"));
246       else if (kind == svn_node_file)
247         SVN_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"),
248                                    no_newline ? "" : "\n"));
249       else
250         {
251           SVN_ERR(svn_cmdline_fprintf(stderr, pool,
252                                       kind == svn_node_none
253                                        ? _("'%s' doesn't exist\n")
254                                        : _("'%s' is of unknown type\n"),
255                                       svn_dirent_local_style(local_abspath,
256                                                              pool)));
257           *exit_code = EXIT_FAILURE;
258           return SVN_NO_ERROR;
259         }
260       return SVN_NO_ERROR;
261     }
262
263   SVN_ERR(err);
264
265   if (! SVN_IS_VALID_REVNUM(res->min_rev))
266     {
267       /* Local uncommitted modifications, no revision info was found. */
268       SVN_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, "
269                                          "copy or move%s"),
270                                  no_newline ? "" : "\n"));
271       return SVN_NO_ERROR;
272     }
273
274   /* Build compact '123[:456]M?S?' string. */
275   SVN_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev));
276   if (res->min_rev != res->max_rev)
277     SVN_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev));
278   if (res->modified)
279     SVN_ERR(svn_cmdline_fputs("M", stdout, pool));
280   if (res->switched)
281     SVN_ERR(svn_cmdline_fputs("S", stdout, pool));
282   if (res->sparse_checkout)
283     SVN_ERR(svn_cmdline_fputs("P", stdout, pool));
284
285   if (! no_newline)
286     SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
287
288   return SVN_NO_ERROR;
289 }
290
291 int
292 main(int argc, const char *argv[])
293 {
294   apr_pool_t *pool;
295   int exit_code = EXIT_SUCCESS;
296   svn_error_t *err;
297
298   /* Initialize the app. */
299   if (svn_cmdline_init("svnversion", stderr) != EXIT_SUCCESS)
300     return EXIT_FAILURE;
301
302   /* Create our top-level pool.  Use a separate mutexless allocator,
303    * given this application is single threaded.
304    */
305   pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
306
307   err = sub_main(&exit_code, argc, argv, pool);
308
309   /* Flush stdout and report if it fails. It would be flushed on exit anyway
310      but this makes sure that output is not silently lost if it fails. */
311   err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
312
313   if (err)
314     {
315       exit_code = EXIT_FAILURE;
316       svn_cmdline_handle_exit_error(err, NULL, "svnversion: ");
317     }
318
319   svn_pool_destroy(pool);
320   return exit_code;
321 }