In the command-line client, forbid
[svn.git] / subversion / libsvn_client / add.c
blobc0b7e0de9ce74bc5cb88e22122a5dfb8afb601f9
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 "client.h"
39 #include "svn_private_config.h"
43 /*** Code. ***/
45 /* This structure is used as baton for enumerating the config entries
46 in the auto-props section.
48 typedef struct
50 /* the file name for which properties are searched */
51 const char *filename;
53 /* when this flag is set the hash contains svn:executable */
54 svn_boolean_t have_executable;
56 /* when mimetype is not NULL is set the hash contains svn:mime-type */
57 const char *mimetype;
59 /* the hash table for storing the property name/value pairs */
60 apr_hash_t *properties;
62 /* a pool used for allocating memory */
63 apr_pool_t *pool;
64 } auto_props_baton_t;
66 /* Remove leading and trailing white space from a C string, in place. */
67 static void
68 trim_string(char **pstr)
70 char *str = *pstr;
71 int i;
73 while (apr_isspace(*str))
74 str++;
75 *pstr = str;
76 i = strlen(str);
77 while ((i > 0) && apr_isspace(str[i-1]))
78 i--;
79 str[i] = '\0';
82 /* For one auto-props config entry (NAME, VALUE), if the filename pattern
83 NAME matches BATON->filename case insensitively then add the properties
84 listed in VALUE into BATON->properties.
85 BATON must point to an auto_props_baton_t.
87 static svn_boolean_t
88 auto_props_enumerator(const char *name,
89 const char *value,
90 void *baton,
91 apr_pool_t *pool)
93 auto_props_baton_t *autoprops = baton;
94 char *property;
95 char *last_token;
97 /* nothing to do here without a value */
98 if (strlen(value) == 0)
99 return TRUE;
101 /* check if filename matches and return if it doesn't */
102 if (apr_fnmatch(name, autoprops->filename, APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH)
103 return TRUE;
105 /* parse the value (we dup it first to effectively lose the
106 'const', and to avoid messing up the original value) */
107 property = apr_pstrdup(autoprops->pool, value);
108 property = apr_strtok(property, ";", &last_token);
109 while (property)
111 int len;
112 const char *this_value;
113 char *equal_sign = strchr(property, '=');
115 if (equal_sign)
117 *equal_sign = '\0';
118 equal_sign++;
119 trim_string(&equal_sign);
120 this_value = equal_sign;
122 else
124 this_value = "";
126 trim_string(&property);
127 len = strlen(property);
128 if (len > 0)
130 svn_string_t *propval = svn_string_create(this_value,
131 autoprops->pool);
133 apr_hash_set(autoprops->properties, property, len, propval);
134 if (strcmp(property, SVN_PROP_MIME_TYPE) == 0)
135 autoprops->mimetype = this_value;
136 else if (strcmp(property, SVN_PROP_EXECUTABLE) == 0)
137 autoprops->have_executable = TRUE;
139 property = apr_strtok(NULL, ";", &last_token);
141 return TRUE;
144 svn_error_t *
145 svn_client__get_auto_props(apr_hash_t **properties,
146 const char **mimetype,
147 const char *path,
148 svn_client_ctx_t *ctx,
149 apr_pool_t *pool)
151 svn_config_t *cfg;
152 svn_boolean_t use_autoprops;
153 auto_props_baton_t autoprops;
155 /* initialisation */
156 autoprops.properties = apr_hash_make(pool);
157 autoprops.filename = svn_path_basename(path, pool);
158 autoprops.pool = pool;
159 autoprops.mimetype = NULL;
160 autoprops.have_executable = FALSE;
161 *properties = autoprops.properties;
163 cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
164 APR_HASH_KEY_STRING) : NULL;
166 /* check that auto props is enabled */
167 SVN_ERR(svn_config_get_bool(cfg, &use_autoprops,
168 SVN_CONFIG_SECTION_MISCELLANY,
169 SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE));
171 /* search for auto props */
172 if (use_autoprops)
173 svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS,
174 auto_props_enumerator, &autoprops, pool);
176 /* if mimetype has not been set check the file */
177 if (! autoprops.mimetype)
179 SVN_ERR(svn_io_detect_mimetype2(&autoprops.mimetype, path,
180 ctx->mimetypes_map, pool));
181 if (autoprops.mimetype)
182 apr_hash_set(autoprops.properties, SVN_PROP_MIME_TYPE,
183 strlen(SVN_PROP_MIME_TYPE),
184 svn_string_create(autoprops.mimetype, pool));
187 /* Don't automatically set the svn:executable property on added items
188 * on OS400. While OS400 supports the executable permission its use is
189 * inconsistent at best. */
190 #ifndef AS400
191 /* if executable has not been set check the file */
192 if (! autoprops.have_executable)
194 svn_boolean_t executable = FALSE;
195 SVN_ERR(svn_io_is_file_executable(&executable, path, pool));
196 if (executable)
197 apr_hash_set(autoprops.properties, SVN_PROP_EXECUTABLE,
198 strlen(SVN_PROP_EXECUTABLE),
199 svn_string_create("", pool));
201 #endif
203 *mimetype = autoprops.mimetype;
204 return SVN_NO_ERROR;
207 static svn_error_t *
208 add_file(const char *path,
209 svn_client_ctx_t *ctx,
210 svn_wc_adm_access_t *adm_access,
211 apr_pool_t *pool)
213 apr_hash_t* properties;
214 apr_hash_index_t *hi;
215 const char *mimetype;
216 svn_node_kind_t kind;
217 svn_boolean_t is_special;
219 /* Check to see if this is a special file. */
220 SVN_ERR(svn_io_check_special_path(path, &kind, &is_special, pool));
222 if (is_special)
223 mimetype = NULL;
224 else
225 /* Get automatic properties */
226 /* This may fail on write-only files:
227 we open them to estimate file type.
228 That's why we postpone the add until after this step. */
229 SVN_ERR(svn_client__get_auto_props(&properties, &mimetype, path, ctx,
230 pool));
232 /* Add the file */
233 SVN_ERR(svn_wc_add2(path, adm_access, NULL, SVN_INVALID_REVNUM,
234 ctx->cancel_func, ctx->cancel_baton,
235 NULL, NULL, pool));
237 if (is_special)
238 /* This must be a special file. */
239 SVN_ERR(svn_wc_prop_set2
240 (SVN_PROP_SPECIAL,
241 svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool),
242 path, adm_access, FALSE, pool));
243 else if (properties)
245 /* loop through the hashtable and add the properties */
246 for (hi = apr_hash_first(pool, properties);
247 hi != NULL; hi = apr_hash_next(hi))
249 const void *pname;
250 void *pval;
252 apr_hash_this(hi, &pname, NULL, &pval);
253 /* It's probably best to pass 0 for force, so that if
254 the autoprops say to set some weird combination,
255 we just error and let the user sort it out. */
256 SVN_ERR(svn_wc_prop_set2(pname, pval, path,
257 adm_access, FALSE, pool));
261 /* Report the addition to the caller. */
262 if (ctx->notify_func2 != NULL)
264 svn_wc_notify_t *notify = svn_wc_create_notify(path, svn_wc_notify_add,
265 pool);
266 notify->kind = svn_node_file;
267 notify->mime_type = mimetype;
268 (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
271 return SVN_NO_ERROR;
274 /* Schedule directory DIRNAME, and some of the tree under it, for
275 * addition with access baton ADM_ACCESS. DEPTH is the depth at this
276 * point in the descent (it may be changed for recursive calls).
278 * If DIRNAME (or any item below directory DIRNAME) is already scheduled for
279 * addition, add will fail and return an error unless FORCE is TRUE.
281 * Files and directories that match ignore patterns will not be added unless
282 * NO_IGNORE is TRUE.
284 * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow
285 * the user to cancel the operation
287 static svn_error_t *
288 add_dir_recursive(const char *dirname,
289 svn_wc_adm_access_t *adm_access,
290 svn_depth_t depth,
291 svn_boolean_t force,
292 svn_boolean_t no_ignore,
293 svn_client_ctx_t *ctx,
294 apr_pool_t *pool)
296 apr_dir_t *dir;
297 apr_finfo_t this_entry;
298 svn_error_t *err;
299 apr_pool_t *subpool;
300 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
301 svn_wc_adm_access_t *dir_access;
302 apr_array_header_t *ignores;
304 /* Check cancellation; note that this catches recursive calls too. */
305 if (ctx->cancel_func)
306 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
308 /* Add this directory to revision control. */
309 err = svn_wc_add2(dirname, adm_access,
310 NULL, SVN_INVALID_REVNUM,
311 ctx->cancel_func, ctx->cancel_baton,
312 ctx->notify_func2, ctx->notify_baton2, pool);
313 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
314 svn_error_clear(err);
315 else if (err)
316 return err;
318 SVN_ERR(svn_wc_adm_retrieve(&dir_access, adm_access, dirname, pool));
320 if (!no_ignore)
321 SVN_ERR(svn_wc_get_ignores(&ignores, ctx->config, dir_access, pool));
323 subpool = svn_pool_create(pool);
325 SVN_ERR(svn_io_dir_open(&dir, dirname, pool));
327 /* Read the directory entries one by one and add those things to
328 version control. */
329 while (1)
331 const char *fullpath;
333 svn_pool_clear(subpool);
335 err = svn_io_dir_read(&this_entry, flags, dir, subpool);
337 if (err)
339 /* Check if we're done reading the dir's entries. */
340 if (APR_STATUS_IS_ENOENT(err->apr_err))
342 apr_status_t apr_err;
344 svn_error_clear(err);
345 apr_err = apr_dir_close(dir);
346 if (apr_err)
347 return svn_error_wrap_apr
348 (apr_err, _("Can't close directory '%s'"),
349 svn_path_local_style(dirname, subpool));
350 break;
352 else
354 return svn_error_createf
355 (err->apr_err, err,
356 _("Error during add of '%s'"),
357 svn_path_local_style(dirname, subpool));
361 /* Skip entries for this dir and its parent. */
362 if (this_entry.name[0] == '.'
363 && (this_entry.name[1] == '\0'
364 || (this_entry.name[1] == '.' && this_entry.name[2] == '\0')))
365 continue;
367 /* Check cancellation so you can cancel during an
368 * add of a directory with lots of files. */
369 if (ctx->cancel_func)
370 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
372 /* Skip over SVN admin directories. */
373 if (svn_wc_is_adm_dir(this_entry.name, subpool))
374 continue;
376 if ((!no_ignore) && svn_wc_match_ignore_list(this_entry.name,
377 ignores, subpool))
378 continue;
380 /* Construct the full path of the entry. */
381 fullpath = svn_path_join(dirname, this_entry.name, subpool);
383 /* Recurse on directories; add files; ignore the rest. */
384 if (this_entry.filetype == APR_DIR && depth >= svn_depth_immediates)
386 svn_depth_t depth_below_here = depth;
387 if (depth == svn_depth_immediates)
388 depth_below_here = svn_depth_empty;
390 SVN_ERR(add_dir_recursive(fullpath, dir_access, depth_below_here,
391 force, no_ignore, ctx, subpool));
393 else if (this_entry.filetype != APR_UNKFILE
394 && this_entry.filetype != APR_DIR
395 && depth >= svn_depth_files)
397 err = add_file(fullpath, ctx, dir_access, subpool);
398 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
399 svn_error_clear(err);
400 else if (err)
401 return err;
405 /* Opened by svn_wc_add */
406 SVN_ERR(svn_wc_adm_close(dir_access));
408 /* Destroy the per-iteration pool. */
409 svn_pool_destroy(subpool);
411 return SVN_NO_ERROR;
415 /* The main logic of the public svn_client_add4; the only difference
416 is that this function uses an existing access baton.
417 (svn_client_add4 just generates an access baton and calls this func.) */
418 static svn_error_t *
419 add(const char *path,
420 svn_depth_t depth,
421 svn_boolean_t force,
422 svn_boolean_t no_ignore,
423 svn_wc_adm_access_t *adm_access,
424 svn_client_ctx_t *ctx,
425 apr_pool_t *pool)
427 svn_node_kind_t kind;
428 svn_error_t *err;
430 SVN_ERR(svn_io_check_path(path, &kind, pool));
431 if (kind == svn_node_dir && depth >= svn_depth_files)
432 err = add_dir_recursive(path, adm_access, depth,
433 force, no_ignore, ctx, pool);
434 else if (kind == svn_node_file)
435 err = add_file(path, ctx, adm_access, pool);
436 else
437 err = svn_wc_add2(path, adm_access, NULL, SVN_INVALID_REVNUM,
438 ctx->cancel_func, ctx->cancel_baton,
439 ctx->notify_func2, ctx->notify_baton2, pool);
441 /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */
442 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
444 svn_error_clear(err);
445 err = SVN_NO_ERROR;
447 return err;
451 /* Go up the directory tree, looking for a versioned directory. If found,
452 add all the intermediate directories. Otherwise, return
453 SVN_ERR_CLIENT_NO_VERSIONED_PARENT. */
454 static svn_error_t *
455 add_parent_dirs(const char *path,
456 svn_wc_adm_access_t **parent_access,
457 svn_client_ctx_t *ctx,
458 apr_pool_t *pool)
460 svn_wc_adm_access_t *adm_access;
461 svn_error_t *err;
463 err = svn_wc_adm_open3(&adm_access, NULL, path, TRUE, 0,
464 ctx->cancel_func, ctx->cancel_baton, pool);
466 if (err && err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
468 if (svn_dirent_is_root(path, strlen(path)))
470 svn_error_clear(err);
472 return svn_error_create
473 (SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, NULL);
475 else
477 const char *parent_path = svn_path_dirname(path, pool);
479 svn_error_clear(err);
480 SVN_ERR(add_parent_dirs(parent_path, &adm_access, ctx, pool));
481 SVN_ERR(svn_wc_adm_retrieve(&adm_access, adm_access, parent_path,
482 pool));
483 SVN_ERR(svn_wc_add2(path, adm_access, NULL, SVN_INVALID_REVNUM,
484 ctx->cancel_func, ctx->cancel_baton,
485 ctx->notify_func2, ctx->notify_baton2, pool));
488 else if (err)
490 return err;
493 if (parent_access)
494 *parent_access = adm_access;
496 return SVN_NO_ERROR;
501 svn_error_t *
502 svn_client_add4(const char *path,
503 svn_depth_t depth,
504 svn_boolean_t force,
505 svn_boolean_t no_ignore,
506 svn_boolean_t add_parents,
507 svn_client_ctx_t *ctx,
508 apr_pool_t *pool)
510 svn_error_t *err, *err2;
511 svn_wc_adm_access_t *adm_access;
512 const char *parent_dir;
514 if (add_parents)
516 apr_pool_t *subpool;
518 SVN_ERR(svn_path_get_absolute(&path, path, pool));
519 parent_dir = svn_path_dirname(path, pool);
521 subpool = svn_pool_create(pool);
522 SVN_ERR(add_parent_dirs(parent_dir, &adm_access, ctx, subpool));
523 SVN_ERR(svn_wc_adm_close(adm_access));
524 svn_pool_destroy(subpool);
526 SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, parent_dir,
527 TRUE, 0, ctx->cancel_func, ctx->cancel_baton,
528 pool));
530 else
532 parent_dir = svn_path_dirname(path, pool);
533 SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, parent_dir,
534 TRUE, 0, ctx->cancel_func, ctx->cancel_baton,
535 pool));
538 err = add(path, depth, force, no_ignore, adm_access, ctx, pool);
540 err2 = svn_wc_adm_close(adm_access);
541 if (err2)
543 if (err)
544 svn_error_clear(err2);
545 else
546 err = err2;
549 return err;
552 svn_error_t *
553 svn_client_add3(const char *path,
554 svn_boolean_t recursive,
555 svn_boolean_t force,
556 svn_boolean_t no_ignore,
557 svn_client_ctx_t *ctx,
558 apr_pool_t *pool)
560 return svn_client_add4(path, SVN_DEPTH_INFINITY_OR_FILES(recursive),
561 force, no_ignore, FALSE, ctx,
562 pool);
565 svn_error_t *
566 svn_client_add2(const char *path,
567 svn_boolean_t recursive,
568 svn_boolean_t force,
569 svn_client_ctx_t *ctx,
570 apr_pool_t *pool)
572 return svn_client_add3(path, recursive, force, FALSE, ctx, pool);
575 svn_error_t *
576 svn_client_add(const char *path,
577 svn_boolean_t recursive,
578 svn_client_ctx_t *ctx,
579 apr_pool_t *pool)
581 return svn_client_add3(path, recursive, FALSE, FALSE, ctx, pool);
585 static svn_error_t *
586 path_driver_cb_func(void **dir_baton,
587 void *parent_baton,
588 void *callback_baton,
589 const char *path,
590 apr_pool_t *pool)
592 const svn_delta_editor_t *editor = callback_baton;
593 SVN_ERR(svn_path_check_valid(path, pool));
594 return editor->add_directory(path, parent_baton, NULL,
595 SVN_INVALID_REVNUM, pool, dir_baton);
598 /* Append URL, and all it's non-existent parent directories, to TARGETS.
599 Use TEMPPOOL for temporary allocations and POOL for any additions to
600 TARGETS. */
601 static svn_error_t *
602 add_url_parents(svn_ra_session_t *ra_session,
603 const char *url,
604 apr_array_header_t *targets,
605 apr_pool_t *temppool,
606 apr_pool_t *pool)
608 svn_node_kind_t kind;
609 const char *parent_url;
611 svn_path_split(url, &parent_url, NULL, pool);
613 SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool));
614 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
615 temppool));
617 if (kind == svn_node_none)
618 SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool));
620 APR_ARRAY_PUSH(targets, const char *) = url;
622 return SVN_NO_ERROR;
625 static svn_error_t *
626 mkdir_urls(svn_commit_info_t **commit_info_p,
627 const apr_array_header_t *urls,
628 svn_boolean_t make_parents,
629 svn_client_ctx_t *ctx,
630 apr_pool_t *pool)
632 svn_ra_session_t *ra_session = NULL;
633 const svn_delta_editor_t *editor;
634 void *edit_baton;
635 void *commit_baton;
636 const char *log_msg;
637 apr_hash_t *revprop_table;
638 apr_array_header_t *targets;
639 svn_error_t *err;
640 const char *common;
641 int i;
643 /* Find any non-existent parent directories */
644 if (make_parents)
646 apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts,
647 sizeof(const char *));
648 const char *first_url = APR_ARRAY_IDX(urls, 0, const char *);
649 apr_pool_t *iterpool = svn_pool_create(pool);
651 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, first_url,
652 NULL, NULL, NULL, FALSE,
653 TRUE, ctx, pool));
655 for (i = 0; i < urls->nelts; i++)
657 const char *url = APR_ARRAY_IDX(urls, i, const char *);
659 svn_pool_clear(iterpool);
660 SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool));
663 svn_pool_destroy(iterpool);
665 urls = all_urls;
668 /* Condense our list of mkdir targets. */
669 SVN_ERR(svn_path_condense_targets(&common, &targets, urls, FALSE, pool));
671 if (! targets->nelts)
673 const char *bname;
674 svn_path_split(common, &common, &bname, pool);
675 APR_ARRAY_PUSH(targets, const char *) = bname;
677 else
679 svn_boolean_t resplit = FALSE;
681 /* We can't "mkdir" the root of an editor drive, so if one of
682 our targets is the empty string, we need to back everything
683 up by a path component. */
684 for (i = 0; i < targets->nelts; i++)
686 const char *path = APR_ARRAY_IDX(targets, i, const char *);
687 if (! *path)
689 resplit = TRUE;
690 break;
693 if (resplit)
695 const char *bname;
696 svn_path_split(common, &common, &bname, pool);
697 for (i = 0; i < targets->nelts; i++)
699 const char *path = APR_ARRAY_IDX(targets, i, const char *);
700 path = svn_path_join(bname, path, pool);
701 APR_ARRAY_IDX(targets, i, const char *) = path;
706 /* Create new commit items and add them to the array. */
707 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
709 svn_client_commit_item3_t *item;
710 const char *tmp_file;
711 apr_array_header_t *commit_items
712 = apr_array_make(pool, targets->nelts, sizeof(item));
714 for (i = 0; i < targets->nelts; i++)
716 const char *path = APR_ARRAY_IDX(targets, i, const char *);
717 SVN_ERR(svn_client_commit_item_create
718 ((const svn_client_commit_item3_t **) &item, pool));
719 item->url = svn_path_join(common, path, pool);
720 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
721 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
724 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
725 ctx, pool));
727 if (! log_msg)
728 return SVN_NO_ERROR;
730 else
731 log_msg = "";
733 SVN_ERR(svn_client__get_revprop_table(&revprop_table, log_msg, ctx, pool));
735 /* Open an RA session for the URL. Note that we don't have a local
736 directory, nor a place to put temp files. */
737 if (!ra_session)
738 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, common, NULL,
739 NULL, NULL, FALSE, TRUE,
740 ctx, pool));
742 /* URI-decode each target. */
743 for (i = 0; i < targets->nelts; i++)
745 const char *path = APR_ARRAY_IDX(targets, i, const char *);
746 path = svn_path_uri_decode(path, pool);
747 APR_ARRAY_IDX(targets, i, const char *) = path;
750 /* Fetch RA commit editor */
751 SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
752 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
753 revprop_table,
754 svn_client__commit_callback,
755 commit_baton,
756 NULL, TRUE, /* No lock tokens */
757 pool));
759 /* Call the path-based editor driver. */
760 err = svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM,
761 targets, path_driver_cb_func,
762 (void *)editor, pool);
763 if (err)
765 /* At least try to abort the edit (and fs txn) before throwing err. */
766 svn_error_clear(editor->abort_edit(edit_baton, pool));
767 return err;
770 /* Close the edit. */
771 SVN_ERR(editor->close_edit(edit_baton, pool));
773 return SVN_NO_ERROR;
778 svn_error_t *
779 svn_client__make_local_parents(const char *path,
780 svn_boolean_t make_parents,
781 svn_client_ctx_t *ctx,
782 apr_pool_t *pool)
784 svn_error_t *err;
786 if (make_parents)
787 SVN_ERR(svn_io_make_dir_recursively(path, pool));
788 else
789 SVN_ERR(svn_io_dir_make(path, APR_OS_DEFAULT, pool));
791 err = svn_client_add4(path, svn_depth_empty, FALSE, FALSE,
792 make_parents, ctx, pool);
794 /* We just created a new directory, but couldn't add it to
795 version control. Don't leave unversioned directories behind. */
796 if (err)
798 /* ### If this returns an error, should we link it onto
799 err instead, so that the user is warned that we just
800 created an unversioned directory? */
802 svn_error_clear(svn_io_remove_dir(path, pool));
805 return err;
809 svn_error_t *
810 svn_client_mkdir3(svn_commit_info_t **commit_info_p,
811 const apr_array_header_t *paths,
812 svn_boolean_t make_parents,
813 svn_client_ctx_t *ctx,
814 apr_pool_t *pool)
816 if (! paths->nelts)
817 return SVN_NO_ERROR;
819 if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *)))
821 SVN_ERR(mkdir_urls(commit_info_p, paths, make_parents, ctx, pool));
823 else
825 /* This is a regular "mkdir" + "svn add" */
826 apr_pool_t *subpool = svn_pool_create(pool);
827 int i;
829 for (i = 0; i < paths->nelts; i++)
831 const char *path = APR_ARRAY_IDX(paths, i, const char *);
833 svn_pool_clear(subpool);
835 /* See if the user wants us to stop. */
836 if (ctx->cancel_func)
837 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
839 SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx,
840 subpool));
842 svn_pool_destroy(subpool);
845 return SVN_NO_ERROR;
849 svn_error_t *
850 svn_client_mkdir2(svn_commit_info_t **commit_info_p,
851 const apr_array_header_t *paths,
852 svn_client_ctx_t *ctx,
853 apr_pool_t *pool)
855 return svn_client_mkdir3(commit_info_p, paths, FALSE, ctx, pool);
859 svn_error_t *
860 svn_client_mkdir(svn_client_commit_info_t **commit_info_p,
861 const apr_array_header_t *paths,
862 svn_client_ctx_t *ctx,
863 apr_pool_t *pool)
865 svn_commit_info_t *commit_info = NULL;
866 svn_error_t *err;
868 err = svn_client_mkdir2(&commit_info, paths, ctx, pool);
869 /* These structs have the same layout for the common fields. */
870 *commit_info_p = (svn_client_commit_info_t *) commit_info;
871 return err;