1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2014, 2016-2018 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
)
51 return GIT_PASSTHROUGH
;
53 if (GIT_ATTR_UNSPECIFIED(attr_values
[0]))
54 return GIT_PASSTHROUGH
;
56 if (GIT_ATTR_FALSE(attr_values
[0]))
57 return GIT_PASSTHROUGH
;
59 if (GIT_ATTR_TRUE(attr_values
[0]))
60 return GIT_PASSTHROUGH
;
62 *payload
= git__strdup(attr_values
[0]);
72 static int expandPerCentF(git_buf
*buf
, const char *replaceWith
)
74 ssize_t foundPercentage
= git_buf_find(buf
, '%');
75 if (foundPercentage
) {
76 git_buf expanded
= GIT_BUF_INIT
;
77 const char *end
= buf
->ptr
+ buf
->size
;
78 const char *lastPercentage
= buf
->ptr
;
79 const char *idx
= buf
->ptr
+ foundPercentage
;
82 if (idx
+ 1 == end
|| (idx
+ 1 < end
&& *(idx
+ 1) == '%')) { // one '%' is at the end of the string OR "%%" is in the string
83 git_buf_putc(&expanded
, '%');
89 // now we know, that we're not at the end of the string and that the next char is not '%'
90 git_buf_put(&expanded
, lastPercentage
, idx
- lastPercentage
);
93 git_buf_printf(&expanded
, "\"%s\"", replaceWith
);
102 git_buf_put(&expanded
, lastPercentage
, idx
- lastPercentage
);
103 if (git_buf_oom(&expanded
))
108 git_buf_swap(buf
, &expanded
);
109 git_buf_dispose(&expanded
);
114 static void setProcessError(DWORD exitCode
, git_buf
*errBuf
)
116 if (!git_buf_oom(errBuf
) && git_buf_len(errBuf
))
117 giterr_set(GITERR_FILTER
, "External filter application exited non-zero (%ld) and reported:\n%s", exitCode
, errBuf
->ptr
);
119 giterr_set(GITERR_FILTER
, "External filter application exited non-zero: %ld", exitCode
);
122 static int filter_apply(
124 void **payload
, /* may be read and/or set */
127 const git_filter_source
*src
)
129 struct filter_filter
*ffs
= (struct filter_filter
*)self
;
131 git_buf configKey
= GIT_BUF_INIT
;
132 int isRequired
= FALSE
;
134 git_buf cmd
= GIT_BUF_INIT
;
136 COMMAND_HANDLE commandHandle
;
137 git_buf errBuf
= GIT_BUF_INIT
;
141 return GIT_PASSTHROUGH
;
143 if (git_repository_config__weakptr(&config
, git_filter_source_repo(src
)))
146 git_buf_join3(&configKey
, '.', "filter", *payload
, "required");
147 if (git_buf_oom(&configKey
)) {
152 error
= git_config_get_bool(&isRequired
, config
, configKey
.ptr
);
153 git_buf_dispose(&configKey
);
154 if (error
&& error
!= GIT_ENOTFOUND
)
157 git_buf_join(&configKey
, '.', "filter", *payload
);
158 if (git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
) {
159 git_buf_puts(&configKey
, ".smudge");
161 git_buf_puts(&configKey
, ".clean");
163 if (git_buf_oom(&configKey
)) {
168 error
= git_config_get_string_buf(&cmd
, config
, configKey
.ptr
);
169 git_buf_dispose(&configKey
);
170 if (error
&& error
!= GIT_ENOTFOUND
)
173 if (error
== GIT_ENOTFOUND
) {
176 return GIT_PASSTHROUGH
;
179 if (expandPerCentF(&cmd
, git_filter_source_path(src
)))
182 if (ffs
->shexepath
) {
183 // build params for sh.exe
184 git_buf shParams
= GIT_BUF_INIT
;
185 git_buf_puts(&shParams
, " -c \"");
186 git_buf_text_puts_escaped(&shParams
, cmd
.ptr
, "\"\\", "\\");
187 git_buf_puts(&shParams
, "\"");
188 if (git_buf_oom(&shParams
)) {
189 git_buf_dispose(&cmd
);
193 git_buf_swap(&shParams
, &cmd
);
194 git_buf_dispose(&shParams
);
197 if (git__utf8_to_16_alloc(&wide_cmd
, cmd
.ptr
) < 0)
199 git_buf_dispose(&cmd
);
203 git_buf_dispose(&cmd
);
205 if (ffs
->shexepath
) {
206 // build cmd, i.e. shexepath + params
207 size_t len
= wcslen(ffs
->shexepath
) + wcslen(wide_cmd
) + 1;
208 wchar_t *tmp
= git__calloc(len
, sizeof(wchar_t));
214 wcscat_s(tmp
, len
, ffs
->shexepath
);
215 wcscat_s(tmp
, len
, wide_cmd
);
220 command_init(&commandHandle
);
221 commandHandle
.errBuf
= &errBuf
;
222 if (command_start(wide_cmd
, &commandHandle
, ffs
->pEnv
, 0)) {
226 return GIT_PASSTHROUGH
;
230 if (commmand_start_stdout_reading_thread(&commandHandle
, to
)) {
231 command_close(&commandHandle
);
235 if (command_write_gitbuf(&commandHandle
, from
)) {
236 exitCode
= command_close(&commandHandle
);
238 setProcessError(exitCode
, &errBuf
);
239 git_buf_dispose(&errBuf
);
242 return GIT_PASSTHROUGH
;
244 command_close_stdin(&commandHandle
);
246 if (command_wait_stdout_reading_thread(&commandHandle
)) {
247 exitCode
= command_close(&commandHandle
);
249 setProcessError(exitCode
, &errBuf
);
250 git_buf_dispose(&errBuf
);
253 return GIT_PASSTHROUGH
;
256 exitCode
= command_close(&commandHandle
);
259 setProcessError(exitCode
, &errBuf
);
260 git_buf_dispose(&errBuf
);
263 git_buf_dispose(&errBuf
);
264 return GIT_PASSTHROUGH
;
267 git_buf_dispose(&errBuf
);
272 static void filter_cleanup(
280 static void filter_free(git_filter
*self
)
282 struct filter_filter
*ffs
= (struct filter_filter
*)self
;
284 git__free(ffs
->shexepath
);
289 git_filter
*git_filter_filter_new(LPCWSTR shexepath
, LPWSTR
* pEnv
)
291 struct filter_filter
*f
= git__calloc(1, sizeof(struct filter_filter
));
295 f
->f
.version
= GIT_FILTER_VERSION
;
296 f
->f
.attributes
= "filter";
297 f
->f
.initialize
= NULL
;
298 f
->f
.shutdown
= filter_free
;
299 f
->f
.check
= filter_check
;
300 f
->f
.apply
= filter_apply
;
301 f
->f
.cleanup
= filter_cleanup
;
303 if (shexepath
&& shexepath
[0])
304 f
->shexepath
= wcsdup(shexepath
);
307 return (git_filter
*)f
;