]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/svnversion/svnversion.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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   exit(1);
51 }
52
53
54 static void
55 help(const apr_getopt_option_t *options, apr_pool_t *pool)
56 {
57   svn_error_clear
58     (svn_cmdline_fprintf
59      (stdout, pool,
60       _("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n\n"
61         "  Produce a compact version identifier for the working copy path\n"
62         "  WC_PATH.  TRAIL_URL is the trailing portion of the URL used to\n"
63         "  determine if WC_PATH itself is switched (detection of switches\n"
64         "  within WC_PATH does not rely on TRAIL_URL).  The version identifier\n"
65         "  is written to standard output.  For example:\n"
66         "\n"
67         "    $ svnversion . /repos/svn/trunk\n"
68         "    4168\n"
69         "\n"
70         "  The version identifier will be a single number if the working\n"
71         "  copy is single revision, unmodified, not switched and with\n"
72         "  a URL that matches the TRAIL_URL argument.  If the working\n"
73         "  copy is unusual the version identifier will be more complex:\n"
74         "\n"
75         "   4123:4168     mixed revision working copy\n"
76         "   4168M         modified working copy\n"
77         "   4123S         switched working copy\n"
78         "   4123P         partial working copy, from a sparse checkout\n"
79         "   4123:4168MS   mixed revision, modified, switched working copy\n"
80         "\n"
81         "  If WC_PATH is an unversioned path, the program will output\n"
82         "  'Unversioned directory' or 'Unversioned file'.  If WC_PATH is\n"
83         "  an added or copied or moved path, the program will output\n"
84         "  'Uncommitted local addition, copy or move'.\n"
85         "\n"
86         "  If invoked without arguments WC_PATH will be the current directory.\n"
87         "\n"
88         "Valid options:\n")));
89   while (options->description)
90     {
91       const char *optstr;
92       svn_opt_format_option(&optstr, options, TRUE, pool);
93       svn_error_clear(svn_cmdline_fprintf(stdout, pool, "  %s\n", optstr));
94       ++options;
95     }
96   svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
97   exit(0);
98 }
99
100
101 /* Version compatibility check */
102 static svn_error_t *
103 check_lib_versions(void)
104 {
105   static const svn_version_checklist_t checklist[] =
106     {
107       { "svn_subr",   svn_subr_version },
108       { "svn_wc",     svn_wc_version },
109       { NULL, NULL }
110     };
111   SVN_VERSION_DEFINE(my_version);
112
113   return svn_ver_check_list(&my_version, checklist);
114 }
115
116 /*
117  * Why is this not an svn subcommand?  I have this vague idea that it could
118  * be run as part of the build process, with the output embedded in the svn
119  * program.  Obviously we don't want to have to run svn when building svn.
120  */
121 int
122 main(int argc, const char *argv[])
123 {
124   const char *wc_path, *trail_url;
125   const char *local_abspath;
126   apr_pool_t *pool;
127   svn_wc_revision_status_t *res;
128   svn_boolean_t no_newline = FALSE, committed = FALSE;
129   svn_error_t *err;
130   apr_getopt_t *os;
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[] =
135     {
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")},
141       {"quiet",         'q', 0,
142        N_("no progress (only errors) to stderr")},
143       {0,             0,  0,  0}
144     };
145
146   /* Initialize the app. */
147   if (svn_cmdline_init("svnversion", stderr) != EXIT_SUCCESS)
148     return EXIT_FAILURE;
149
150   /* Create our top-level pool.  Use a separate mutexless allocator,
151    * given this application is single threaded.
152    */
153   pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
154
155   /* Check library versions */
156   err = check_lib_versions();
157   if (err)
158     return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
159
160 #if defined(WIN32) || defined(__CYGWIN__)
161   /* Set the working copy administrative directory name. */
162   if (getenv("SVN_ASP_DOT_NET_HACK"))
163     {
164       err = svn_wc_set_adm_dir("_svn", pool);
165       if (err)
166         return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
167     }
168 #endif
169
170   err = svn_cmdline__getopt_init(&os, argc, argv, pool);
171   if (err)
172     return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
173
174   os->interleave = 1;
175   while (1)
176     {
177       int opt;
178       const char *arg;
179       apr_status_t status = apr_getopt_long(os, options, &opt, &arg);
180       if (APR_STATUS_IS_EOF(status))
181         break;
182       if (status != APR_SUCCESS)
183         usage(pool);  /* this will exit() */
184
185       switch (opt)
186         {
187         case 'n':
188           no_newline = TRUE;
189           break;
190         case 'c':
191           committed = TRUE;
192           break;
193         case 'q':
194           quiet = TRUE;
195           break;
196         case 'h':
197           help(options, pool);
198           break;
199         case SVNVERSION_OPT_VERSION:
200           is_version = TRUE;
201           break;
202         default:
203           usage(pool);  /* this will exit() */
204         }
205     }
206
207   if (is_version)
208     {
209       SVN_INT_ERR(version(quiet, pool));
210       exit(0);
211     }
212   if (os->ind > argc || os->ind < argc - 2)
213     usage(pool);  /* this will exit() */
214
215   SVN_INT_ERR(svn_utf_cstring_to_utf8(&wc_path,
216                                       (os->ind < argc) ? os->argv[os->ind]
217                                                        : ".",
218                                       pool));
219
220   SVN_INT_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool));
221   SVN_INT_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool));
222   SVN_INT_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool));
223
224   if (os->ind+1 < argc)
225     SVN_INT_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1],
226                                         pool));
227   else
228     trail_url = NULL;
229
230   err = svn_wc_revision_status2(&res, wc_ctx, local_abspath, trail_url,
231                                 committed, NULL, NULL, pool, pool);
232
233   if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
234               || err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
235     {
236       svn_node_kind_t kind;
237       svn_boolean_t special;
238
239       svn_error_clear(err);
240
241       SVN_INT_ERR(svn_io_check_special_path(local_abspath, &kind, &special,
242                                             pool));
243
244       if (special)
245         SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"),
246                                        no_newline ? "" : "\n"));
247       else if (kind == svn_node_dir)
248         SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"),
249                                        no_newline ? "" : "\n"));
250       else if (kind == svn_node_file)
251         SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"),
252                                        no_newline ? "" : "\n"));
253       else
254         {
255           SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
256                                           kind == svn_node_none
257                                            ? _("'%s' doesn't exist\n")
258                                            : _("'%s' is of unknown type\n"),
259                                           svn_dirent_local_style(local_abspath,
260                                                                  pool)));
261           svn_pool_destroy(pool);
262           return EXIT_FAILURE;
263         }
264       svn_pool_destroy(pool);
265       return EXIT_SUCCESS;
266     }
267
268   SVN_INT_ERR(err);
269
270   if (! SVN_IS_VALID_REVNUM(res->min_rev))
271     {
272       /* Local uncommitted modifications, no revision info was found. */
273       SVN_INT_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, "
274                                              "copy or move%s"),
275                                              no_newline ? "" : "\n"));
276       svn_pool_destroy(pool);
277       return EXIT_SUCCESS;
278     }
279
280   /* Build compact '123[:456]M?S?' string. */
281   SVN_INT_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev));
282   if (res->min_rev != res->max_rev)
283     SVN_INT_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev));
284   if (res->modified)
285     SVN_INT_ERR(svn_cmdline_fputs("M", stdout, pool));
286   if (res->switched)
287     SVN_INT_ERR(svn_cmdline_fputs("S", stdout, pool));
288   if (res->sparse_checkout)
289     SVN_INT_ERR(svn_cmdline_fputs("P", stdout, pool));
290
291   if (! no_newline)
292     SVN_INT_ERR(svn_cmdline_fputs("\n", stdout, pool));
293
294   svn_pool_destroy(pool);
295
296   /* Flush stdout to make sure that the user will see any printing errors. */
297   SVN_INT_ERR(svn_cmdline_fflush(stdout));
298
299   return EXIT_SUCCESS;
300 }