| /* |
| * 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); |
| } |