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"
30 #include "repository.h"
32 #include "system-call.h"
33 #include "filter-filter.h"
35 struct filter_filter
{
41 static int filter_check(
43 void **payload
, /* points to NULL ptr on entry, may be set */
44 const git_filter_source
*src
,
45 const char **attr_values
)
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]);
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
;
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
, '%');
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
);
90 git_buf_printf(&expanded
, "\"%s\"", replaceWith
);
99 git_buf_put(&expanded
, lastPercentage
, idx
- lastPercentage
);
100 if (git_buf_oom(&expanded
))
105 git_buf_swap(buf
, &expanded
);
106 git_buf_free(&expanded
);
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
);
116 giterr_set(GITERR_FILTER
, "External filter application exited non-zero: %ld", exitCode
);
119 static int filter_apply(
121 void **payload
, /* may be read and/or set */
124 const git_filter_source
*src
)
126 struct filter_filter
*ffs
= (struct filter_filter
*)self
;
128 git_buf configKey
= GIT_BUF_INIT
;
129 int isRequired
= FALSE
;
131 git_buf cmd
= GIT_BUF_INIT
;
133 COMMAND_HANDLE commandHandle
;
134 git_buf errBuf
= GIT_BUF_INIT
;
138 return GIT_PASSTHROUGH
;
140 if (git_repository_config__weakptr(&config
, git_filter_source_repo(src
)))
143 git_buf_join3(&configKey
, '.', "filter", *payload
, "required");
144 if (git_buf_oom(&configKey
)) {
149 error
= git_config_get_bool(&isRequired
, config
, configKey
.ptr
);
150 git_buf_free(&configKey
);
151 if (error
&& error
!= GIT_ENOTFOUND
)
154 git_buf_join(&configKey
, '.', "filter", *payload
);
155 if (git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
) {
156 git_buf_puts(&configKey
, ".smudge");
158 git_buf_puts(&configKey
, ".clean");
160 if (git_buf_oom(&configKey
)) {
165 error
= git_config_get_string_buf(&cmd
, config
, configKey
.ptr
);
166 git_buf_free(&configKey
);
167 if (error
&& error
!= GIT_ENOTFOUND
)
170 if (error
== GIT_ENOTFOUND
) {
173 return GIT_PASSTHROUGH
;
176 if (expandPerCentF(&cmd
, git_filter_source_path(src
)))
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
)) {
190 git_buf_swap(&shParams
, &cmd
);
191 git_buf_free(&shParams
);
194 if (git__utf8_to_16_alloc(&wide_cmd
, cmd
.ptr
) < 0)
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));
211 wcscat_s(tmp
, len
, ffs
->shexepath
);
212 wcscat_s(tmp
, len
, wide_cmd
);
217 command_init(&commandHandle
);
218 commandHandle
.errBuf
= &errBuf
;
219 if (command_start(wide_cmd
, &commandHandle
, ffs
->pEnv
, 0)) {
223 return GIT_PASSTHROUGH
;
227 if (commmand_start_stdout_reading_thread(&commandHandle
, to
)) {
228 command_close(&commandHandle
);
232 if (command_write_gitbuf(&commandHandle
, from
)) {
233 DWORD exitCode
= command_close(&commandHandle
);
235 setProcessError(exitCode
, &errBuf
);
236 git_buf_free(&errBuf
);
239 return GIT_PASSTHROUGH
;
241 command_close_stdin(&commandHandle
);
243 if (command_wait_stdout_reading_thread(&commandHandle
)) {
244 DWORD exitCode
= command_close(&commandHandle
);
246 setProcessError(exitCode
, &errBuf
);
247 git_buf_free(&errBuf
);
250 return GIT_PASSTHROUGH
;
253 exitCode
= command_close(&commandHandle
);
256 setProcessError(exitCode
, &errBuf
);
257 git_buf_free(&errBuf
);
260 git_buf_free(&errBuf
);
261 return GIT_PASSTHROUGH
;
264 git_buf_free(&errBuf
);
269 static void filter_cleanup(
277 static void filter_free(git_filter
*self
)
279 struct filter_filter
*ffs
= (struct filter_filter
*)self
;
282 git__free(ffs
->shexepath
);
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
;
299 if (shexepath
&& shexepath
[0])
300 f
->shexepath
= wcsdup(shexepath
);
303 return (git_filter
*)f
;