1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2014 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 const char *cmd
= NULL
;
132 git_buf cmdBuf
= GIT_BUF_INIT
;
134 COMMAND_HANDLE commandHandle
;
135 git_buf errBuf
= GIT_BUF_INIT
;
139 return GIT_PASSTHROUGH
;
141 if (git_repository_config__weakptr(&config
, git_filter_source_repo(src
)))
144 git_buf_join3(&configKey
, '.', "filter", *payload
, "required");
145 if (git_buf_oom(&configKey
)) {
150 error
= git_config_get_bool(&isRequired
, config
, configKey
.ptr
);
151 git_buf_free(&configKey
);
152 if (error
&& error
!= GIT_ENOTFOUND
)
155 git_buf_join(&configKey
, '.', "filter", *payload
);
156 if (git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
) {
157 git_buf_puts(&configKey
, ".smudge");
159 git_buf_puts(&configKey
, ".clean");
161 if (git_buf_oom(&configKey
)) {
166 error
= git_config_get_string(&cmd
, config
, configKey
.ptr
);
167 git_buf_free(&configKey
);
168 if (error
&& error
!= GIT_ENOTFOUND
)
171 if (error
== GIT_ENOTFOUND
) {
174 return GIT_PASSTHROUGH
;
177 git_buf_puts(&cmdBuf
, cmd
);
178 if (git_buf_oom(&cmdBuf
)) {
183 if (expandPerCentF(&cmdBuf
, git_filter_source_path(src
)))
186 if (ffs
->shexepath
) {
187 // build params for sh.exe
188 git_buf shParams
= GIT_BUF_INIT
;
189 git_buf_puts(&shParams
, " -c \"");
190 git_buf_text_puts_escaped(&shParams
, cmdBuf
.ptr
, "\"\\", "\\");
191 git_buf_puts(&shParams
, "\"");
192 if (git_buf_oom(&shParams
)) {
193 git_buf_free(&cmdBuf
);
197 git_buf_swap(&shParams
, &cmdBuf
);
198 git_buf_free(&shParams
);
201 if (git__utf8_to_16_alloc(&wide_cmd
, cmdBuf
.ptr
) < 0)
203 git_buf_free(&cmdBuf
);
207 git_buf_free(&cmdBuf
);
209 if (ffs
->shexepath
) {
210 // build cmd, i.e. shexepath + params
211 size_t len
= wcslen(ffs
->shexepath
) + wcslen(wide_cmd
) + 1;
212 wchar_t *tmp
= git__calloc(len
, sizeof(wchar_t));
218 wcscat_s(tmp
, len
, ffs
->shexepath
);
219 wcscat_s(tmp
, len
, wide_cmd
);
224 command_init(&commandHandle
);
225 commandHandle
.errBuf
= &errBuf
;
226 if (command_start(wide_cmd
, &commandHandle
, ffs
->pEnv
, 0)) {
230 return GIT_PASSTHROUGH
;
234 if (commmand_start_stdout_reading_thread(&commandHandle
, to
)) {
235 command_close(&commandHandle
);
239 if (command_write_gitbuf(&commandHandle
, from
)) {
240 DWORD exitCode
= command_close(&commandHandle
);
242 setProcessError(exitCode
, &errBuf
);
243 git_buf_free(&errBuf
);
246 return GIT_PASSTHROUGH
;
248 command_close_stdin(&commandHandle
);
250 if (command_wait_stdout_reading_thread(&commandHandle
)) {
251 DWORD exitCode
= command_close(&commandHandle
);
253 setProcessError(exitCode
, &errBuf
);
254 git_buf_free(&errBuf
);
257 return GIT_PASSTHROUGH
;
260 exitCode
= command_close(&commandHandle
);
263 setProcessError(exitCode
, &errBuf
);
264 git_buf_free(&errBuf
);
267 git_buf_free(&errBuf
);
268 return GIT_PASSTHROUGH
;
271 git_buf_free(&errBuf
);
276 static void filter_cleanup(
284 static void filter_free(git_filter
*self
)
286 struct filter_filter
*ffs
= (struct filter_filter
*)self
;
289 git__free(ffs
->shexepath
);
294 git_filter
*git_filter_filter_new(LPCWSTR shexepath
, LPWSTR pEnv
)
296 struct filter_filter
*f
= git__calloc(1, sizeof(struct filter_filter
));
298 f
->f
.version
= GIT_FILTER_VERSION
;
299 f
->f
.attributes
= "filter";
300 f
->f
.initialize
= NULL
;
301 f
->f
.shutdown
= filter_free
;
302 f
->f
.check
= filter_check
;
303 f
->f
.apply
= filter_apply
;
304 f
->f
.cleanup
= filter_cleanup
;
305 f
->shexepath
= wcsdup(shexepath
);
308 return (git_filter
*)f
;