]>
Commit | Line | Data |
---|---|---|
ac3d33df JK |
1 | /* |
2 | * Copyright (C) the libgit2 contributors. All rights reserved. | |
3 | * | |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | ||
8 | #include "reader.h" | |
9 | ||
10 | #include "fileops.h" | |
11 | #include "blob.h" | |
12 | ||
13 | #include "git2/tree.h" | |
14 | #include "git2/blob.h" | |
15 | #include "git2/index.h" | |
16 | #include "git2/repository.h" | |
17 | ||
18 | /* tree reader */ | |
19 | ||
20 | typedef struct { | |
21 | git_reader reader; | |
22 | git_tree *tree; | |
23 | } tree_reader; | |
24 | ||
25 | static int tree_reader_read( | |
26 | git_buf *out, | |
27 | git_oid *out_id, | |
28 | git_filemode_t *out_filemode, | |
29 | git_reader *_reader, | |
30 | const char *filename) | |
31 | { | |
32 | tree_reader *reader = (tree_reader *)_reader; | |
33 | git_tree_entry *tree_entry = NULL; | |
34 | git_blob *blob = NULL; | |
35 | git_off_t blobsize; | |
36 | int error; | |
37 | ||
38 | if ((error = git_tree_entry_bypath(&tree_entry, reader->tree, filename)) < 0 || | |
39 | (error = git_blob_lookup(&blob, git_tree_owner(reader->tree), git_tree_entry_id(tree_entry))) < 0) | |
40 | goto done; | |
41 | ||
42 | blobsize = git_blob_rawsize(blob); | |
43 | GIT_ERROR_CHECK_BLOBSIZE(blobsize); | |
44 | ||
45 | if ((error = git_buf_set(out, git_blob_rawcontent(blob), (size_t)blobsize)) < 0) | |
46 | goto done; | |
47 | ||
48 | if (out_id) | |
49 | git_oid_cpy(out_id, git_tree_entry_id(tree_entry)); | |
50 | ||
51 | if (out_filemode) | |
52 | *out_filemode = git_tree_entry_filemode(tree_entry); | |
53 | ||
54 | done: | |
55 | git_blob_free(blob); | |
56 | git_tree_entry_free(tree_entry); | |
57 | return error; | |
58 | } | |
59 | ||
60 | int git_reader_for_tree(git_reader **out, git_tree *tree) | |
61 | { | |
62 | tree_reader *reader; | |
63 | ||
64 | assert(out && tree); | |
65 | ||
66 | reader = git__calloc(1, sizeof(tree_reader)); | |
67 | GIT_ERROR_CHECK_ALLOC(reader); | |
68 | ||
69 | reader->reader.read = tree_reader_read; | |
70 | reader->tree = tree; | |
71 | ||
72 | *out = (git_reader *)reader; | |
73 | return 0; | |
74 | } | |
75 | ||
76 | /* workdir reader */ | |
77 | ||
78 | typedef struct { | |
79 | git_reader reader; | |
80 | git_repository *repo; | |
81 | git_index *index; | |
82 | } workdir_reader; | |
83 | ||
84 | static int workdir_reader_read( | |
85 | git_buf *out, | |
86 | git_oid *out_id, | |
87 | git_filemode_t *out_filemode, | |
88 | git_reader *_reader, | |
89 | const char *filename) | |
90 | { | |
91 | workdir_reader *reader = (workdir_reader *)_reader; | |
92 | git_buf path = GIT_BUF_INIT; | |
93 | struct stat st; | |
94 | git_filemode_t filemode; | |
95 | git_filter_list *filters = NULL; | |
96 | const git_index_entry *idx_entry; | |
97 | git_oid id; | |
98 | int error; | |
99 | ||
100 | if ((error = git_buf_joinpath(&path, | |
101 | git_repository_workdir(reader->repo), filename)) < 0) | |
102 | goto done; | |
103 | ||
104 | if ((error = p_lstat(path.ptr, &st)) < 0) { | |
105 | if (error == -1 && errno == ENOENT) | |
106 | error = GIT_ENOTFOUND; | |
107 | ||
108 | git_error_set(GIT_ERROR_OS, "could not stat '%s'", path.ptr); | |
109 | goto done; | |
110 | } | |
111 | ||
112 | filemode = git_futils_canonical_mode(st.st_mode); | |
113 | ||
114 | /* | |
115 | * Patch application - for example - uses the filtered version of | |
116 | * the working directory data to match git. So we will run the | |
117 | * workdir -> ODB filter on the contents in this workdir reader. | |
118 | */ | |
119 | if ((error = git_filter_list_load(&filters, reader->repo, NULL, filename, | |
120 | GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT)) < 0) | |
121 | goto done; | |
122 | ||
123 | if ((error = git_filter_list_apply_to_file(out, | |
124 | filters, reader->repo, path.ptr)) < 0) | |
125 | goto done; | |
126 | ||
127 | if (out_id || reader->index) { | |
128 | if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB)) < 0) | |
129 | goto done; | |
130 | } | |
131 | ||
132 | if (reader->index) { | |
133 | if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) || | |
134 | filemode != idx_entry->mode || | |
135 | !git_oid_equal(&id, &idx_entry->id)) { | |
136 | error = GIT_READER_MISMATCH; | |
137 | goto done; | |
138 | } | |
139 | } | |
140 | ||
141 | if (out_id) | |
142 | git_oid_cpy(out_id, &id); | |
143 | ||
144 | if (out_filemode) | |
145 | *out_filemode = filemode; | |
146 | ||
147 | done: | |
148 | git_filter_list_free(filters); | |
149 | git_buf_dispose(&path); | |
150 | return error; | |
151 | } | |
152 | ||
153 | int git_reader_for_workdir( | |
154 | git_reader **out, | |
155 | git_repository *repo, | |
156 | bool validate_index) | |
157 | { | |
158 | workdir_reader *reader; | |
159 | int error; | |
160 | ||
161 | assert(out && repo); | |
162 | ||
163 | reader = git__calloc(1, sizeof(workdir_reader)); | |
164 | GIT_ERROR_CHECK_ALLOC(reader); | |
165 | ||
166 | reader->reader.read = workdir_reader_read; | |
167 | reader->repo = repo; | |
168 | ||
169 | if (validate_index && | |
170 | (error = git_repository_index__weakptr(&reader->index, repo)) < 0) { | |
171 | git__free(reader); | |
172 | return error; | |
173 | } | |
174 | ||
175 | *out = (git_reader *)reader; | |
176 | return 0; | |
177 | } | |
178 | ||
179 | /* index reader */ | |
180 | ||
181 | typedef struct { | |
182 | git_reader reader; | |
183 | git_repository *repo; | |
184 | git_index *index; | |
185 | } index_reader; | |
186 | ||
187 | static int index_reader_read( | |
188 | git_buf *out, | |
189 | git_oid *out_id, | |
190 | git_filemode_t *out_filemode, | |
191 | git_reader *_reader, | |
192 | const char *filename) | |
193 | { | |
194 | index_reader *reader = (index_reader *)_reader; | |
195 | const git_index_entry *entry; | |
196 | git_blob *blob; | |
197 | int error; | |
198 | ||
199 | if ((entry = git_index_get_bypath(reader->index, filename, 0)) == NULL) | |
200 | return GIT_ENOTFOUND; | |
201 | ||
202 | if ((error = git_blob_lookup(&blob, reader->repo, &entry->id)) < 0) | |
203 | goto done; | |
204 | ||
205 | if (out_id) | |
206 | git_oid_cpy(out_id, &entry->id); | |
207 | ||
208 | if (out_filemode) | |
209 | *out_filemode = entry->mode; | |
210 | ||
211 | error = git_blob__getbuf(out, blob); | |
212 | ||
213 | done: | |
214 | git_blob_free(blob); | |
215 | return error; | |
216 | } | |
217 | ||
218 | int git_reader_for_index( | |
219 | git_reader **out, | |
220 | git_repository *repo, | |
221 | git_index *index) | |
222 | { | |
223 | index_reader *reader; | |
224 | int error; | |
225 | ||
226 | assert(out && repo); | |
227 | ||
228 | reader = git__calloc(1, sizeof(index_reader)); | |
229 | GIT_ERROR_CHECK_ALLOC(reader); | |
230 | ||
231 | reader->reader.read = index_reader_read; | |
232 | reader->repo = repo; | |
233 | ||
234 | if (index) { | |
235 | reader->index = index; | |
236 | } else if ((error = git_repository_index__weakptr(&reader->index, repo)) < 0) { | |
237 | git__free(reader); | |
238 | return error; | |
239 | } | |
240 | ||
241 | *out = (git_reader *)reader; | |
242 | return 0; | |
243 | } | |
244 | ||
245 | /* generic */ | |
246 | ||
247 | int git_reader_read( | |
248 | git_buf *out, | |
249 | git_oid *out_id, | |
250 | git_filemode_t *out_filemode, | |
251 | git_reader *reader, | |
252 | const char *filename) | |
253 | { | |
254 | assert(out && reader && filename); | |
255 | ||
256 | return reader->read(out, out_id, out_filemode, reader, filename); | |
257 | } | |
258 | ||
259 | void git_reader_free(git_reader *reader) | |
260 | { | |
261 | if (!reader) | |
262 | return; | |
263 | ||
264 | git__free(reader); | |
265 | } |