/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "reader.h" #include "futils.h" #include "blob.h" #include "git2/tree.h" #include "git2/blob.h" #include "git2/index.h" #include "git2/repository.h" /* tree reader */ typedef struct { git_reader reader; git_tree *tree; } tree_reader; static int tree_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *_reader, const char *filename) { tree_reader *reader = (tree_reader *)_reader; git_tree_entry *tree_entry = NULL; git_blob *blob = NULL; git_object_size_t blobsize; int error; if ((error = git_tree_entry_bypath(&tree_entry, reader->tree, filename)) < 0 || (error = git_blob_lookup(&blob, git_tree_owner(reader->tree), git_tree_entry_id(tree_entry))) < 0) goto done; blobsize = git_blob_rawsize(blob); GIT_ERROR_CHECK_BLOBSIZE(blobsize); if ((error = git_buf_set(out, git_blob_rawcontent(blob), (size_t)blobsize)) < 0) goto done; if (out_id) git_oid_cpy(out_id, git_tree_entry_id(tree_entry)); if (out_filemode) *out_filemode = git_tree_entry_filemode(tree_entry); done: git_blob_free(blob); git_tree_entry_free(tree_entry); return error; } int git_reader_for_tree(git_reader **out, git_tree *tree) { tree_reader *reader; assert(out && tree); reader = git__calloc(1, sizeof(tree_reader)); GIT_ERROR_CHECK_ALLOC(reader); reader->reader.read = tree_reader_read; reader->tree = tree; *out = (git_reader *)reader; return 0; } /* workdir reader */ typedef struct { git_reader reader; git_repository *repo; git_index *index; } workdir_reader; static int workdir_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *_reader, const char *filename) { workdir_reader *reader = (workdir_reader *)_reader; git_buf path = GIT_BUF_INIT; struct stat st; git_filemode_t filemode; git_filter_list *filters = NULL; const git_index_entry *idx_entry; git_oid id; int error; if ((error = git_buf_joinpath(&path, git_repository_workdir(reader->repo), filename)) < 0) goto done; if ((error = p_lstat(path.ptr, &st)) < 0) { if (error == -1 && errno == ENOENT) error = GIT_ENOTFOUND; git_error_set(GIT_ERROR_OS, "could not stat '%s'", path.ptr); goto done; } filemode = git_futils_canonical_mode(st.st_mode); /* * Patch application - for example - uses the filtered version of * the working directory data to match git. So we will run the * workdir -> ODB filter on the contents in this workdir reader. */ if ((error = git_filter_list_load(&filters, reader->repo, NULL, filename, GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT)) < 0) goto done; if ((error = git_filter_list_apply_to_file(out, filters, reader->repo, path.ptr)) < 0) goto done; if (out_id || reader->index) { if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB)) < 0) goto done; } if (reader->index) { if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) || filemode != idx_entry->mode || !git_oid_equal(&id, &idx_entry->id)) { error = GIT_READER_MISMATCH; goto done; } } if (out_id) git_oid_cpy(out_id, &id); if (out_filemode) *out_filemode = filemode; done: git_filter_list_free(filters); git_buf_dispose(&path); return error; } int git_reader_for_workdir( git_reader **out, git_repository *repo, bool validate_index) { workdir_reader *reader; int error; assert(out && repo); reader = git__calloc(1, sizeof(workdir_reader)); GIT_ERROR_CHECK_ALLOC(reader); reader->reader.read = workdir_reader_read; reader->repo = repo; if (validate_index && (error = git_repository_index__weakptr(&reader->index, repo)) < 0) { git__free(reader); return error; } *out = (git_reader *)reader; return 0; } /* index reader */ typedef struct { git_reader reader; git_repository *repo; git_index *index; } index_reader; static int index_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *_reader, const char *filename) { index_reader *reader = (index_reader *)_reader; const git_index_entry *entry; git_blob *blob; int error; if ((entry = git_index_get_bypath(reader->index, filename, 0)) == NULL) return GIT_ENOTFOUND; if ((error = git_blob_lookup(&blob, reader->repo, &entry->id)) < 0) goto done; if (out_id) git_oid_cpy(out_id, &entry->id); if (out_filemode) *out_filemode = entry->mode; error = git_blob__getbuf(out, blob); done: git_blob_free(blob); return error; } int git_reader_for_index( git_reader **out, git_repository *repo, git_index *index) { index_reader *reader; int error; assert(out && repo); reader = git__calloc(1, sizeof(index_reader)); GIT_ERROR_CHECK_ALLOC(reader); reader->reader.read = index_reader_read; reader->repo = repo; if (index) { reader->index = index; } else if ((error = git_repository_index__weakptr(&reader->index, repo)) < 0) { git__free(reader); return error; } *out = (git_reader *)reader; return 0; } /* generic */ int git_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *reader, const char *filename) { assert(out && reader && filename); return reader->read(out, out_id, out_filemode, reader, filename); } void git_reader_free(git_reader *reader) { if (!reader) return; git__free(reader); }