RepositoryBrowser: Add drag handler
[TortoiseGit.git] / src / libgit2 / filter-filter.c
blob081f6bd723271f058c57f0b090048e7dca22b59c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2014, 2016 TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "git2/attr.h"
21 #include "git2/blob.h"
22 #include "git2/index.h"
23 #include "git2/sys/filter.h"
25 #include "common.h"
26 #include "fileops.h"
27 #include "hash.h"
28 #include "filter.h"
29 #include "buf_text.h"
30 #include "repository.h"
32 #include "system-call.h"
33 #include "filter-filter.h"
35 struct filter_filter {
36 git_filter f;
37 LPWSTR* pEnv;
38 LPWSTR shexepath;
41 static int filter_check(
42 git_filter *self,
43 void **payload, /* points to NULL ptr on entry, may be set */
44 const git_filter_source *src,
45 const char **attr_values)
47 GIT_UNUSED(self);
48 GIT_UNUSED(src);
50 if (GIT_ATTR_UNSPECIFIED(attr_values[0]))
51 return GIT_PASSTHROUGH;
53 if (GIT_ATTR_FALSE(attr_values[0]))
54 return GIT_PASSTHROUGH;
56 if (GIT_ATTR_TRUE(attr_values[0]))
57 return GIT_PASSTHROUGH;
59 *payload = git__strdup(attr_values[0]);
60 if (!*payload)
62 giterr_set_oom();
63 return -1;
66 return 0;
69 static int expandPerCentF(git_buf *buf, const char *replaceWith)
71 ssize_t foundPercentage = git_buf_find(buf, '%');
72 if (foundPercentage) {
73 git_buf expanded = GIT_BUF_INIT;
74 const char *end = buf->ptr + buf->size;
75 const char *lastPercentage = buf->ptr;
76 const char *idx = buf->ptr + foundPercentage;
77 while (idx < end) {
78 if (*idx == '%') {
79 if (idx + 1 == end || (idx + 1 < end && *(idx + 1) == '%')) { // one '%' is at the end of the string OR "%%" is in the string
80 git_buf_putc(&expanded, '%');
81 ++idx;
82 ++idx;
83 lastPercentage = idx;
84 continue;
86 // now we know, that we're not at the end of the string and that the next char is not '%'
87 git_buf_put(&expanded, lastPercentage, idx - lastPercentage);
88 ++idx;
89 if (*idx == 'f')
90 git_buf_printf(&expanded, "\"%s\"", replaceWith);
92 ++idx;
93 lastPercentage = idx;
94 continue;
96 ++idx;
98 if (lastPercentage)
99 git_buf_put(&expanded, lastPercentage, idx - lastPercentage);
100 if (git_buf_oom(&expanded))
102 giterr_set_oom();
103 return -1;
105 git_buf_swap(buf, &expanded);
106 git_buf_free(&expanded);
108 return 0;
111 static void setProcessError(DWORD exitCode, git_buf *errBuf)
113 if (!git_buf_oom(errBuf) && git_buf_len(errBuf))
114 giterr_set(GITERR_FILTER, "External filter application exited non-zero (%ld) and reported:\n%s", exitCode, errBuf->ptr);
115 else
116 giterr_set(GITERR_FILTER, "External filter application exited non-zero: %ld", exitCode);
119 static int filter_apply(
120 git_filter *self,
121 void **payload, /* may be read and/or set */
122 git_buf *to,
123 const git_buf *from,
124 const git_filter_source *src)
126 struct filter_filter *ffs = (struct filter_filter *)self;
127 git_config *config;
128 git_buf configKey = GIT_BUF_INIT;
129 int isRequired = FALSE;
130 int error;
131 git_buf cmd = GIT_BUF_INIT;
132 wchar_t *wide_cmd;
133 COMMAND_HANDLE commandHandle;
134 git_buf errBuf = GIT_BUF_INIT;
135 DWORD exitCode;
137 if (!*payload)
138 return GIT_PASSTHROUGH;
140 if (git_repository_config__weakptr(&config, git_filter_source_repo(src)))
141 return -1;
143 git_buf_join3(&configKey, '.', "filter", *payload, "required");
144 if (git_buf_oom(&configKey)) {
145 giterr_set_oom();
146 return -1;
149 error = git_config_get_bool(&isRequired, config, configKey.ptr);
150 git_buf_free(&configKey);
151 if (error && error != GIT_ENOTFOUND)
152 return -1;
154 git_buf_join(&configKey, '.', "filter", *payload);
155 if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE) {
156 git_buf_puts(&configKey, ".smudge");
157 } else {
158 git_buf_puts(&configKey, ".clean");
160 if (git_buf_oom(&configKey)) {
161 giterr_set_oom();
162 return -1;
165 error = git_config_get_string_buf(&cmd, config, configKey.ptr);
166 git_buf_free(&configKey);
167 if (error && error != GIT_ENOTFOUND)
168 return -1;
170 if (error == GIT_ENOTFOUND) {
171 if (isRequired)
172 return -1;
173 return GIT_PASSTHROUGH;
176 if (expandPerCentF(&cmd, git_filter_source_path(src)))
177 return -1;
179 if (ffs->shexepath) {
180 // build params for sh.exe
181 git_buf shParams = GIT_BUF_INIT;
182 git_buf_puts(&shParams, " -c \"");
183 git_buf_text_puts_escaped(&shParams, cmd.ptr, "\"\\", "\\");
184 git_buf_puts(&shParams, "\"");
185 if (git_buf_oom(&shParams)) {
186 git_buf_free(&cmd);
187 giterr_set_oom();
188 return -1;
190 git_buf_swap(&shParams, &cmd);
191 git_buf_free(&shParams);
194 if (git__utf8_to_16_alloc(&wide_cmd, cmd.ptr) < 0)
196 git_buf_free(&cmd);
197 giterr_set_oom();
198 return -1;
200 git_buf_free(&cmd);
202 if (ffs->shexepath) {
203 // build cmd, i.e. shexepath + params
204 size_t len = wcslen(ffs->shexepath) + wcslen(wide_cmd) + 1;
205 wchar_t *tmp = git__calloc(len, sizeof(wchar_t));
206 if (!tmp) {
207 git__free(wide_cmd);
208 giterr_set_oom();
209 return -1;
211 wcscat_s(tmp, len, ffs->shexepath);
212 wcscat_s(tmp, len, wide_cmd);
213 git__free(wide_cmd);
214 wide_cmd = tmp;
217 command_init(&commandHandle);
218 commandHandle.errBuf = &errBuf;
219 if (command_start(wide_cmd, &commandHandle, ffs->pEnv, 0)) {
220 git__free(wide_cmd);
221 if (isRequired)
222 return -1;
223 return GIT_PASSTHROUGH;
225 git__free(wide_cmd);
227 if (commmand_start_stdout_reading_thread(&commandHandle, to)) {
228 command_close(&commandHandle);
229 return -1;
232 if (command_write_gitbuf(&commandHandle, from)) {
233 DWORD exitCode = command_close(&commandHandle);
234 if (exitCode)
235 setProcessError(exitCode, &errBuf);
236 git_buf_free(&errBuf);
237 if (isRequired)
238 return -1;
239 return GIT_PASSTHROUGH;
241 command_close_stdin(&commandHandle);
243 if (command_wait_stdout_reading_thread(&commandHandle)) {
244 DWORD exitCode = command_close(&commandHandle);
245 if (exitCode)
246 setProcessError(exitCode, &errBuf);
247 git_buf_free(&errBuf);
248 if (isRequired)
249 return -1;
250 return GIT_PASSTHROUGH;
253 exitCode = command_close(&commandHandle);
254 if (exitCode) {
255 if (isRequired) {
256 setProcessError(exitCode, &errBuf);
257 git_buf_free(&errBuf);
258 return -1;
260 git_buf_free(&errBuf);
261 return GIT_PASSTHROUGH;
264 git_buf_free(&errBuf);
266 return 0;
269 static void filter_cleanup(
270 git_filter *self,
271 void *payload)
273 GIT_UNUSED(self);
274 git__free(payload);
277 static void filter_free(git_filter *self)
279 struct filter_filter *ffs = (struct filter_filter *)self;
281 if (ffs->shexepath)
282 git__free(ffs->shexepath);
284 git__free(self);
287 git_filter *git_filter_filter_new(LPCWSTR shexepath, LPWSTR* pEnv)
289 struct filter_filter *f = git__calloc(1, sizeof(struct filter_filter));
291 f->f.version = GIT_FILTER_VERSION;
292 f->f.attributes = "filter";
293 f->f.initialize = NULL;
294 f->f.shutdown = filter_free;
295 f->f.check = filter_check;
296 f->f.apply = filter_apply;
297 f->f.cleanup = filter_cleanup;
298 f->shexepath = NULL;
299 if (shexepath && wcslen(shexepath) > 0)
300 f->shexepath = wcsdup(shexepath);
301 f->pEnv = pEnv;
303 return (git_filter *)f;