Fix compiler warning due to missing function prototype.
[svn.git] / subversion / libsvn_client / add.c
blobca8c0841371bcbe47b1bf0e12a8a0a1247b7ffa7
1 /*
2 * add.c: wrappers around wc add/mkdir functionality.
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 <string.h>
26 #include <apr_lib.h>
27 #include <apr_fnmatch.h>
28 #include "svn_wc.h"
29 #include "svn_client.h"
30 #include "svn_string.h"
31 #include "svn_pools.h"
32 #include "svn_error.h"
33 #include "svn_path.h"
34 #include "svn_io.h"
35 #include "svn_config.h"
36 #include "svn_props.h"
37 #include "svn_hash.h"
38 #include "svn_sorts.h"
39 #include "client.h"
41 #include "svn_private_config.h"
45 /*** Code. ***/
47 /* This structure is used as baton for enumerating the config entries
48 in the auto-props section.
50 typedef struct
52 /* the file name for which properties are searched */
53 const char *filename;
55 /* when this flag is set the hash contains svn:executable */
56 svn_boolean_t have_executable;
58 /* when mimetype is not NULL is set the hash contains svn:mime-type */
59 const char *mimetype;
61 /* the hash table for storing the property name/value pairs */
62 apr_hash_t *properties;
64 /* a pool used for allocating memory */
65 apr_pool_t *pool;
66 } auto_props_baton_t;
68 /* Remove leading and trailing white space from a C string, in place. */
69 static void
70 trim_string(char **pstr)
72 char *str = *pstr;
73 int i;
75 while (apr_isspace(*str))
76 str++;
77 *pstr = str;
78 i = strlen(str);
79 while ((i > 0) && apr_isspace(str[i-1]))
80 i--;
81 str[i] = '\0';
84 /* For one auto-props config entry (NAME, VALUE), if the filename pattern
85 NAME matches BATON->filename case insensitively then add the properties
86 listed in VALUE into BATON->properties.
87 BATON must point to an auto_props_baton_t.
89 static svn_boolean_t
90 auto_props_enumerator(const char *name,
91 const char *value,
92 void *baton,
93 apr_pool_t *pool)
95 auto_props_baton_t *autoprops = baton;
96 char *property;
97 char *last_token;
99 /* nothing to do here without a value */
100 if (strlen(value) == 0)
101 return TRUE;
103 /* check if filename matches and return if it doesn't */
104 if (apr_fnmatch(name, autoprops->filename, APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH)
105 return TRUE;
107 /* parse the value (we dup it first to effectively lose the
108 'const', and to avoid messing up the original value) */
109 property = apr_pstrdup(autoprops->pool, value);
110 property = apr_strtok(property, ";", &last_token);
111 while (property)
113 int len;
114 const char *this_value;
115 char *equal_sign = strchr(property, '=');
117 if (equal_sign)
119 *equal_sign = '\0';
120 equal_sign++;
121 trim_string(&equal_sign);
122 this_value = equal_sign;
124 else
126 this_value = "";
128 trim_string(&property);
129 len = strlen(property);
130 if (len > 0)
132 svn_string_t *propval = svn_string_create(this_value,
133 autoprops->pool);
135 apr_hash_set(autoprops->properties, property, len, propval);
136 if (strcmp(property, SVN_PROP_MIME_TYPE) == 0)
137 autoprops->mimetype = this_value;
138 else if (strcmp(property, SVN_PROP_EXECUTABLE) == 0)
139 autoprops->have_executable = TRUE;
141 property = apr_strtok(NULL, ";", &last_token);
143 return TRUE;
146 svn_error_t *
147 svn_client__get_auto_props(apr_hash_t **properties,
148 const char **mimetype,
149 const char *path,
150 svn_client_ctx_t *ctx,
151 apr_pool_t *pool)
153 svn_config_t *cfg;
154 svn_boolean_t use_autoprops;
155 auto_props_baton_t autoprops;
157 /* initialisation */
158 autoprops.properties = apr_hash_make(pool);
159 autoprops.filename = svn_path_basename(path, pool);
160 autoprops.pool = pool;
161 autoprops.mimetype = NULL;
162 autoprops.have_executable = FALSE;
163 *properties = autoprops.properties;
165 cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
166 APR_HASH_KEY_STRING) : NULL;
168 /* check that auto props is enabled */
169 SVN_ERR(svn_config_get_bool(cfg, &use_autoprops,
170 SVN_CONFIG_SECTION_MISCELLANY,
171 SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE));
173 /* search for auto props */
174 if (use_autoprops)
175 svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS,
176 auto_props_enumerator, &autoprops, pool);
178 /* if mimetype has not been set check the file */
179 if (! autoprops.mimetype)
181 SVN_ERR(svn_io_detect_mimetype2(&autoprops.mimetype, path,
182 ctx->mimetypes_map, pool));
183 if (autoprops.mimetype)
184 apr_hash_set(autoprops.properties, SVN_PROP_MIME_TYPE,
185 strlen(SVN_PROP_MIME_TYPE),
186 svn_string_create(autoprops.mimetype, pool));
189 /* Don't automatically set the svn:executable property on added items
190 * on OS400. While OS400 supports the executable permission its use is
191 * inconsistent at best. */
192 #ifndef AS400
193 /* if executable has not been set check the file */
194 if (! autoprops.have_executable)
196 svn_boolean_t executable = FALSE;
197 SVN_ERR(svn_io_is_file_executable(&executable, path, pool));
198 if (executable)
199 apr_hash_set(autoprops.properties, SVN_PROP_EXECUTABLE,
200 strlen(SVN_PROP_EXECUTABLE),
201 svn_string_create("", pool));
203 #endif
205 *mimetype = autoprops.mimetype;
206 return SVN_NO_ERROR;
209 static svn_error_t *
210 add_file(const char *path,
211 svn_client_ctx_t *ctx,
212 svn_wc_adm_access_t *adm_access,
213 apr_pool_t *pool)
215 apr_hash_t* properties;
216 apr_hash_index_t *hi;
217 const char *mimetype;
218 svn_node_kind_t kind;
219 svn_boolean_t is_special;
221 /* Check to see if this is a special file. */
222 SVN_ERR(svn_io_check_special_path(path, &kind, &is_special, pool));
224 if (is_special)
225 mimetype = NULL;
226 else
227 /* Get automatic properties */
228 /* This may fail on write-only files:
229 we open them to estimate file type.
230 That's why we postpone the add until after this step. */
231 SVN_ERR(svn_client__get_auto_props(&properties, &mimetype, path, ctx,
232 pool));
234 /* Add the file */
235 SVN_ERR(svn_wc_add2(path, adm_access, NULL, SVN_INVALID_REVNUM,
236 ctx->cancel_func, ctx->cancel_baton,
237 NULL, NULL, pool));
239 if (is_special)
240 /* This must be a special file. */
241 SVN_ERR(svn_wc_prop_set2
242 (SVN_PROP_SPECIAL,
243 svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool),
244 path, adm_access, FALSE, pool));
245 else if (properties)
247 /* loop through the hashtable and add the properties */
248 for (hi = apr_hash_first(pool, properties);
249 hi != NULL; hi = apr_hash_next(hi))
251 const void *pname;
252 void *pval;
254 apr_hash_this(hi, &pname, NULL, &pval);
255 /* It's probably best to pass 0 for force, so that if
256 the autoprops say to set some weird combination,
257 we just error and let the user sort it out. */
258 SVN_ERR(svn_wc_prop_set2(pname, pval, path,
259 adm_access, FALSE, pool));
263 /* Report the addition to the caller. */
264 if (ctx->notify_func2 != NULL)
266 svn_wc_notify_t *notify = svn_wc_create_notify(path, svn_wc_notify_add,
267 pool);
268 notify->kind = svn_node_file;
269 notify->mime_type = mimetype;
270 (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
273 return SVN_NO_ERROR;
276 /* Schedule directory DIRNAME, and some of the tree under it, for
277 * addition with access baton ADM_ACCESS. DEPTH is the depth at this
278 * point in the descent (it may be changed for recursive calls).
280 * If DIRNAME (or any item below directory DIRNAME) is already scheduled for
281 * addition, add will fail and return an error unless FORCE is TRUE.
283 * Files and directories that match ignore patterns will not be added unless
284 * NO_IGNORE is TRUE.
286 * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow
287 * the user to cancel the operation
289 static svn_error_t *
290 add_dir_recursive(const char *dirname,
291 svn_wc_adm_access_t *adm_access,
292 svn_depth_t depth,
293 svn_boolean_t force,
294 svn_boolean_t no_ignore,
295 svn_client_ctx_t *ctx,
296 apr_pool_t *pool)
298 apr_dir_t *dir;
299 apr_finfo_t this_entry;
300 svn_error_t *err;
301 apr_pool_t *subpool;
302 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
303 svn_wc_adm_access_t *dir_access;
304 apr_array_header_t *ignores;
306 /* Check cancellation; note that this catches recursive calls too. */
307 if (ctx->cancel_func)
308 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
310 /* Add this directory to revision control. */
311 err = svn_wc_add2(dirname, adm_access,
312 NULL, SVN_INVALID_REVNUM,
313 ctx->cancel_func, ctx->cancel_baton,
314 ctx->notify_func2, ctx->notify_baton2, pool);
315 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
316 svn_error_clear(err);
317 else if (err)
318 return err;
320 SVN_ERR(svn_wc_adm_retrieve(&dir_access, adm_access, dirname, pool));
322 if (!no_ignore)
323 SVN_ERR(svn_wc_get_ignores(&ignores, ctx->config, dir_access, pool));
325 subpool = svn_pool_create(pool);
327 SVN_ERR(svn_io_dir_open(&dir, dirname, pool));
329 /* Read the directory entries one by one and add those things to
330 version control. */
331 while (1)
333 const char *fullpath;
335 svn_pool_clear(subpool);
337 err = svn_io_dir_read(&this_entry, flags, dir, subpool);
339 if (err)
341 /* Check if we're done reading the dir's entries. */
342 if (APR_STATUS_IS_ENOENT(err->apr_err))
344 apr_status_t apr_err;
346 svn_error_clear(err);
347 apr_err = apr_dir_close(dir);
348 if (apr_err)
349 return svn_error_wrap_apr
350 (apr_err, _("Can't close directory '%s'"),
351 svn_path_local_style(dirname, subpool));
352 break;
354 else
356 return svn_error_createf
357 (err->apr_err, err,
358 _("Error during add of '%s'"),
359 svn_path_local_style(dirname, subpool));
363 /* Skip entries for this dir and its parent. */
364 if (this_entry.name[0] == '.'
365 && (this_entry.name[1] == '\0'
366 || (this_entry.name[1] == '.' && this_entry.name[2] == '\0')))
367 continue;
369 /* Check cancellation so you can cancel during an
370 * add of a directory with lots of files. */
371 if (ctx->cancel_func)
372 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
374 /* Skip over SVN admin directories. */
375 if (svn_wc_is_adm_dir(this_entry.name, subpool))
376 continue;
378 if ((!no_ignore) && svn_wc_match_ignore_list(this_entry.name,
379 ignores, subpool))
380 continue;
382 /* Construct the full path of the entry. */
383 fullpath = svn_path_join(dirname, this_entry.name, subpool);
385 /* Recurse on directories; add files; ignore the rest. */
386 if (this_entry.filetype == APR_DIR && depth >= svn_depth_immediates)
388 svn_depth_t depth_below_here = depth;
389 if (depth == svn_depth_immediates)
390 depth_below_here = svn_depth_empty;
392 SVN_ERR(add_dir_recursive(fullpath, dir_access, depth_below_here,
393 force, no_ignore, ctx, subpool));
395 else if (this_entry.filetype != APR_UNKFILE
396 && this_entry.filetype != APR_DIR
397 && depth >= svn_depth_files)
399 err = add_file(fullpath, ctx, dir_access, subpool);
400 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
401 svn_error_clear(err);
402 else if (err)
403 return err;
407 /* Opened by svn_wc_add */
408 SVN_ERR(svn_wc_adm_close(dir_access));
410 /* Destroy the per-iteration pool. */
411 svn_pool_destroy(subpool);
413 return SVN_NO_ERROR;
417 /* The main logic of the public svn_client_add4; the only difference
418 is that this function uses an existing access baton.
419 (svn_client_add4 just generates an access baton and calls this func.) */
420 static svn_error_t *
421 add(const char *path,
422 svn_depth_t depth,
423 svn_boolean_t force,
424 svn_boolean_t no_ignore,
425 svn_wc_adm_access_t *adm_access,
426 svn_client_ctx_t *ctx,
427 apr_pool_t *pool)
429 svn_node_kind_t kind;
430 svn_error_t *err;
432 SVN_ERR(svn_io_check_path(path, &kind, pool));
433 if (kind == svn_node_dir && depth >= svn_depth_files)
434 err = add_dir_recursive(path, adm_access, depth,
435 force, no_ignore, ctx, pool);
436 else if (kind == svn_node_file)
437 err = add_file(path, ctx, adm_access, pool);
438 else
439 err = svn_wc_add2(path, adm_access, NULL, SVN_INVALID_REVNUM,
440 ctx->cancel_func, ctx->cancel_baton,
441 ctx->notify_func2, ctx->notify_baton2, pool);
443 /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */
444 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
446 svn_error_clear(err);
447 err = SVN_NO_ERROR;
449 return err;
453 /* Go up the directory tree, looking for a versioned directory. If found,
454 add all the intermediate directories. Otherwise, return
455 SVN_ERR_CLIENT_NO_VERSIONED_PARENT. */
456 static svn_error_t *
457 add_parent_dirs(const char *path,
458 svn_wc_adm_access_t **parent_access,
459 svn_client_ctx_t *ctx,
460 apr_pool_t *pool)
462 svn_wc_adm_access_t *adm_access;
463 svn_error_t *err;
465 err = svn_wc_adm_open3(&adm_access, NULL, path, TRUE, 0,
466 ctx->cancel_func, ctx->cancel_baton, pool);
468 if (err && err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
470 if (svn_dirent_is_root(path, strlen(path)))
472 svn_error_clear(err);
474 return svn_error_create
475 (SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, NULL);
477 else
479 const char *parent_path = svn_path_dirname(path, pool);
481 svn_error_clear(err);
482 SVN_ERR(add_parent_dirs(parent_path, &adm_access, ctx, pool));
483 SVN_ERR(svn_wc_adm_retrieve(&adm_access, adm_access, parent_path,
484 pool));
485 SVN_ERR(svn_wc_add2(path, adm_access, NULL, SVN_INVALID_REVNUM,
486 ctx->cancel_func, ctx->cancel_baton,
487 ctx->notify_func2, ctx->notify_baton2, pool));
490 else if (err)
492 return err;
495 if (parent_access)
496 *parent_access = adm_access;
498 return SVN_NO_ERROR;
503 svn_error_t *
504 svn_client_add4(const char *path,
505 svn_depth_t depth,
506 svn_boolean_t force,
507 svn_boolean_t no_ignore,
508 svn_boolean_t add_parents,
509 svn_client_ctx_t *ctx,
510 apr_pool_t *pool)
512 svn_error_t *err, *err2;
513 svn_wc_adm_access_t *adm_access;
514 const char *parent_dir;
516 if (add_parents)
518 apr_pool_t *subpool;
520 SVN_ERR(svn_path_get_absolute(&path, path, pool));
521 parent_dir = svn_path_dirname(path, pool);
523 subpool = svn_pool_create(pool);
524 SVN_ERR(add_parent_dirs(parent_dir, &adm_access, ctx, subpool));
525 SVN_ERR(svn_wc_adm_close(adm_access));
526 svn_pool_destroy(subpool);
528 SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, parent_dir,
529 TRUE, 0, ctx->cancel_func, ctx->cancel_baton,
530 pool));
532 else
534 parent_dir = svn_path_dirname(path, pool);
535 SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, parent_dir,
536 TRUE, 0, ctx->cancel_func, ctx->cancel_baton,
537 pool));
540 err = add(path, depth, force, no_ignore, adm_access, ctx, pool);
542 err2 = svn_wc_adm_close(adm_access);
543 if (err2)
545 if (err)
546 svn_error_clear(err2);
547 else
548 err = err2;
551 return err;
554 svn_error_t *
555 svn_client_add3(const char *path,
556 svn_boolean_t recursive,
557 svn_boolean_t force,
558 svn_boolean_t no_ignore,
559 svn_client_ctx_t *ctx,
560 apr_pool_t *pool)
562 return svn_client_add4(path, SVN_DEPTH_INFINITY_OR_FILES(recursive),
563 force, no_ignore, FALSE, ctx,
564 pool);
567 svn_error_t *
568 svn_client_add2(const char *path,
569 svn_boolean_t recursive,
570 svn_boolean_t force,
571 svn_client_ctx_t *ctx,
572 apr_pool_t *pool)
574 return svn_client_add3(path, recursive, force, FALSE, ctx, pool);
577 svn_error_t *
578 svn_client_add(const char *path,
579 svn_boolean_t recursive,
580 svn_client_ctx_t *ctx,
581 apr_pool_t *pool)
583 return svn_client_add3(path, recursive, FALSE, FALSE, ctx, pool);
587 static svn_error_t *
588 path_driver_cb_func(void **dir_baton,
589 void *parent_baton,
590 void *callback_baton,
591 const char *path,
592 apr_pool_t *pool)
594 const svn_delta_editor_t *editor = callback_baton;
595 SVN_ERR(svn_path_check_valid(path, pool));
596 return editor->add_directory(path, parent_baton, NULL,
597 SVN_INVALID_REVNUM, pool, dir_baton);
600 /* Append URL, and all it's non-existent parent directories, to TARGETS.
601 Use TEMPPOOL for temporary allocations and POOL for any additions to
602 TARGETS. */
603 static svn_error_t *
604 add_url_parents(svn_ra_session_t *ra_session,
605 const char *url,
606 apr_array_header_t *targets,
607 apr_pool_t *temppool,
608 apr_pool_t *pool)
610 svn_node_kind_t kind;
611 const char *parent_url;
613 svn_path_split(url, &parent_url, NULL, pool);
615 SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool));
616 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
617 temppool));
619 if (kind == svn_node_none)
620 SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool));
622 APR_ARRAY_PUSH(targets, const char *) = url;
624 return SVN_NO_ERROR;
627 static svn_error_t *
628 mkdir_urls(svn_commit_info_t **commit_info_p,
629 const apr_array_header_t *urls,
630 svn_boolean_t make_parents,
631 svn_client_ctx_t *ctx,
632 apr_pool_t *pool)
634 svn_ra_session_t *ra_session = NULL;
635 const svn_delta_editor_t *editor;
636 void *edit_baton;
637 void *commit_baton;
638 const char *log_msg;
639 apr_hash_t *revprop_table;
640 apr_array_header_t *targets;
641 apr_hash_t *targets_hash;
642 svn_error_t *err;
643 const char *common;
644 int i;
646 /* Find any non-existent parent directories */
647 if (make_parents)
649 apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts,
650 sizeof(const char *));
651 const char *first_url = APR_ARRAY_IDX(urls, 0, const char *);
652 apr_pool_t *iterpool = svn_pool_create(pool);
654 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, first_url,
655 NULL, NULL, NULL, FALSE,
656 TRUE, ctx, pool));
658 for (i = 0; i < urls->nelts; i++)
660 const char *url = APR_ARRAY_IDX(urls, i, const char *);
662 svn_pool_clear(iterpool);
663 SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool));
666 svn_pool_destroy(iterpool);
668 urls = all_urls;
671 /* Condense our list of mkdir targets. */
672 SVN_ERR(svn_path_condense_targets(&common, &targets, urls, FALSE, pool));
673 SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool));
674 SVN_ERR(svn_hash_keys(&targets, targets_hash, pool));
676 if (! targets->nelts)
678 const char *bname;
679 svn_path_split(common, &common, &bname, pool);
680 APR_ARRAY_PUSH(targets, const char *) = bname;
682 else
684 svn_boolean_t resplit = FALSE;
686 /* We can't "mkdir" the root of an editor drive, so if one of
687 our targets is the empty string, we need to back everything
688 up by a path component. */
689 for (i = 0; i < targets->nelts; i++)
691 const char *path = APR_ARRAY_IDX(targets, i, const char *);
692 if (! *path)
694 resplit = TRUE;
695 break;
698 if (resplit)
700 const char *bname;
701 svn_path_split(common, &common, &bname, pool);
702 for (i = 0; i < targets->nelts; i++)
704 const char *path = APR_ARRAY_IDX(targets, i, const char *);
705 path = svn_path_join(bname, path, pool);
706 APR_ARRAY_IDX(targets, i, const char *) = path;
710 qsort(targets->elts, targets->nelts, targets->elt_size,
711 svn_sort_compare_paths);
713 /* Create new commit items and add them to the array. */
714 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
716 svn_client_commit_item3_t *item;
717 const char *tmp_file;
718 apr_array_header_t *commit_items
719 = apr_array_make(pool, targets->nelts, sizeof(item));
721 for (i = 0; i < targets->nelts; i++)
723 const char *path = APR_ARRAY_IDX(targets, i, const char *);
724 SVN_ERR(svn_client_commit_item_create
725 ((const svn_client_commit_item3_t **) &item, pool));
726 item->url = svn_path_join(common, path, pool);
727 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
728 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
731 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
732 ctx, pool));
734 if (! log_msg)
735 return SVN_NO_ERROR;
737 else
738 log_msg = "";
740 SVN_ERR(svn_client__get_revprop_table(&revprop_table, log_msg, ctx, pool));
742 /* Open an RA session for the URL. Note that we don't have a local
743 directory, nor a place to put temp files. */
744 if (!ra_session)
745 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, common, NULL,
746 NULL, NULL, FALSE, TRUE,
747 ctx, pool));
749 /* URI-decode each target. */
750 for (i = 0; i < targets->nelts; i++)
752 const char *path = APR_ARRAY_IDX(targets, i, const char *);
753 path = svn_path_uri_decode(path, pool);
754 APR_ARRAY_IDX(targets, i, const char *) = path;
757 /* Fetch RA commit editor */
758 SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
759 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
760 revprop_table,
761 svn_client__commit_callback,
762 commit_baton,
763 NULL, TRUE, /* No lock tokens */
764 pool));
766 /* Call the path-based editor driver. */
767 err = svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM,
768 targets, path_driver_cb_func,
769 (void *)editor, pool);
770 if (err)
772 /* At least try to abort the edit (and fs txn) before throwing err. */
773 svn_error_clear(editor->abort_edit(edit_baton, pool));
774 return err;
777 /* Close the edit. */
778 SVN_ERR(editor->close_edit(edit_baton, pool));
780 return SVN_NO_ERROR;
785 svn_error_t *
786 svn_client__make_local_parents(const char *path,
787 svn_boolean_t make_parents,
788 svn_client_ctx_t *ctx,
789 apr_pool_t *pool)
791 svn_error_t *err;
793 if (make_parents)
794 SVN_ERR(svn_io_make_dir_recursively(path, pool));
795 else
796 SVN_ERR(svn_io_dir_make(path, APR_OS_DEFAULT, pool));
798 err = svn_client_add4(path, svn_depth_empty, FALSE, FALSE,
799 make_parents, ctx, pool);
801 /* We just created a new directory, but couldn't add it to
802 version control. Don't leave unversioned directories behind. */
803 if (err)
805 /* ### If this returns an error, should we link it onto
806 err instead, so that the user is warned that we just
807 created an unversioned directory? */
809 svn_error_clear(svn_io_remove_dir(path, pool));
812 return err;
816 svn_error_t *
817 svn_client_mkdir3(svn_commit_info_t **commit_info_p,
818 const apr_array_header_t *paths,
819 svn_boolean_t make_parents,
820 svn_client_ctx_t *ctx,
821 apr_pool_t *pool)
823 if (! paths->nelts)
824 return SVN_NO_ERROR;
826 if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *)))
828 SVN_ERR(mkdir_urls(commit_info_p, paths, make_parents, ctx, pool));
830 else
832 /* This is a regular "mkdir" + "svn add" */
833 apr_pool_t *subpool = svn_pool_create(pool);
834 int i;
836 for (i = 0; i < paths->nelts; i++)
838 const char *path = APR_ARRAY_IDX(paths, i, const char *);
840 svn_pool_clear(subpool);
842 /* See if the user wants us to stop. */
843 if (ctx->cancel_func)
844 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
846 SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx,
847 subpool));
849 svn_pool_destroy(subpool);
852 return SVN_NO_ERROR;
856 svn_error_t *
857 svn_client_mkdir2(svn_commit_info_t **commit_info_p,
858 const apr_array_header_t *paths,
859 svn_client_ctx_t *ctx,
860 apr_pool_t *pool)
862 return svn_client_mkdir3(commit_info_p, paths, FALSE, ctx, pool);
866 svn_error_t *
867 svn_client_mkdir(svn_client_commit_info_t **commit_info_p,
868 const apr_array_header_t *paths,
869 svn_client_ctx_t *ctx,
870 apr_pool_t *pool)
872 svn_commit_info_t *commit_info = NULL;
873 svn_error_t *err;
875 err = svn_client_mkdir2(&commit_info, paths, ctx, pool);
876 /* These structs have the same layout for the common fields. */
877 *commit_info_p = (svn_client_commit_info_t *) commit_info;
878 return err;