svn_error_t * svn_opt_parse_revprop(apr_hash_t **revprop_table_p, const char *revprop_spec, apr_pool_t *pool) { const char *sep, *propname; svn_string_t *propval; if (! *revprop_spec) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Revision property pair is empty")); if (! *revprop_table_p) *revprop_table_p = apr_hash_make(pool); sep = strchr(revprop_spec, '='); if (sep) { propname = apr_pstrndup(pool, revprop_spec, sep - revprop_spec); SVN_ERR(svn_utf_cstring_to_utf8(&propname, propname, pool)); propval = svn_string_create(sep + 1, pool); } else { SVN_ERR(svn_utf_cstring_to_utf8(&propname, revprop_spec, pool)); propval = svn_string_create_empty(pool); } if (!svn_prop_name_is_valid(propname)) return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, _("'%s' is not a valid Subversion property name"), propname); svn_hash_sets(*revprop_table_p, propname, propval); return SVN_NO_ERROR; }
svn_error_t * svn_client__get_auto_props(apr_hash_t **properties, const char **mimetype, const char *path, svn_magic__cookie_t *magic_cookie, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_config_t *cfg; svn_boolean_t use_autoprops; auto_props_baton_t autoprops; /* initialisation */ autoprops.properties = apr_hash_make(pool); autoprops.filename = svn_dirent_basename(path, pool); autoprops.pool = pool; autoprops.mimetype = NULL; autoprops.have_executable = FALSE; *properties = autoprops.properties; cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* check that auto props is enabled */ SVN_ERR(svn_config_get_bool(cfg, &use_autoprops, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE)); /* search for auto props */ if (use_autoprops) svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS, auto_props_enumerator, &autoprops, pool); /* if mimetype has not been set check the file */ if (! autoprops.mimetype) { SVN_ERR(svn_io_detect_mimetype2(&autoprops.mimetype, path, ctx->mimetypes_map, pool)); /* If we got no mime-type, or if it is "application/octet-stream", * try to get the mime-type from libmagic. */ if (magic_cookie && (!autoprops.mimetype || strcmp(autoprops.mimetype, "application/octet-stream") == 0)) { const char *magic_mimetype; /* Since libmagic usually treats UTF-16 files as "text/plain", * svn_magic__detect_binary_mimetype() will return NULL for such * files. This is fine for now since we currently don't support * UTF-16-encoded text files (issue #2194). * Once we do support UTF-16 this code path will fail to detect * them as text unless the svn_io_detect_mimetype2() call above * returns "text/plain" for them. */ SVN_ERR(svn_magic__detect_binary_mimetype(&magic_mimetype, path, magic_cookie, pool, pool)); if (magic_mimetype) autoprops.mimetype = magic_mimetype; } if (autoprops.mimetype) apr_hash_set(autoprops.properties, SVN_PROP_MIME_TYPE, strlen(SVN_PROP_MIME_TYPE), svn_string_create(autoprops.mimetype, pool)); } /* if executable has not been set check the file */ if (! autoprops.have_executable) { svn_boolean_t executable = FALSE; SVN_ERR(svn_io_is_file_executable(&executable, path, pool)); if (executable) apr_hash_set(autoprops.properties, SVN_PROP_EXECUTABLE, strlen(SVN_PROP_EXECUTABLE), svn_string_create_empty(pool)); } *mimetype = autoprops.mimetype; return SVN_NO_ERROR; }
svn_error_t * svn_cl__get_log_message(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool) { svn_stringbuf_t *default_msg = NULL; struct log_msg_baton *lmb = baton; svn_stringbuf_t *message = NULL; svn_config_t *cfg; const char *mfc_after, *sponsored_by; cfg = lmb->config ? svn_hash_gets(lmb->config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; /* Set default message. */ default_msg = svn_stringbuf_create(APR_EOL_STR, pool); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "PR:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Submitted by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reported by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reviewed by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Approved by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Obtained from:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "MFC after:\t"); svn_config_get(cfg, &mfc_after, SVN_CONFIG_SECTION_MISCELLANY, "freebsd-mfc-after", NULL); if (mfc_after != NULL) svn_stringbuf_appendcstr(default_msg, mfc_after); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "MFH:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Relnotes:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Security:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Sponsored by:\t"); svn_config_get(cfg, &sponsored_by, SVN_CONFIG_SECTION_MISCELLANY, "freebsd-sponsored-by", #ifdef HAS_ORGANIZATION_NAME ORGANIZATION_NAME); #else NULL); #endif if (sponsored_by != NULL) svn_stringbuf_appendcstr(default_msg, sponsored_by); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Differential Revision:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Description of fields to fill in above: 76 columns --|" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> PR: If and which Problem Report is related." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Submitted by: If someone else sent in the change." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Reported by: If someone else reported the issue." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Reviewed by: If someone else reviewed your modification." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Approved by: If you needed approval for this commit." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Obtained from: If the change is from a third party." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> MFC after: N [day[s]|week[s]|month[s]]. Request a reminder email." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> MFH: Ports tree branch name. Request approval for merge." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Relnotes: Set to 'yes' for mention in release notes." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Security: Vulnerability reference (one per line) or description." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Sponsored by: If the change was sponsored by an organization." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Differential Revision: https://reviews.freebsd.org/D### (*full* phabric URL needed)." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Empty fields above will be automatically removed." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); *tmp_file = NULL; if (lmb->message) { svn_string_t *log_msg_str = svn_string_create(lmb->message, pool); SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, NULL, NULL, log_msg_str, lmb->message_encoding, FALSE, pool, pool), _("Error normalizing log message to internal format")); /* Strip off the EOF marker text and the junk that follows it. */ truncate_buffer_at_prefix(&(log_msg_str->len), (char *)log_msg_str->data, EDITOR_EOF_PREFIX); cleanmsg(&(log_msg_str->len), (char*)log_msg_str->data); *log_msg = log_msg_str->data; return SVN_NO_ERROR; } if (! commit_items->nelts) { *log_msg = ""; return SVN_NO_ERROR; } while (! message) { /* We still don't have a valid commit message. Use $EDITOR to get one. Note that svn_cl__edit_string_externally will still return a UTF-8'ized log message. */ int i; svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool); svn_error_t *err = SVN_NO_ERROR; svn_string_t *msg_string = svn_string_create_empty(pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *); const char *path = item->path; char text_mod = '_', prop_mod = ' ', unlock = ' '; if (! path) path = item->url; else if (lmb->base_dir) path = svn_dirent_is_child(lmb->base_dir, path, pool); /* If still no path, then just use current directory. */ if (! path || !*path) path = "."; if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) text_mod = 'R'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) text_mod = 'A'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) text_mod = 'D'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) text_mod = 'M'; if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) prop_mod = 'M'; if (! lmb->keep_locks && item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN) unlock = 'U'; svn_stringbuf_appendbyte(tmp_message, text_mod); svn_stringbuf_appendbyte(tmp_message, prop_mod); svn_stringbuf_appendbyte(tmp_message, unlock); if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) /* History included via copy/move. */ svn_stringbuf_appendcstr(tmp_message, "+ "); else svn_stringbuf_appendcstr(tmp_message, " "); svn_stringbuf_appendcstr(tmp_message, path); svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR); } msg_string->data = tmp_message->data; msg_string->len = tmp_message->len; /* Use the external edit to get a log message. */ if (! lmb->non_interactive) { err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left, lmb->editor_cmd, lmb->base_dir ? lmb->base_dir : "", msg_string, "svn-commit", lmb->config, TRUE, lmb->message_encoding, pool); } else /* non_interactive flag says we can't pop up an editor, so error */ { return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("Cannot invoke editor to get log message " "when non-interactive")); } /* Dup the tmpfile path into its baton's pool. */ *tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool, lmb->tmpfile_left); /* If the edit returned an error, handle it. */ if (err) { if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR) err = svn_error_quick_wrap (err, _("Could not use external editor to fetch log message; " "consider setting the $SVN_EDITOR environment variable " "or using the --message (-m) or --file (-F) options")); return svn_error_trace(err); } if (msg_string) message = svn_stringbuf_create_from_string(msg_string, pool); /* Strip off the EOF marker text and the junk that follows it. */ if (message) truncate_buffer_at_prefix(&message->len, message->data, EDITOR_EOF_PREFIX); /* * Since we're adding freebsd-specific tokens to the log message, * clean out any leftovers to avoid accidently sending them to other * projects that won't be expecting them. */ if (message) cleanmsg(&message->len, message->data); if (message) { /* We did get message, now check if it is anything more than just white space as we will consider white space only as empty */ apr_size_t len; for (len = 0; len < message->len; len++) { /* FIXME: should really use an UTF-8 whitespace test rather than svn_ctype_isspace, which is ASCII only */ if (! svn_ctype_isspace(message->data[len])) break; } if (len == message->len) message = NULL; } if (! message) { const char *reply; SVN_ERR(svn_cmdline_prompt_user2 (&reply, _("\nLog message unchanged or not specified\n" "(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool)); if (reply) { int letter = apr_tolower(reply[0]); /* If the user chooses to abort, we cleanup the temporary file and exit the loop with a NULL message. */ if ('a' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; break; } /* If the user chooses to continue, we make an empty message, which will cause us to exit the loop. We also cleanup the temporary file. */ if ('c' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; message = svn_stringbuf_create_empty(pool); } /* If the user chooses anything else, the loop will continue on the NULL message. */ } } } *log_msg = message ? message->data : NULL; return SVN_NO_ERROR; }
svn_error_t * svn_cl__get_log_message(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool) { svn_stringbuf_t *default_msg = NULL; struct log_msg_baton *lmb = baton; svn_stringbuf_t *message = NULL; /* Set default message. */ default_msg = svn_stringbuf_create(APR_EOL_STR, pool); svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR APR_EOL_STR); *tmp_file = NULL; if (lmb->message) { svn_stringbuf_t *log_msg_buf = svn_stringbuf_create(lmb->message, pool); svn_string_t *log_msg_str = apr_pcalloc(pool, sizeof(*log_msg_str)); /* Trim incoming messages of the EOF marker text and the junk that follows it. */ truncate_buffer_at_prefix(&(log_msg_buf->len), log_msg_buf->data, EDITOR_EOF_PREFIX); /* Make a string from a stringbuf, sharing the data allocation. */ log_msg_str->data = log_msg_buf->data; log_msg_str->len = log_msg_buf->len; SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, FALSE, FALSE, log_msg_str, lmb->message_encoding, FALSE, pool, pool), _("Error normalizing log message to internal format")); *log_msg = log_msg_str->data; return SVN_NO_ERROR; } if (! commit_items->nelts) { *log_msg = ""; return SVN_NO_ERROR; } while (! message) { /* We still don't have a valid commit message. Use $EDITOR to get one. Note that svn_cl__edit_string_externally will still return a UTF-8'ized log message. */ int i; svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool); svn_error_t *err = SVN_NO_ERROR; svn_string_t *msg_string = svn_string_create_empty(pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *); const char *path = item->path; char text_mod = '_', prop_mod = ' ', unlock = ' '; if (! path) path = item->url; else if (! *path) path = "."; if (! svn_path_is_url(path) && lmb->base_dir) path = svn_dirent_is_child(lmb->base_dir, path, pool); /* If still no path, then just use current directory. */ if (! path) path = "."; if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) text_mod = 'R'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) text_mod = 'A'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) text_mod = 'D'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) text_mod = 'M'; if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) prop_mod = 'M'; if (! lmb->keep_locks && item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN) unlock = 'U'; svn_stringbuf_appendbyte(tmp_message, text_mod); svn_stringbuf_appendbyte(tmp_message, prop_mod); svn_stringbuf_appendbyte(tmp_message, unlock); if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) /* History included via copy/move. */ svn_stringbuf_appendcstr(tmp_message, "+ "); else svn_stringbuf_appendcstr(tmp_message, " "); svn_stringbuf_appendcstr(tmp_message, path); svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR); } msg_string->data = tmp_message->data; msg_string->len = tmp_message->len; /* Use the external edit to get a log message. */ if (! lmb->non_interactive) { err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left, lmb->editor_cmd, lmb->base_dir, msg_string, "svn-commit", lmb->config, TRUE, lmb->message_encoding, pool); } else /* non_interactive flag says we can't pop up an editor, so error */ { return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("Cannot invoke editor to get log message " "when non-interactive")); } /* Dup the tmpfile path into its baton's pool. */ *tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool, lmb->tmpfile_left); /* If the edit returned an error, handle it. */ if (err) { if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR) err = svn_error_quick_wrap (err, _("Could not use external editor to fetch log message; " "consider setting the $SVN_EDITOR environment variable " "or using the --message (-m) or --file (-F) options")); return svn_error_trace(err); } if (msg_string) message = svn_stringbuf_create_from_string(msg_string, pool); /* Strip the prefix from the buffer. */ if (message) truncate_buffer_at_prefix(&message->len, message->data, EDITOR_EOF_PREFIX); if (message) { /* We did get message, now check if it is anything more than just white space as we will consider white space only as empty */ apr_size_t len; for (len = 0; len < message->len; len++) { /* FIXME: should really use an UTF-8 whitespace test rather than svn_ctype_isspace, which is ASCII only */ if (! svn_ctype_isspace(message->data[len])) break; } if (len == message->len) message = NULL; } if (! message) { const char *reply; SVN_ERR(svn_cmdline_prompt_user2 (&reply, _("\nLog message unchanged or not specified\n" "(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool)); if (reply) { int letter = apr_tolower(reply[0]); /* If the user chooses to abort, we cleanup the temporary file and exit the loop with a NULL message. */ if ('a' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; break; } /* If the user chooses to continue, we make an empty message, which will cause us to exit the loop. We also cleanup the temporary file. */ if ('c' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; message = svn_stringbuf_create_empty(pool); } /* If the user chooses anything else, the loop will continue on the NULL message. */ } } } *log_msg = message ? message->data : NULL; return SVN_NO_ERROR; }