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]);
66 static int expandPerCentF(git_buf
*buf
, const char *replaceWith
)
68 ssize_t foundPercentage
= git_buf_find(buf
, '%');
69 if (foundPercentage
) {
70 git_buf expanded
= GIT_BUF_INIT
;
71 const char *end
= buf
->ptr
+ buf
->size
;
72 const char *lastPercentage
= buf
->ptr
;
73 const char *idx
= buf
->ptr
+ foundPercentage
;
76 if (idx
+ 1 == buf
->ptr
+ buf
->size
|| (idx
+ 1 < end
&& *(idx
+ 1) == '%')) { // one '%' is at the end of the string OR "%%" is in the string
77 git_buf_putc(&expanded
, '%');
83 // now we know, that we're not at the end of the string and that the next char is not '%'
84 git_buf_put(&expanded
, lastPercentage
, idx
- lastPercentage
);
87 git_buf_printf(&expanded
, "\"%s\"", replaceWith
);
96 git_buf_put(&expanded
, lastPercentage
, idx
- lastPercentage
);
97 if (git_buf_oom(&expanded
))
99 git_buf_swap(buf
, &expanded
);
100 git_buf_free(&expanded
);
105 static void setProcessError(DWORD exitCode
, git_buf
*errBuf
)
107 if (!git_buf_oom(errBuf
) && git_buf_len(errBuf
))
108 giterr_set(GITERR_FILTER
, "External filter application exited non-zero (%ld) and reported:\n%s", exitCode
, errBuf
->ptr
);
110 giterr_set(GITERR_FILTER
, "External filter application exited non-zero: %ld", exitCode
);
113 static int filter_apply(
115 void **payload
, /* may be read and/or set */
118 const git_filter_source
*src
)
120 struct filter_filter
*ffs
= (struct filter_filter
*)self
;
123 return GIT_PASSTHROUGH
;
126 if (git_repository_config__weakptr(&config
, git_filter_source_repo(src
)))
129 git_buf configKey
= GIT_BUF_INIT
;
130 git_buf_join3(&configKey
, '.', "filter", *payload
, "required");
131 if (git_buf_oom(&configKey
))
134 int isRequired
= FALSE
;
135 int error
= git_config_get_bool(&isRequired
, config
, configKey
.ptr
);
136 git_buf_free(&configKey
);
137 if (error
&& error
!= GIT_ENOTFOUND
)
140 git_buf_join(&configKey
, '.', "filter", *payload
);
141 if (git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
) {
142 git_buf_puts(&configKey
, ".smudge");
144 git_buf_puts(&configKey
, ".clean");
146 if (git_buf_oom(&configKey
))
149 const char *cmd
= NULL
;
150 error
= git_config_get_string(&cmd
, config
, configKey
.ptr
);
151 git_buf_free(&configKey
);
152 if (error
&& error
!= GIT_ENOTFOUND
)
155 if (error
== GIT_ENOTFOUND
) {
158 return GIT_PASSTHROUGH
;
161 git_buf cmdBuf
= GIT_BUF_INIT
;
162 git_buf_puts(&cmdBuf
, cmd
);
163 if (git_buf_oom(&cmdBuf
))
166 if (expandPerCentF(&cmdBuf
, git_filter_source_path(src
)))
169 if (ffs
->shexepath
) {
170 // build params for sh.exe
171 git_buf shParams
= GIT_BUF_INIT
;
172 git_buf_puts(&shParams
, " -c \"");
173 git_buf_text_puts_escaped(&shParams
, cmdBuf
.ptr
, "\"\\", "\\");
174 git_buf_puts(&shParams
, "\"");
175 git_buf_swap(&shParams
, &cmdBuf
);
176 git_buf_free(&shParams
);
180 if (git__utf8_to_16_alloc(&wide_cmd
, cmdBuf
.ptr
) < 0)
182 git_buf_free(&cmdBuf
);
185 git_buf_free(&cmdBuf
);
187 if (ffs
->shexepath
) {
188 // build cmd, i.e. shexepath + params
189 size_t len
= wcslen(ffs
->shexepath
) + wcslen(wide_cmd
) + 1;
190 wchar_t *tmp
= git__calloc(len
, sizeof(wchar_t));
195 wcscat_s(tmp
, len
, ffs
->shexepath
);
196 wcscat_s(tmp
, len
, wide_cmd
);
201 COMMAND_HANDLE commandHandle
= { 0 };
202 git_buf errBuf
= GIT_BUF_INIT
;
203 commandHandle
.errBuf
= &errBuf
;
204 if (command_start(wide_cmd
, &commandHandle
, ffs
->pEnv
)) {
208 return GIT_PASSTHROUGH
;
212 HANDLE readingThread
= commmand_start_stdout_reading_thread(&commandHandle
, to
);
213 if (!readingThread
) {
214 command_close(&commandHandle
);
218 if (command_write_gitbuf(&commandHandle
, from
)) {
219 DWORD exitCode
= command_close(&commandHandle
);
221 setProcessError(exitCode
, &errBuf
);
222 CloseHandle(readingThread
);
223 git_buf_free(&errBuf
);
226 return GIT_PASSTHROUGH
;
228 command_close_stdin(&commandHandle
);
230 DWORD exitCode
= MAXDWORD
;
231 WaitForSingleObject(readingThread
, INFINITE
);
232 if (!GetExitCodeThread(readingThread
, &exitCode
) || exitCode
) {
233 exitCode
= command_close(&commandHandle
);
235 setProcessError(exitCode
, &errBuf
);
236 CloseHandle(readingThread
);
237 git_buf_free(&errBuf
);
240 return GIT_PASSTHROUGH
;
242 CloseHandle(readingThread
);
244 exitCode
= command_close(&commandHandle
);
247 setProcessError(exitCode
, &errBuf
);
248 git_buf_free(&errBuf
);
251 git_buf_free(&errBuf
);
252 return GIT_PASSTHROUGH
;
255 git_buf_free(&errBuf
);
260 static void filter_cleanup(
268 static void filter_free(git_filter
*self
)
270 struct filter_filter
*ffs
= (struct filter_filter
*)self
;
273 git__free(ffs
->shexepath
);
278 git_filter
*git_filter_filter_new(LPCWSTR shexepath
, LPWSTR pEnv
)
280 struct filter_filter
*f
= git__calloc(1, sizeof(struct filter_filter
));
282 f
->f
.version
= GIT_FILTER_VERSION
;
283 f
->f
.attributes
= "filter";
284 f
->f
.initialize
= NULL
;
285 f
->f
.shutdown
= filter_free
;
286 f
->f
.check
= filter_check
;
287 f
->f
.apply
= filter_apply
;
288 f
->f
.cleanup
= filter_cleanup
;
289 f
->shexepath
= wcsdup(shexepath
);
292 return (git_filter
*)f
;