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 /* ==================================================================== */
25 #include "svn_cmdline.h"
26 #include "svn_client.h"
27 #include "svn_error.h"
28 #include "svn_pools.h"
34 #include "svn_private_config.h"
38 /* Baton used when printing directory entries. */
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. */
47 print_dirent(void *baton
,
49 const svn_dirent_t
*dirent
,
50 const svn_lock_t
*lock
,
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
);
67 /* Don't bother to list if no useful information will be shown. */
75 apr_time_t now
= apr_time_now();
76 apr_time_exp_t exp_time
;
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
);
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 */
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",
110 dirent
->last_author
? dirent
->last_author
: " ? ",
112 (dirent
->kind
== svn_node_file
) ? sizestr
: "",
115 (dirent
->kind
== svn_node_dir
) ? "/" : ""));
119 SVN_ERR(svn_cmdline_printf(pool
, "%s%s\n", entryname
,
120 (dirent
->kind
== svn_node_dir
)
128 /* This implements the svn_client_list_func_t API, printing a single dirent
131 print_dirent_xml(void *baton
,
133 const svn_dirent_t
*dirent
,
134 const svn_lock_t
*lock
,
135 const char *abs_path
,
138 struct print_baton
*pb
= baton
;
139 const char *entryname
;
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
)
149 /* Don't bother to list if no useful information will be shown. */
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
),
164 svn_cl__xml_tagged_cdata(&sb
, pool
, "name", entryname
);
166 if (dirent
->kind
== svn_node_file
)
168 svn_cl__xml_tagged_cdata
170 apr_psprintf(pool
, "%" SVN_FILESIZE_T_FMT
, dirent
->size
));
173 svn_xml_make_open_tag(&sb
, pool
, svn_xml_normal
, "commit",
175 apr_psprintf(pool
, "%ld", dirent
->created_rev
),
177 svn_cl__xml_tagged_cdata(&sb
, pool
, "author", dirent
->last_author
);
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");
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
,
192 if (lock
->expiration_date
!= 0)
193 svn_cl__xml_tagged_cdata(&sb
, pool
, "expires",
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
));
207 /* This implements the `svn_opt_subcommand_t' interface. */
209 svn_cl__list(apr_getopt_t
*os
,
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
;
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
,
225 /* Add "." if user passed 0 arguments */
226 svn_opt_push_implicit_dot_target(targets
, pool
);
230 /* The XML output contains all the information, so "--verbose"
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
));
244 if (opt_state
->incremental
)
245 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR
, NULL
,
246 _("'incremental' option only valid in XML "
250 if (opt_state
->verbose
|| opt_state
->xml
)
251 dirent_fields
= SVN_DIRENT_ALL
;
253 dirent_fields
= SVN_DIRENT_KIND
; /* the only thing we actually need... */
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
,
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
,
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
),
289 (opt_state
->xml
|| opt_state
->verbose
),
290 opt_state
->xml
? print_dirent_xml
: print_dirent
,
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
));