Followup to r29625: fix getopt tests.
[svn.git] / subversion / svn / status-cmd.c
bloba81207db8974fa4316f3e654805d074b891174ab
1 /*
2 * status-cmd.c -- Display status information in current directory
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include "svn_string.h"
26 #include "svn_wc.h"
27 #include "svn_client.h"
28 #include "svn_error_codes.h"
29 #include "svn_error.h"
30 #include "svn_pools.h"
31 #include "svn_xml.h"
32 #include "svn_path.h"
33 #include "svn_cmdline.h"
34 #include "cl.h"
36 #include "svn_private_config.h"
40 /*** Code. ***/
42 struct status_baton
44 /* These fields all correspond to the ones in the
45 svn_cl__print_status() interface. */
46 svn_boolean_t detailed;
47 svn_boolean_t show_last_committed;
48 svn_boolean_t skip_unrecognized;
49 svn_boolean_t repos_locks;
50 apr_pool_t *pool;
52 apr_hash_t *cached_changelists;
53 apr_pool_t *cl_pool; /* where cached changelists are allocated */
55 svn_boolean_t had_print_error; /* To avoid printing lots of errors if we get
56 errors while printing to stdout */
57 svn_boolean_t xml_mode;
61 struct status_cache
63 const char *path;
64 svn_wc_status2_t *status;
68 /* Prints XML target element with path attribute TARGET, using POOL for
69 temporary allocations. */
70 static svn_error_t *
71 print_start_target_xml(const char *target, apr_pool_t *pool)
73 svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
75 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target",
76 "path", target, NULL);
78 return svn_cl__error_checked_fputs(sb->data, stdout);
82 /* Finish a target element by optionally printing an against element if
83 * REPOS_REV is a valid revision number, and then printing an target end tag.
84 * Use POOL for temporary allocations. */
85 static svn_error_t *
86 print_finish_target_xml(svn_revnum_t repos_rev,
87 apr_pool_t *pool)
89 svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
91 if (SVN_IS_VALID_REVNUM(repos_rev))
93 const char *repos_rev_str;
94 repos_rev_str = apr_psprintf(pool, "%ld", repos_rev);
95 svn_xml_make_open_tag(&sb, pool, svn_xml_self_closing, "against",
96 "revision", repos_rev_str, NULL);
99 svn_xml_make_close_tag(&sb, pool, "target");
101 return svn_cl__error_checked_fputs(sb->data, stdout);
105 /* Function which *actually* causes a status structure to be output to
106 the user. Called by both print_status() and svn_cl__status(). */
107 static void
108 print_status_normal_or_xml(void *baton,
109 const char *path,
110 svn_wc_status2_t *status)
112 struct status_baton *sb = baton;
113 svn_error_t *err;
115 if (sb->xml_mode)
116 err = svn_cl__print_status_xml(path, status, sb->pool);
117 else
118 err = svn_cl__print_status(path, status, sb->detailed,
119 sb->show_last_committed,
120 sb->skip_unrecognized,
121 sb->repos_locks,
122 sb->pool);
124 if (err)
126 /* Print if it is the first error. */
127 if (!sb->had_print_error)
129 sb->had_print_error = TRUE;
130 svn_handle_error2(err, stderr, FALSE, "svn: ");
132 svn_error_clear(err);
137 /* A status callback function for printing STATUS for PATH. */
138 static void
139 print_status(void *baton,
140 const char *path,
141 svn_wc_status2_t *status)
143 struct status_baton *sb = baton;
145 /* If there's a changelist attached to the entry, then we don't print
146 the item, but instead dup & cache the status structure for later. */
147 if (status->entry && status->entry->changelist)
149 /* The hash maps a changelist name to an array of status_cache
150 structures. */
151 apr_array_header_t *path_array;
152 const char *cl_key = apr_pstrdup(sb->cl_pool, status->entry->changelist);
153 struct status_cache *scache = apr_pcalloc(sb->cl_pool, sizeof(*scache));
154 scache->path = apr_pstrdup(sb->cl_pool, path);
155 scache->status = svn_wc_dup_status2(status, sb->cl_pool);
157 path_array = (apr_array_header_t *)
158 apr_hash_get(sb->cached_changelists, cl_key, APR_HASH_KEY_STRING);
159 if (path_array == NULL)
161 path_array = apr_array_make(sb->cl_pool, 1,
162 sizeof(struct status_cache *));
163 apr_hash_set(sb->cached_changelists, cl_key,
164 APR_HASH_KEY_STRING, path_array);
167 APR_ARRAY_PUSH(path_array, struct status_cache *) = scache;
168 return;
171 print_status_normal_or_xml(baton, path, status);
174 /* Simpler helper to allow use of svn_cl__try. */
175 static svn_error_t *
176 do_status(svn_cl__opt_state_t *opt_state,
177 const char *target,
178 const svn_opt_revision_t *rev,
179 void *status_baton,
180 svn_client_ctx_t *ctx,
181 apr_pool_t *pool)
183 svn_revnum_t repos_rev = SVN_INVALID_REVNUM;
185 if (opt_state->xml)
186 SVN_ERR(print_start_target_xml(svn_path_local_style(target, pool), pool));
188 SVN_ERR(svn_client_status3(&repos_rev, target, rev,
189 print_status, status_baton,
190 opt_state->depth,
191 opt_state->verbose,
192 opt_state->update,
193 opt_state->no_ignore,
194 opt_state->ignore_externals,
195 opt_state->changelists,
196 ctx, pool));
198 if (opt_state->xml)
199 SVN_ERR(print_finish_target_xml(repos_rev, pool));
201 return SVN_NO_ERROR;
204 /* This implements the `svn_opt_subcommand_t' interface. */
205 svn_error_t *
206 svn_cl__status(apr_getopt_t *os,
207 void *baton,
208 apr_pool_t *pool)
210 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
211 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
212 apr_array_header_t *targets;
213 apr_pool_t *subpool;
214 apr_hash_t *master_cl_hash = apr_hash_make(pool);
215 int i;
216 svn_opt_revision_t rev;
217 struct status_baton sb;
219 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
220 opt_state->targets,
221 pool));
223 /* Add "." if user passed 0 arguments */
224 svn_opt_push_implicit_dot_target(targets, pool);
226 /* We want our -u statuses to be against HEAD. */
227 rev.kind = svn_opt_revision_head;
229 /* The notification callback, leave the notifier as NULL in XML mode */
230 if (! opt_state->xml)
231 svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE,
232 FALSE, FALSE, pool);
234 subpool = svn_pool_create(pool);
236 sb.had_print_error = FALSE;
238 if (opt_state->xml)
240 /* If output is not incremental, output the XML header and wrap
241 everything in a top-level element. This makes the output in
242 its entirety a well-formed XML document. */
243 if (! opt_state->incremental)
244 SVN_ERR(svn_cl__xml_print_header("status", pool));
246 else
248 if (opt_state->incremental)
249 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
250 _("'incremental' option only valid in XML "
251 "mode"));
254 sb.detailed = (opt_state->verbose || opt_state->update);
255 sb.show_last_committed = opt_state->verbose;
256 sb.skip_unrecognized = opt_state->quiet;
257 sb.repos_locks = opt_state->update;
258 sb.xml_mode = opt_state->xml;
259 sb.pool = subpool;
260 sb.cached_changelists = master_cl_hash;
261 sb.cl_pool = pool;
263 for (i = 0; i < targets->nelts; i++)
265 const char *target = APR_ARRAY_IDX(targets, i, const char *);
267 svn_pool_clear(subpool);
269 SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
271 /* Retrieve a hash of status structures with the information
272 requested by the user. */
273 SVN_ERR(svn_cl__try(do_status(opt_state, target, &rev, &sb, ctx,
274 subpool),
275 NULL, opt_state->quiet,
276 SVN_ERR_WC_NOT_DIRECTORY, /* not versioned */
277 SVN_NO_ERROR));
280 /* If any paths were cached because they were associatied with
281 changelists, we can now display them as grouped changelists. */
282 if (apr_hash_count(master_cl_hash) > 0)
284 apr_hash_index_t *hi;
285 svn_stringbuf_t *buf;
287 if (opt_state->xml)
288 buf = svn_stringbuf_create("", pool);
290 for (hi = apr_hash_first(pool, master_cl_hash); hi;
291 hi = apr_hash_next(hi))
293 const char *changelist_name;
294 apr_array_header_t *path_array;
295 const void *key;
296 void *val;
297 int j;
299 apr_hash_this(hi, &key, NULL, &val);
300 changelist_name = key;
301 path_array = val;
303 /* ### TODO: For non-XML output, we shouldn't print the
304 ### leading \n on the first changelist if there were no
305 ### non-changelist entries. */
306 if (opt_state->xml)
308 svn_stringbuf_set(buf, "");
309 svn_xml_make_open_tag(&buf, pool, svn_xml_normal, "changelist",
310 "name", changelist_name, NULL);
311 SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout));
313 else
314 SVN_ERR(svn_cmdline_printf(pool, _("\n--- Changelist '%s':\n"),
315 changelist_name));
317 for (j = 0; j < path_array->nelts; j++)
319 struct status_cache *scache =
320 APR_ARRAY_IDX(path_array, j, struct status_cache *);
321 print_status_normal_or_xml(&sb, scache->path, scache->status);
324 if (opt_state->xml)
326 svn_stringbuf_set(buf, "");
327 svn_xml_make_close_tag(&buf, pool, "changelist");
328 SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout));
333 svn_pool_destroy(subpool);
334 if (opt_state->xml && (! opt_state->incremental))
335 SVN_ERR(svn_cl__xml_print_footer("status", pool));
337 return SVN_NO_ERROR;