/* what is the common non-wildcard prefix for all items in the pathspec */ char *git_pathspec_prefix(const git_strarray *pathspec) { git_buf prefix = GIT_BUF_INIT; const char *scan; if (!pathspec || !pathspec->count || git_buf_text_common_prefix(&prefix, pathspec) < 0) return NULL; /* diff prefix will only be leading non-wildcards */ for (scan = prefix.ptr; *scan; ++scan) { if (git__iswildcard(*scan) && (scan == prefix.ptr || (*(scan - 1) != '\\'))) break; } git_buf_truncate(&prefix, scan - prefix.ptr); if (prefix.size <= 0) { git_buf_free(&prefix); return NULL; } git_buf_text_unescape(&prefix); return git_buf_detach(&prefix); }
static bool _check_dir_contents( git_buf *dir, const char *sub, bool (*predicate)(const char *)) { bool result; size_t dir_size = git_buf_len(dir); size_t sub_size = strlen(sub); size_t alloc_size; /* leave base valid even if we could not make space for subdir */ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) || GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) || git_buf_try_grow(dir, alloc_size, false, false) < 0) return false; /* save excursion */ git_buf_joinpath(dir, dir->ptr, sub); result = predicate(dir->ptr); /* restore path */ git_buf_truncate(dir, dir_size); return result; }
static int diriter_update_paths(git_path_diriter *diriter) { size_t filename_len, path_len; filename_len = wcslen(diriter->current.cFileName); if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) || GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2)) return -1; if (path_len > GIT_WIN_PATH_UTF16) { giterr_set(GITERR_FILESYSTEM, "invalid path '%.*ls\\%ls' (path too long)", diriter->parent_len, diriter->path, diriter->current.cFileName); return -1; } diriter->path[diriter->parent_len] = L'\\'; memcpy(&diriter->path[diriter->parent_len+1], diriter->current.cFileName, filename_len * sizeof(wchar_t)); diriter->path[path_len-1] = L'\0'; git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len); git_buf_putc(&diriter->path_utf8, '/'); git_buf_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len); if (git_buf_oom(&diriter->path_utf8)) return -1; return 0; }
git_vector_foreach(contents, i, ps) { /* skip if before start_stat or after end_stat */ cmp_len = min(start_len, ps->path_len); if (cmp_len && strncomp(ps->path, start_stat, cmp_len) < 0) continue; cmp_len = min(end_len, ps->path_len); if (cmp_len && strncomp(ps->path, end_stat, cmp_len) > 0) continue; git_buf_truncate(&full, prefix_len); if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || (error = git_path_lstat(full.ptr, &ps->st)) < 0) { if (error == GIT_ENOTFOUND) { giterr_clear(); error = 0; git_vector_remove(contents, i--); continue; } break; } if (S_ISDIR(ps->st.st_mode)) { ps->path[ps->path_len++] = '/'; ps->path[ps->path_len] = '\0'; } }
static int diff_context_line__pattern_match( git_diff_driver *driver, git_buf *line) { size_t i, maxi = git_array_size(driver->fn_patterns); regmatch_t pmatch[2]; for (i = 0; i < maxi; ++i) { git_diff_driver_pattern *pat = git_array_get(driver->fn_patterns, i); if (!regexec(&pat->re, line->ptr, 2, pmatch, 0)) { if (pat->flags & REG_NEGATE) return false; /* use pmatch data to trim line data */ i = (pmatch[1].rm_so >= 0) ? 1 : 0; git_buf_consume(line, git_buf_cstr(line) + pmatch[i].rm_so); git_buf_truncate(line, pmatch[i].rm_eo - pmatch[i].rm_so); git_buf_rtrim(line); return true; } } return false; }
static int _check_dir_contents( git_buf *dir, const char *sub, int append_on_success, int (*predicate)(const char *)) { int error = GIT_SUCCESS; size_t dir_size = dir->size; size_t sub_size = strlen(sub); /* leave base valid even if we could not make space for subdir */ if ((error = git_buf_try_grow(dir, dir_size + sub_size + 2)) < GIT_SUCCESS) return error; /* save excursion */ git_buf_joinpath(dir, dir->ptr, sub); error = (*predicate)(dir->ptr); /* restore excursion */ if (!append_on_success || error != GIT_SUCCESS) git_buf_truncate(dir, dir_size); return error; }
git_vector_foreach(contents, i, ps) { /* skip if before start_stat or after end_stat */ cmp_len = min(start_len, ps->path_len); if (cmp_len && strncomp(ps->path, start_stat, cmp_len) < 0) continue; cmp_len = min(end_len, ps->path_len); if (cmp_len && strncomp(ps->path, end_stat, cmp_len) > 0) continue; git_buf_truncate(&full, prefix_len); if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || (error = git_path_lstat(full.ptr, &ps->st)) < 0) break; if (S_ISDIR(ps->st.st_mode)) { if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0) break; if (p_access(full.ptr, F_OK) == 0) { ps->st.st_mode = GIT_FILEMODE_COMMIT; } else { ps->path[ps->path_len++] = '/'; ps->path[ps->path_len] = '\0'; } } }
int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings) { size_t i; const char *str, *pfx; git_buf_clear(buf); if (!strings || !strings->count) return 0; /* initialize common prefix to first string */ if (git_buf_sets(buf, strings->strings[0]) < 0) return -1; /* go through the rest of the strings, truncating to shared prefix */ for (i = 1; i < strings->count; ++i) { for (str = strings->strings[i], pfx = buf->ptr; *str && *str == *pfx; str++, pfx++) /* scanning */; git_buf_truncate(buf, pfx - buf->ptr); if (!buf->size) break; } return 0; }
static int checkout_blob( struct checkout_diff_data *data, const git_diff_file *file) { git_blob *blob; int error; git_buf_truncate(data->path, data->workdir_len); if (git_buf_joinpath(data->path, git_buf_cstr(data->path), file->path) < 0) return -1; if ((error = git_blob_lookup(&blob, data->owner, &file->oid)) < 0) return error; if (S_ISLNK(file->mode)) error = blob_content_to_link( blob, git_buf_cstr(data->path), data->can_symlink); else error = blob_content_to_file( blob, git_buf_cstr(data->path), file->mode, data->checkout_opts); git_blob_free(blob); return error; }
static void create_paths(const char *root, int depth) { git_buf fullpath = GIT_BUF_INIT; size_t root_len; int i; cl_git_pass(git_buf_puts(&fullpath, root)); cl_git_pass(git_buf_putc(&fullpath, '/')); root_len = fullpath.size; for (i = 0; i < 8; i++) { bool file = (depth == 0 || (i % 2) == 0); git_buf_truncate(&fullpath, root_len); cl_git_pass(git_buf_printf(&fullpath, "item%d", i)); if (file) { cl_git_rewritefile(fullpath.ptr, "This is a file!\n"); } else { cl_must_pass(p_mkdir(fullpath.ptr, 0777)); if (depth > 0) create_paths(fullpath.ptr, (depth - 1)); } } git_buf_dispose(&fullpath); }
void test_repo_open__win32_path(void) { #ifdef GIT_WIN32 git_repository *repo = cl_git_sandbox_init("empty_standard_repo"), *repo2; git_buf winpath = GIT_BUF_INIT; static const char *repo_path = "empty_standard_repo/.git/"; static const char *repo_wd = "empty_standard_repo/"; cl_assert(git__suffixcmp(git_repository_path(repo), repo_path) == 0); cl_assert(git__suffixcmp(git_repository_workdir(repo), repo_wd) == 0); cl_git_pass(git_buf_sets(&winpath, git_repository_path(repo))); unposix_path(&winpath); cl_git_pass(git_repository_open(&repo2, winpath.ptr)); cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); git_repository_free(repo2); cl_git_pass(git_buf_sets(&winpath, git_repository_path(repo))); git_buf_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */ unposix_path(&winpath); cl_git_pass(git_repository_open(&repo2, winpath.ptr)); cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); git_repository_free(repo2); cl_git_pass(git_buf_sets(&winpath, git_repository_workdir(repo))); unposix_path(&winpath); cl_git_pass(git_repository_open(&repo2, winpath.ptr)); cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); git_repository_free(repo2); cl_git_pass(git_buf_sets(&winpath, git_repository_workdir(repo))); git_buf_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */ unposix_path(&winpath); cl_git_pass(git_repository_open(&repo2, winpath.ptr)); cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); git_repository_free(repo2); git_buf_free(&winpath); #endif }
int git_path_direach( git_buf *path, int (*fn)(void *, git_buf *), void *arg) { ssize_t wd_len; DIR *dir; struct dirent *de, *de_buf; if (git_path_to_dir(path) < 0) return -1; wd_len = git_buf_len(path); if ((dir = opendir(path->ptr)) == NULL) { giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr); return -1; } #ifdef __sun de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1); #else de_buf = git__malloc(sizeof(struct dirent)); #endif while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) { int result; if (is_dot_or_dotdot(de->d_name)) continue; if (git_buf_puts(path, de->d_name) < 0) { closedir(dir); git__free(de_buf); return -1; } result = fn(arg, path); git_buf_truncate(path, wd_len); /* restore path */ if (result < 0) { closedir(dir); git__free(de_buf); return -1; } } closedir(dir); git__free(de_buf); return 0; }
int git_path_diriter_next(git_path_diriter *diriter) { struct dirent *de; const char *filename; size_t filename_len; bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); int error = 0; assert(diriter); errno = 0; do { if ((de = readdir(diriter->dir)) == NULL) { if (!errno) return GIT_ITEROVER; giterr_set(GITERR_OS, "Could not read directory '%s'", diriter->path); return -1; } } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name)); filename = de->d_name; filename_len = strlen(filename); #ifdef GIT_USE_ICONV if ((diriter->flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0 && (error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0) return error; #endif git_buf_truncate(&diriter->path, diriter->parent_len); if (diriter->parent_len > 0 && diriter->path.ptr[diriter->parent_len-1] != '/') git_buf_putc(&diriter->path, '/'); git_buf_put(&diriter->path, filename, filename_len); if (git_buf_oom(&diriter->path)) return -1; return error; }
GIT_INLINE(int) rebase_readfile( git_buf *out, git_buf *state_path, const char *filename) { size_t state_path_len = state_path->size; int error; git_buf_clear(out); if ((error = git_buf_joinpath(state_path, state_path->ptr, filename)) < 0 || (error = git_futils_readbuffer(out, state_path->ptr)) < 0) goto done; git_buf_rtrim(out); done: git_buf_truncate(state_path, state_path_len); return error; }
static int index_path(git_buf *path, git_indexer *idx, const char *suffix) { const char prefix[] = "pack-"; size_t slash = (size_t)path->size; /* search backwards for '/' */ while (slash > 0 && path->ptr[slash - 1] != '/') slash--; if (git_buf_grow(path, slash + 1 + strlen(prefix) + GIT_OID_HEXSZ + strlen(suffix) + 1) < 0) return -1; git_buf_truncate(path, slash); git_buf_puts(path, prefix); git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash); path->size += GIT_OID_HEXSZ; git_buf_puts(path, suffix); return git_buf_oom(path) ? -1 : 0; }
git_vector_foreach(contents, i, ps) { /* skip if before start_stat or after end_stat */ cmp_len = min(start_len, ps->path_len); if (cmp_len && strncomp(ps->path, start_stat, cmp_len) < 0) continue; cmp_len = min(end_len, ps->path_len); if (cmp_len && strncomp(ps->path, end_stat, cmp_len) > 0) continue; git_buf_truncate(&full, prefix_len); if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || (error = git_path_lstat(full.ptr, &ps->st)) < 0) { if (error == GIT_ENOTFOUND) { /* file was removed between readdir and lstat */ char *entry_path = git_vector_get(contents, i); git_vector_remove(contents, i--); git__free(entry_path); } else { /* Treat the file as unreadable if we get any other error */ memset(&ps->st, 0, sizeof(ps->st)); ps->st.st_mode = GIT_FILEMODE_UNREADABLE; } giterr_clear(); error = 0; continue; } if (S_ISDIR(ps->st.st_mode)) { ps->path[ps->path_len++] = '/'; ps->path[ps->path_len] = '\0'; } else if (!S_ISREG(ps->st.st_mode) && !S_ISLNK(ps->st.st_mode)) { char *entry_path = git_vector_get(contents, i); git_vector_remove(contents, i--); git__free(entry_path); } }
int git_path_direach( git_buf *path, int (*fn)(void *, git_buf *), void *arg) { ssize_t wd_len; DIR *dir; struct dirent *de; if (git_path_to_dir(path) < GIT_SUCCESS) return git_buf_lasterror(path); wd_len = path->size; dir = opendir(path->ptr); if (!dir) return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path->ptr); while ((de = readdir(dir)) != NULL) { int result; if (is_dot_or_dotdot(de->d_name)) continue; if (git_buf_puts(path, de->d_name) < GIT_SUCCESS) return git_buf_lasterror(path); result = fn(arg, path); git_buf_truncate(path, wd_len); /* restore path */ if (result != GIT_SUCCESS) { closedir(dir); return result; /* The callee is reponsible for setting the correct error message */ } } closedir(dir); return GIT_SUCCESS; }
int git_path_dirload_with_stat( const char *path, size_t prefix_len, git_vector *contents) { int error; unsigned int i; git_path_with_stat *ps; git_buf full = GIT_BUF_INIT; if (git_buf_set(&full, path, prefix_len) < 0) return -1; error = git_path_dirload( path, prefix_len, sizeof(git_path_with_stat) + 1, contents); if (error < 0) { git_buf_free(&full); return error; } git_vector_foreach(contents, i, ps) { size_t path_len = strlen((char *)ps); memmove(ps->path, ps, path_len + 1); ps->path_len = path_len; if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || (error = git_path_lstat(full.ptr, &ps->st)) < 0) break; git_buf_truncate(&full, prefix_len); if (S_ISDIR(ps->st.st_mode)) { ps->path[path_len] = '/'; ps->path[path_len + 1] = '\0'; } }
int git_path_direach( git_buf *path, uint32_t flags, int (*fn)(void *, git_buf *), void *arg) { int error = 0; ssize_t wd_len; DIR *dir; struct dirent *de; #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; #endif GIT_UNUSED(flags); if (git_path_to_dir(path) < 0) return -1; wd_len = git_buf_len(path); if ((dir = opendir(path->ptr)) == NULL) { giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr); if (errno == ENOENT) return GIT_ENOTFOUND; return -1; } #ifdef GIT_USE_ICONV if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0) (void)git_path_iconv_init_precompose(&ic); #endif while ((de = readdir(dir)) != NULL) { char *de_path = de->d_name; size_t de_len = strlen(de_path); if (git_path_is_dot_or_dotdot(de_path)) continue; #ifdef GIT_USE_ICONV if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0) break; #endif if ((error = git_buf_put(path, de_path, de_len)) < 0) break; giterr_clear(); error = fn(arg, path); git_buf_truncate(path, wd_len); /* restore path */ /* Only set our own error if the callback did not set one already */ if (error != 0) { if (!giterr_last()) giterr_set_after_callback(error); break; } } closedir(dir); #ifdef GIT_USE_ICONV git_path_iconv_clear(&ic); #endif return error; }
/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */ int git_reference__normalize_name( git_buf *buf, const char *name, unsigned int flags) { const char *current; int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; unsigned int process_flags; bool normalize = (buf != NULL); #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; #endif assert(name); process_flags = flags; current = (char *)name; if (*current == '/') goto cleanup; if (normalize) git_buf_clear(buf); #ifdef GIT_USE_ICONV if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) { size_t namelen = strlen(current); if ((error = git_path_iconv_init_precompose(&ic)) < 0 || (error = git_path_iconv(&ic, ¤t, &namelen)) < 0) goto cleanup; error = GIT_EINVALIDSPEC; } #endif while (true) { segment_len = ensure_segment_validity(current); if (segment_len < 0) { if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && current[0] == '*' && (current[1] == '\0' || current[1] == '/')) { /* Accept one wildcard as a full refname component. */ process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN; segment_len = 1; } else goto cleanup; } if (segment_len > 0) { if (normalize) { size_t cur_len = git_buf_len(buf); git_buf_joinpath(buf, git_buf_cstr(buf), current); git_buf_truncate(buf, cur_len + segment_len + (segments_count ? 1 : 0)); if (git_buf_oom(buf)) { error = -1; goto cleanup; } } segments_count++; } /* No empty segment is allowed when not normalizing */ if (segment_len == 0 && !normalize) goto cleanup; if (current[segment_len] == '\0') break; current += segment_len + 1; } /* A refname can not be empty */ if (segment_len == 0 && segments_count == 0) goto cleanup; /* A refname can not end with "." */ if (current[segment_len - 1] == '.') goto cleanup; /* A refname can not end with "/" */ if (current[segment_len - 1] == '/') goto cleanup; if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL)) goto cleanup; if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) && !(is_all_caps_and_underscore(name, (size_t)segment_len) || ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name)))) goto cleanup; if ((segments_count > 1) && (is_all_caps_and_underscore(name, strchr(name, '/') - name))) goto cleanup; error = 0; cleanup: if (error == GIT_EINVALIDSPEC) giterr_set( GITERR_REFERENCE, "The given reference name '%s' is not valid", name); if (error && normalize) git_buf_free(buf); #ifdef GIT_USE_ICONV git_path_iconv_clear(&ic); #endif return error; }
int git_rebase_open( git_rebase **out, git_repository *repo, const git_rebase_options *given_opts) { git_rebase *rebase; git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT, orig_head_id = GIT_BUF_INIT, onto_id = GIT_BUF_INIT; int state_path_len, error; assert(repo); if ((error = rebase_check_versions(given_opts)) < 0) return error; if (rebase_alloc(&rebase, given_opts) < 0) return -1; rebase->repo = repo; if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0) goto done; if (rebase->type == GIT_REBASE_TYPE_NONE) { giterr_set(GITERR_REBASE, "There is no rebase in progress"); error = GIT_ENOTFOUND; goto done; } if ((error = git_buf_puts(&path, rebase->state_path)) < 0) goto done; state_path_len = git_buf_len(&path); if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 || (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0) goto done; git_buf_rtrim(&orig_head_name); if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0) rebase->head_detached = 1; git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0) goto done; if (!git_path_isfile(path.ptr)) { /* Previous versions of git.git used 'head' here; support that. */ git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0) goto done; } if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0) goto done; git_buf_rtrim(&orig_head_id); if ((error = git_oid_fromstr(&rebase->orig_head_id, orig_head_id.ptr)) < 0) goto done; git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, ONTO_FILE)) < 0 || (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0) goto done; git_buf_rtrim(&onto_id); if ((error = git_oid_fromstr(&rebase->onto_id, onto_id.ptr)) < 0) goto done; if (!rebase->head_detached) rebase->orig_head_name = git_buf_detach(&orig_head_name); switch (rebase->type) { case GIT_REBASE_TYPE_INTERACTIVE: giterr_set(GITERR_REBASE, "Interactive rebase is not supported"); error = -1; break; case GIT_REBASE_TYPE_MERGE: error = rebase_open_merge(rebase); break; case GIT_REBASE_TYPE_APPLY: giterr_set(GITERR_REBASE, "Patch application rebase is not supported"); error = -1; break; default: abort(); } done: if (error == 0) *out = rebase; else git_rebase_free(rebase); git_buf_free(&path); git_buf_free(&orig_head_name); git_buf_free(&orig_head_id); git_buf_free(&onto_id); return error; }
static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix) { ssize_t idx = git_buf_rfind(key, '.'); git_buf_truncate(key, (size_t)(idx + 1)); return git_buf_puts(key, suffix); }
/* Locate an object matching a given short oid */ static int locate_object_short_oid( git_buf *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, size_t len) { char *objects_dir = backend->objects_dir; size_t dir_len = strlen(objects_dir), alloc_len; loose_locate_object_state state; int error; /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3); if (git_buf_grow(object_location, alloc_len) < 0) return -1; git_buf_set(object_location, objects_dir, dir_len); git_path_to_dir(object_location); /* save adjusted position at end of dir so it can be restored later */ dir_len = git_buf_len(object_location); /* Convert raw oid to hex formatted oid */ git_oid_fmt((char *)state.short_oid, short_oid); /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0) return -1; object_location->ptr[object_location->size - 1] = '/'; /* Check that directory exists */ if (git_path_isdir(object_location->ptr) == false) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); state.dir_len = git_buf_len(object_location); state.short_oid_len = len; state.found = 0; /* Explore directory to find a unique object matching short_oid */ error = git_path_direach( object_location, 0, fn_locate_object_short_oid, &state); if (error < 0 && error != GIT_EAMBIGUOUS) return error; if (!state.found) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); if (state.found > 1) return git_odb__error_ambiguous("multiple matches in loose objects"); /* Convert obtained hex formatted oid to raw */ error = git_oid_fromstr(res_oid, (char *)state.res_oid); if (error) return error; /* Update the location according to the oid obtained */ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); git_buf_truncate(object_location, dir_len); if (git_buf_grow(object_location, alloc_len) < 0) return -1; git_oid_pathfmt(object_location->ptr + dir_len, res_oid); object_location->size += GIT_OID_HEXSZ + 1; object_location->ptr[object_location->size] = '\0'; return 0; }
static int git_diff_driver_load( git_diff_driver **out, git_repository *repo, const char *driver_name) { int error = 0; git_diff_driver_registry *reg; git_diff_driver *drv = NULL; size_t namelen = strlen(driver_name); khiter_t pos; git_config *cfg; git_buf name = GIT_BUF_INIT; const git_config_entry *ce; bool found_driver = false; if ((reg = git_repository_driver_registry(repo)) == NULL) return -1; pos = git_strmap_lookup_index(reg->drivers, driver_name); if (git_strmap_valid_index(reg->drivers, pos)) { *out = git_strmap_value_at(reg->drivers, pos); return 0; } drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); GITERR_CHECK_ALLOC(drv); drv->type = DIFF_DRIVER_AUTO; memcpy(drv->name, driver_name, namelen); /* if you can't read config for repo, just use default driver */ if (git_repository_config_snapshot(&cfg, repo) < 0) { giterr_clear(); goto done; } if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) goto done; switch (git_config__get_bool_force(cfg, name.ptr, -1)) { case true: /* if diff.<driver>.binary is true, just return the binary driver */ *out = &global_drivers[DIFF_DRIVER_BINARY]; goto done; case false: /* if diff.<driver>.binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ drv->binary_flags = GIT_DIFF_FORCE_TEXT; found_driver = true; break; default: /* diff.<driver>.binary unspecified or "auto", so just continue */ break; } /* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */ git_buf_truncate(&name, namelen + strlen("diff..")); git_buf_put(&name, "xfuncname", strlen("xfuncname")); if ((error = git_config_get_multivar_foreach( cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) { if (error != GIT_ENOTFOUND) goto done; giterr_clear(); /* no diff.<driver>.xfuncname, so just continue */ } git_buf_truncate(&name, namelen + strlen("diff..")); git_buf_put(&name, "funcname", strlen("funcname")); if ((error = git_config_get_multivar_foreach( cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) { if (error != GIT_ENOTFOUND) goto done; giterr_clear(); /* no diff.<driver>.funcname, so just continue */ } /* if we found any patterns, set driver type to use correct callback */ if (git_array_size(drv->fn_patterns) > 0) { drv->type = DIFF_DRIVER_PATTERNLIST; found_driver = true; } git_buf_truncate(&name, namelen + strlen("diff..")); git_buf_put(&name, "wordregex", strlen("wordregex")); if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0) goto done; if (!ce || !ce->value) /* no diff.<driver>.wordregex, so just continue */; else if (!(error = regcomp(&drv->word_pattern, ce->value, REG_EXTENDED))) found_driver = true; else { /* TODO: warn about bad regex instead of failure */ error = giterr_set_regex(&drv->word_pattern, error); goto done; } /* TODO: look up diff.<driver>.algorithm to turn on minimal / patience * diff in drv->other_flags */ /* if no driver config found at all, fall back on AUTO driver */ if (!found_driver) goto done; /* store driver in registry */ git_strmap_insert(reg->drivers, drv->name, drv, error); if (error < 0) goto done; error = 0; *out = drv; done: git_buf_free(&name); git_config_free(cfg); if (!*out) { int error2 = git_diff_driver_builtin(out, reg, driver_name); if (!error) error = error2; } if (drv && drv != *out) git_diff_driver_free(drv); return error; }