reader.c 5.48 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*
 * 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"

10
#include "futils.h"
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#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,
27
	git_oid *out_id,
28
	git_filemode_t *out_filemode,
29 30 31 32 33 34
	git_reader *_reader,
	const char *filename)
{
	tree_reader *reader = (tree_reader *)_reader;
	git_tree_entry *tree_entry = NULL;
	git_blob *blob = NULL;
35
	git_object_size_t blobsize;
36 37 38
	int error;

	if ((error = git_tree_entry_bypath(&tree_entry, reader->tree, filename)) < 0 ||
39 40 41 42 43 44 45
	    (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)
46 47
		goto done;

48 49 50
	if (out_id)
		git_oid_cpy(out_id, git_tree_entry_id(tree_entry));

51 52 53
	if (out_filemode)
		*out_filemode = git_tree_entry_filemode(tree_entry);

54 55 56 57 58 59 60 61 62 63
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;

64 65
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(tree);
66 67

	reader = git__calloc(1, sizeof(tree_reader));
68
	GIT_ERROR_CHECK_ALLOC(reader);
69 70 71 72 73 74 75 76 77 78 79 80 81

	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;
82
	git_index *index;
83 84 85 86
} workdir_reader;

static int workdir_reader_read(
	git_buf *out,
87
	git_oid *out_id,
88
	git_filemode_t *out_filemode,
89 90 91 92 93
	git_reader *_reader,
	const char *filename)
{
	workdir_reader *reader = (workdir_reader *)_reader;
	git_buf path = GIT_BUF_INIT;
94 95
	struct stat st;
	git_filemode_t filemode;
96
	git_filter_list *filters = NULL;
97 98
	const git_index_entry *idx_entry;
	git_oid id;
99 100
	int error;

101
	if ((error = git_repository_workdir_path(&path, reader->repo, filename)) < 0)
102 103
		goto done;

104 105 106 107
	if ((error = p_lstat(path.ptr, &st)) < 0) {
		if (error == -1 && errno == ENOENT)
			error = GIT_ENOTFOUND;

108
		git_error_set(GIT_ERROR_OS, "could not stat '%s'", path.ptr);
109 110 111 112 113
		goto done;
	}

	filemode = git_futils_canonical_mode(st.st_mode);

114 115 116 117 118 119 120 121 122 123 124
	/*
	 * 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)
125 126
		goto done;

127
	if (out_id || reader->index) {
128
		if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB)) < 0)
129 130 131 132 133
			goto done;
	}

	if (reader->index) {
		if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) ||
134
		    filemode != idx_entry->mode ||
135 136 137 138 139 140
		    !git_oid_equal(&id, &idx_entry->id)) {
			error = GIT_READER_MISMATCH;
			goto done;
		}
	}

141
	if (out_id)
142
		git_oid_cpy(out_id, &id);
143

144 145 146
	if (out_filemode)
		*out_filemode = filemode;

147
done:
148
	git_filter_list_free(filters);
149 150 151 152
	git_buf_dispose(&path);
	return error;
}

153 154 155 156
int git_reader_for_workdir(
	git_reader **out,
	git_repository *repo,
	bool validate_index)
157 158
{
	workdir_reader *reader;
159
	int error;
160

161 162
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(repo);
163 164

	reader = git__calloc(1, sizeof(workdir_reader));
165
	GIT_ERROR_CHECK_ALLOC(reader);
166 167 168 169

	reader->reader.read = workdir_reader_read;
	reader->repo = repo;

170 171 172 173 174 175
	if (validate_index &&
	    (error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
		git__free(reader);
		return error;
	}

176 177 178 179 180 181 182 183 184 185 186 187 188 189
	*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,
190
	git_oid *out_id,
191
	git_filemode_t *out_filemode,
192 193 194 195 196 197 198 199 200 201 202 203 204 205
	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;

206 207 208
	if (out_id)
		git_oid_cpy(out_id, &entry->id);

209 210 211
	if (out_filemode)
		*out_filemode = entry->mode;

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
	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;

227 228
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(repo);
229 230

	reader = git__calloc(1, sizeof(index_reader));
231
	GIT_ERROR_CHECK_ALLOC(reader);
232 233 234 235 236 237

	reader->reader.read = index_reader_read;
	reader->repo = repo;

	if (index) {
		reader->index = index;
238 239 240
	} else if ((error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
		git__free(reader);
		return error;
241 242 243 244 245 246 247 248
	}

	*out = (git_reader *)reader;
	return 0;
}

/* generic */

249 250 251
int git_reader_read(
	git_buf *out,
	git_oid *out_id,
252
	git_filemode_t *out_filemode,
253 254
	git_reader *reader,
	const char *filename)
255
{
256 257 258
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(reader);
	GIT_ASSERT_ARG(filename);
259

260
	return reader->read(out, out_id, out_filemode, reader, filename);
261 262 263 264 265 266 267 268 269
}

void git_reader_free(git_reader *reader)
{
	if (!reader)
		return;

	git__free(reader);
}