diff options
author | Vicent Martà <vicent@github.com> | 2012-07-12 20:32:44 +0400 |
---|---|---|
committer | Vicent Martà <vicent@github.com> | 2012-07-12 20:32:44 +0400 |
commit | 48bcf81dd2584d91e5922dd1458dab6c4173bdcf (patch) | |
tree | c834ff548520151966e6b04ef2ec19d475ff18e6 /src | |
parent | 111ee3fe2d4c6de6729b94235c709986b4079c4b (diff) | |
parent | 54e29b9380f1cd0fa596d71c317de907da19b13d (diff) |
Merge pull request #812 from arrbee/assorted-tweaks
Assorted goodies
Diffstat (limited to 'src')
-rw-r--r-- | src/buffer.c | 36 | ||||
-rw-r--r-- | src/buffer.h | 19 | ||||
-rw-r--r-- | src/config.c | 16 | ||||
-rw-r--r-- | src/config_file.c | 37 | ||||
-rw-r--r-- | src/config_file.h | 17 | ||||
-rw-r--r-- | src/path.c | 69 | ||||
-rw-r--r-- | src/path.h | 23 | ||||
-rw-r--r-- | src/repository.c | 92 | ||||
-rw-r--r-- | src/util.h | 2 |
9 files changed, 287 insertions, 24 deletions
diff --git a/src/buffer.c b/src/buffer.c index 04aaec3df..5d54ee1a5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -141,6 +141,42 @@ int git_buf_puts(git_buf *buf, const char *string) return git_buf_put(buf, string, strlen(string)); } +int git_buf_puts_escaped( + git_buf *buf, const char *string, const char *esc_chars, const char *esc_with) +{ + const char *scan = string; + size_t total = 0, esc_with_len = strlen(esc_with); + + while (*scan) { + size_t count = strcspn(scan, esc_chars); + total += count + 1 + esc_with_len; + scan += count + 1; + } + + ENSURE_SIZE(buf, buf->size + total + 1); + + for (scan = string; *scan; ) { + size_t count = strcspn(scan, esc_chars); + + memmove(buf->ptr + buf->size, scan, count); + scan += count; + buf->size += count; + + if (*scan) { + memmove(buf->ptr + buf->size, esc_with, esc_with_len); + buf->size += esc_with_len; + + memmove(buf->ptr + buf->size, scan, 1); + scan += 1; + buf->size += 1; + } + } + + buf->ptr[buf->size] = '\0'; + + return 0; +} + int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) { int len; diff --git a/src/buffer.h b/src/buffer.h index 50c75f64e..75f3b0e4f 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -91,6 +91,18 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...); int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b); /** + * Copy string into buf prefixing every character that is contained in the + * esc_chars string with the esc_with string. + */ +int git_buf_puts_escaped( + git_buf *buf, const char *string, const char *esc_chars, const char *esc_with); + +GIT_INLINE(int) git_buf_puts_escape_regex(git_buf *buf, const char *string) +{ + return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\"); +} + +/** * Join two strings as paths, inserting a slash between as needed. * @return 0 on success, -1 on failure */ @@ -121,6 +133,13 @@ GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch) return idx; } +GIT_INLINE(ssize_t) git_buf_rfind(git_buf *buf, char ch) +{ + ssize_t idx = (ssize_t)buf->size - 1; + while (idx >= 0 && buf->ptr[idx] != ch) idx--; + return idx; +} + /* Remove whitespace from the end of the buffer */ void git_buf_rtrim(git_buf *buf); diff --git a/src/config.c b/src/config.c index d18b85c30..98fb3b20d 100644 --- a/src/config.c +++ b/src/config.c @@ -136,17 +136,27 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority) * Loop over all the variables */ -int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) +int git_config_foreach( + git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) +{ + return git_config_foreach_match(cfg, NULL, fn, data); +} + +int git_config_foreach_match( + git_config *cfg, + const char *regexp, + int (*fn)(const char *, const char *, void *), + void *data) { int ret = 0; unsigned int i; file_internal *internal; git_config_file *file; - for(i = 0; i < cfg->files.length && ret == 0; ++i) { + for (i = 0; i < cfg->files.length && ret == 0; ++i) { internal = git_vector_get(&cfg->files, i); file = internal->file; - ret = file->foreach(file, fn, data); + ret = file->foreach(file, regexp, fn, data); } return ret; diff --git a/src/config_file.c b/src/config_file.c index fd1aa8d08..1f3ebfca9 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -188,25 +188,46 @@ static void backend_free(git_config_file *_backend) git__free(backend); } -static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data) +static int file_foreach( + git_config_file *backend, + const char *regexp, + int (*fn)(const char *, const char *, void *), + void *data) { diskfile_backend *b = (diskfile_backend *)backend; cvar_t *var; const char *key; + regex_t regex; + int result = 0; if (!b->values) return 0; + if (regexp != NULL) { + if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { + giterr_set_regex(®ex, result); + regfree(®ex); + return -1; + } + } + git_strmap_foreach(b->values, key, var, - do { - if (fn(key, var->value, data) < 0) - break; + for (; var != NULL; var = CVAR_LIST_NEXT(var)) { + /* skip non-matching keys if regexp was provided */ + if (regexp && regexec(®ex, key, 0, NULL, 0) != 0) + continue; - var = CVAR_LIST_NEXT(var); - } while (var != NULL); + /* abort iterator on non-zero return value */ + if ((result = fn(key, var->value, data)) != 0) + goto cleanup; + } ); - return 0; +cleanup: + if (regexp != NULL) + regfree(®ex); + + return result; } static int config_set(git_config_file *cfg, const char *name, const char *value) @@ -337,6 +358,7 @@ static int config_get_multivar( result = regcomp(®ex, regex_str, REG_EXTENDED); if (result < 0) { giterr_set_regex(®ex, result); + regfree(®ex); return -1; } @@ -396,6 +418,7 @@ static int config_set_multivar( if (result < 0) { git__free(key); giterr_set_regex(&preg, result); + regfree(&preg); return -1; } diff --git a/src/config_file.h b/src/config_file.h index 0080b5713..c31292881 100644 --- a/src/config_file.h +++ b/src/config_file.h @@ -19,12 +19,27 @@ GIT_INLINE(void) git_config_file_free(git_config_file *cfg) cfg->free(cfg); } +GIT_INLINE(int) git_config_file_set_string( + git_config_file *cfg, const char *name, const char *value) +{ + return cfg->set(cfg, name, value); +} + GIT_INLINE(int) git_config_file_foreach( git_config_file *cfg, int (*fn)(const char *key, const char *value, void *data), void *data) { - return cfg->foreach(cfg, fn, data); + return cfg->foreach(cfg, NULL, fn, data); +} + +GIT_INLINE(int) git_config_file_foreach_match( + git_config_file *cfg, + const char *regexp, + int (*fn)(const char *key, const char *value, void *data), + void *data) +{ + return cfg->foreach(cfg, regexp, fn, data); } #endif diff --git a/src/path.c b/src/path.c index 9c88240e0..e9bc4871c 100644 --- a/src/path.c +++ b/src/path.c @@ -17,9 +17,7 @@ #include <stdio.h> #include <ctype.h> -#ifdef GIT_WIN32 #define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':') -#endif /* * Based on the Android implementation, BSD licensed. @@ -172,11 +170,11 @@ int git_path_root(const char *path) { int offset = 0; -#ifdef GIT_WIN32 /* Does the root of the path look like a windows drive ? */ if (LOOKS_LIKE_DRIVE_PREFIX(path)) offset += 2; +#ifdef GIT_WIN32 /* Are we dealing with a windows network path? */ else if ((path[0] == '/' && path[1] == '/') || (path[0] == '\\' && path[1] == '\\')) @@ -464,6 +462,71 @@ int git_path_find_dir(git_buf *dir, const char *path, const char *base) return error; } +int git_path_resolve_relative(git_buf *path, size_t ceiling) +{ + char *base, *to, *from, *next; + size_t len; + + if (!path || git_buf_oom(path)) + return -1; + + if (ceiling > path->size) + ceiling = path->size; + + /* recognize drive prefixes, etc. that should not be backed over */ + if (ceiling == 0) + ceiling = git_path_root(path->ptr) + 1; + + /* recognize URL prefixes that should not be backed over */ + if (ceiling == 0) { + for (next = path->ptr; *next && git__isalpha(*next); ++next); + if (next[0] == ':' && next[1] == '/' && next[2] == '/') + ceiling = (next + 3) - path->ptr; + } + + base = to = from = path->ptr + ceiling; + + while (*from) { + for (next = from; *next && *next != '/'; ++next); + + len = next - from; + + if (len == 1 && from[0] == '.') + /* do nothing with singleton dot */; + + else if (len == 2 && from[0] == '.' && from[1] == '.') { + while (to > base && to[-1] == '/') to--; + while (to > base && to[-1] != '/') to--; + } + + else { + if (*next == '/') + len++; + + if (to != from) + memmove(to, from, len); + + to += len; + } + + from += len; + + while (*from == '/') from++; + } + + *to = '\0'; + + path->size = to - path->ptr; + + return 0; +} + +int git_path_apply_relative(git_buf *target, const char *relpath) +{ + git_buf_joinpath(target, git_buf_cstr(target), relpath); + return git_path_resolve_relative(target, 0); +} + int git_path_cmp( const char *name1, size_t len1, int isdir1, const char *name2, size_t len2, int isdir2) diff --git a/src/path.h b/src/path.h index fd76805e5..d68393b3d 100644 --- a/src/path.h +++ b/src/path.h @@ -186,6 +186,29 @@ extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char extern int git_path_find_dir(git_buf *dir, const char *path, const char *base); /** + * Resolve relative references within a path. + * + * This eliminates "./" and "../" relative references inside a path, + * as well as condensing multiple slashes into single ones. It will + * not touch the path before the "ceiling" length. + * + * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL + * prefix and not touch that part of the path. + */ +extern int git_path_resolve_relative(git_buf *path, size_t ceiling); + +/** + * Apply a relative path to base path. + * + * Note that the base path could be a filename or a URL and this + * should still work. The relative path is walked segment by segment + * with three rules: series of slashes will be condensed to a single + * slash, "." will be eaten with no change, and ".." will remove a + * segment from the base path. + */ +extern int git_path_apply_relative(git_buf *target, const char *relpath); + +/** * Walk each directory entry, except '.' and '..', calling fn(state). * * @param pathbuf buffer the function reads the initial directory diff --git a/src/repository.c b/src/repository.c index bee7b3ee6..a2931713e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -741,15 +741,24 @@ static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit) #define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n" static int repo_write_template( - const char *git_dir, const char *file, mode_t mode, const char *content) + const char *git_dir, + bool allow_overwrite, + const char *file, + mode_t mode, + const char *content) { git_buf path = GIT_BUF_INIT; - int fd, error = 0; + int fd, error = 0, flags; if (git_buf_joinpath(&path, git_dir, file) < 0) return -1; - fd = p_open(git_buf_cstr(&path), O_WRONLY | O_CREAT | O_EXCL, mode); + if (allow_overwrite) + flags = O_WRONLY | O_CREAT | O_TRUNC; + else + flags = O_WRONLY | O_CREAT | O_EXCL; + + fd = p_open(git_buf_cstr(&path), flags, mode); if (fd >= 0) { error = p_write(fd, content, strlen(content)); @@ -811,7 +820,7 @@ static int repo_init_structure(const char *git_dir, int is_bare) /* Make template files as needed */ for (i = 0; tmpl[i].file != NULL; ++i) { if (repo_write_template( - git_dir, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0) + git_dir, false, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0) return -1; } @@ -943,8 +952,47 @@ const char *git_repository_workdir(git_repository *repo) return repo->workdir; } -int git_repository_set_workdir(git_repository *repo, const char *workdir) +static int write_gitlink( + const char *in_dir, const char *to_repo) +{ + int error; + git_buf buf = GIT_BUF_INIT; + struct stat st; + + if (git_path_dirname_r(&buf, to_repo) < 0 || + git_path_to_dir(&buf) < 0) + return -1; + + /* don't write gitlink to natural workdir */ + if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 && + strcmp(in_dir, buf.ptr) == 0) + return GIT_PASSTHROUGH; + + if (git_buf_joinpath(&buf, in_dir, DOT_GIT) < 0) + return -1; + + if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) { + giterr_set(GITERR_REPOSITORY, + "Cannot overwrite gitlink file into path '%s'", in_dir); + return GIT_EEXISTS; + } + + git_buf_clear(&buf); + + if (git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo) < 0) + return -1; + + error = repo_write_template(in_dir, true, DOT_GIT, 0644, buf.ptr); + + git_buf_free(&buf); + + return error; +} + +int git_repository_set_workdir( + git_repository *repo, const char *workdir, int update_gitlink) { + int error = 0; git_buf path = GIT_BUF_INIT; assert(repo && workdir); @@ -952,11 +1000,37 @@ int git_repository_set_workdir(git_repository *repo, const char *workdir) if (git_path_prettify_dir(&path, workdir, NULL) < 0) return -1; - git__free(repo->workdir); + if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) + return 0; - repo->workdir = git_buf_detach(&path); - repo->is_bare = 0; - return 0; + if (update_gitlink) { + git_config *config; + + if (git_repository_config__weakptr(&config, repo) < 0) + return -1; + + error = write_gitlink(path.ptr, git_repository_path(repo)); + + /* passthrough error means gitlink is unnecessary */ + if (error == GIT_PASSTHROUGH) + error = git_config_delete(config, "core.worktree"); + else if (!error) + error = git_config_set_string(config, "core.worktree", path.ptr); + + if (!error) + error = git_config_set_bool(config, "core.bare", false); + } + + if (!error) { + char *old_workdir = repo->workdir; + + repo->workdir = git_buf_detach(&path); + repo->is_bare = 0; + + git__free(old_workdir); + } + + return error; } int git_repository_is_bare(git_repository *repo) diff --git a/src/util.h b/src/util.h index abdd543cc..a84dcab1e 100644 --- a/src/util.h +++ b/src/util.h @@ -211,7 +211,7 @@ GIT_INLINE(bool) git__isdigit(int c) GIT_INLINE(bool) git__isspace(int c) { - return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v'); + return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */); } GIT_INLINE(bool) git__iswildcard(int c) |