Followup to r29625: fix getopt tests.
[svn.git] / subversion / svn / list-cmd.c
blobb12db60554d6dc5bb7af879ed59cbea2bca1d187
1 /*
2 * list-cmd.c -- list a URL
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_cmdline.h"
26 #include "svn_client.h"
27 #include "svn_error.h"
28 #include "svn_pools.h"
29 #include "svn_time.h"
30 #include "svn_xml.h"
31 #include "svn_path.h"
32 #include "cl.h"
34 #include "svn_private_config.h"
36 /*** Code. ***/
38 /* Baton used when printing directory entries. */
39 struct print_baton {
40 svn_boolean_t verbose;
41 svn_client_ctx_t *ctx;
44 /* This implements the svn_client_list_func_t API, printing a single
45 directory entry in text format. */
46 static svn_error_t *
47 print_dirent(void *baton,
48 const char *path,
49 const svn_dirent_t *dirent,
50 const svn_lock_t *lock,
51 const char *abs_path,
52 apr_pool_t *pool)
54 struct print_baton *pb = baton;
55 const char *entryname;
57 if (pb->ctx->cancel_func)
58 SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
60 if (strcmp(path, "") == 0)
62 if (dirent->kind == svn_node_file)
63 entryname = svn_path_basename(abs_path, pool);
64 else if (pb->verbose)
65 entryname = ".";
66 else
67 /* Don't bother to list if no useful information will be shown. */
68 return SVN_NO_ERROR;
70 else
71 entryname = path;
73 if (pb->verbose)
75 apr_time_t now = apr_time_now();
76 apr_time_exp_t exp_time;
77 apr_status_t apr_err;
78 apr_size_t size;
79 char timestr[20];
80 const char *sizestr, *utf8_timestr;
82 /* svn_time_to_human_cstring gives us something *way* too long
83 to use for this, so we have to roll our own. We include
84 the year if the entry's time is not within half a year. */
85 apr_time_exp_lt(&exp_time, dirent->time);
86 if (apr_time_sec(now - dirent->time) < (365 * 86400 / 2)
87 && apr_time_sec(dirent->time - now) < (365 * 86400 / 2))
89 apr_err = apr_strftime(timestr, &size, sizeof(timestr),
90 _("%b %d %H:%M"), &exp_time);
92 else
94 apr_err = apr_strftime(timestr, &size, sizeof(timestr),
95 _("%b %d %Y"), &exp_time);
98 /* if that failed, just zero out the string and print nothing */
99 if (apr_err)
100 timestr[0] = '\0';
102 /* we need it in UTF-8. */
103 SVN_ERR(svn_utf_cstring_to_utf8(&utf8_timestr, timestr, pool));
105 sizestr = apr_psprintf(pool, "%" SVN_FILESIZE_T_FMT, dirent->size);
107 SVN_ERR(svn_cmdline_printf
108 (pool, "%7ld %-8.8s %c %10s %12s %s%s\n",
109 dirent->created_rev,
110 dirent->last_author ? dirent->last_author : " ? ",
111 lock ? 'O' : ' ',
112 (dirent->kind == svn_node_file) ? sizestr : "",
113 utf8_timestr,
114 entryname,
115 (dirent->kind == svn_node_dir) ? "/" : ""));
117 else
119 SVN_ERR(svn_cmdline_printf(pool, "%s%s\n", entryname,
120 (dirent->kind == svn_node_dir)
121 ? "/" : ""));
124 return SVN_NO_ERROR;
128 /* This implements the svn_client_list_func_t API, printing a single dirent
129 in XML format. */
130 static svn_error_t *
131 print_dirent_xml(void *baton,
132 const char *path,
133 const svn_dirent_t *dirent,
134 const svn_lock_t *lock,
135 const char *abs_path,
136 apr_pool_t *pool)
138 struct print_baton *pb = baton;
139 const char *entryname;
140 svn_stringbuf_t *sb;
142 if (strcmp(path, "") == 0)
144 if (dirent->kind == svn_node_file)
145 entryname = svn_path_basename(abs_path, pool);
146 else if (pb->verbose)
147 entryname = ".";
148 else
149 /* Don't bother to list if no useful information will be shown. */
150 return SVN_NO_ERROR;
152 else
153 entryname = path;
155 if (pb->ctx->cancel_func)
156 SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
158 sb = svn_stringbuf_create("", pool);
160 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
161 "kind", svn_cl__node_kind_str(dirent->kind),
162 NULL);
164 svn_cl__xml_tagged_cdata(&sb, pool, "name", entryname);
166 if (dirent->kind == svn_node_file)
168 svn_cl__xml_tagged_cdata
169 (&sb, pool, "size",
170 apr_psprintf(pool, "%" SVN_FILESIZE_T_FMT, dirent->size));
173 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "commit",
174 "revision",
175 apr_psprintf(pool, "%ld", dirent->created_rev),
176 NULL);
177 svn_cl__xml_tagged_cdata(&sb, pool, "author", dirent->last_author);
178 if (dirent->time)
179 svn_cl__xml_tagged_cdata(&sb, pool, "date",
180 svn_time_to_cstring(dirent->time, pool));
181 svn_xml_make_close_tag(&sb, pool, "commit");
183 if (lock)
185 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "lock", NULL);
186 svn_cl__xml_tagged_cdata(&sb, pool, "token", lock->token);
187 svn_cl__xml_tagged_cdata(&sb, pool, "owner", lock->owner);
188 svn_cl__xml_tagged_cdata(&sb, pool, "comment", lock->comment);
189 svn_cl__xml_tagged_cdata(&sb, pool, "created",
190 svn_time_to_cstring(lock->creation_date,
191 pool));
192 if (lock->expiration_date != 0)
193 svn_cl__xml_tagged_cdata(&sb, pool, "expires",
194 svn_time_to_cstring
195 (lock->expiration_date, pool));
196 svn_xml_make_close_tag(&sb, pool, "lock");
199 svn_xml_make_close_tag(&sb, pool, "entry");
201 SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
203 return SVN_NO_ERROR;
207 /* This implements the `svn_opt_subcommand_t' interface. */
208 svn_error_t *
209 svn_cl__list(apr_getopt_t *os,
210 void *baton,
211 apr_pool_t *pool)
213 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
214 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
215 apr_array_header_t *targets;
216 int i;
217 apr_pool_t *subpool = svn_pool_create(pool);
218 apr_uint32_t dirent_fields;
219 struct print_baton pb;
221 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
222 opt_state->targets,
223 pool));
225 /* Add "." if user passed 0 arguments */
226 svn_opt_push_implicit_dot_target(targets, pool);
228 if (opt_state->xml)
230 /* The XML output contains all the information, so "--verbose"
231 does not apply. */
232 if (opt_state->verbose)
233 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
234 _("'verbose' option invalid in XML mode"));
236 /* If output is not incremental, output the XML header and wrap
237 everything in a top-level element. This makes the output in
238 its entirety a well-formed XML document. */
239 if (! opt_state->incremental)
240 SVN_ERR(svn_cl__xml_print_header("lists", pool));
242 else
244 if (opt_state->incremental)
245 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
246 _("'incremental' option only valid in XML "
247 "mode"));
250 if (opt_state->verbose || opt_state->xml)
251 dirent_fields = SVN_DIRENT_ALL;
252 else
253 dirent_fields = SVN_DIRENT_KIND; /* the only thing we actually need... */
255 pb.ctx = ctx;
256 pb.verbose = opt_state->verbose;
258 if (opt_state->depth == svn_depth_unknown)
259 opt_state->depth = svn_depth_immediates;
261 /* For each target, try to list it. */
262 for (i = 0; i < targets->nelts; i++)
264 const char *target = APR_ARRAY_IDX(targets, i, const char *);
265 const char *truepath;
266 svn_opt_revision_t peg_revision;
268 svn_pool_clear(subpool);
270 SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
272 /* Get peg revisions. */
273 SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
274 subpool));
276 if (opt_state->xml)
278 svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
279 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "list",
280 "path", truepath[0] == '\0' ? "." : truepath,
281 NULL);
282 SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
285 SVN_ERR(svn_client_list2(truepath, &peg_revision,
286 &(opt_state->start_revision),
287 opt_state->depth,
288 dirent_fields,
289 (opt_state->xml || opt_state->verbose),
290 opt_state->xml ? print_dirent_xml : print_dirent,
291 &pb, ctx, subpool));
293 if (opt_state->xml)
295 svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
296 svn_xml_make_close_tag(&sb, pool, "list");
297 SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
301 svn_pool_destroy(subpool);
303 if (opt_state->xml && ! opt_state->incremental)
304 SVN_ERR(svn_cl__xml_print_footer("lists", pool));
306 return SVN_NO_ERROR;