]>
git.proxmox.com Git - libgit2.git/blob - src/crlf.c
2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
10 #include "git2/index.h"
11 #include "git2/sys/filter.h"
18 #include "repository.h"
31 static int check_crlf(const char *value
)
33 if (GIT_ATTR_TRUE(value
))
36 if (GIT_ATTR_FALSE(value
))
37 return GIT_CRLF_BINARY
;
39 if (GIT_ATTR_UNSPECIFIED(value
))
40 return GIT_CRLF_GUESS
;
42 if (strcmp(value
, "input") == 0)
43 return GIT_CRLF_INPUT
;
45 if (strcmp(value
, "auto") == 0)
48 return GIT_CRLF_GUESS
;
51 static int check_eol(const char *value
)
53 if (GIT_ATTR_UNSPECIFIED(value
))
56 if (strcmp(value
, "lf") == 0)
59 if (strcmp(value
, "crlf") == 0)
65 static int crlf_input_action(struct crlf_attrs
*ca
)
67 if (ca
->crlf_action
== GIT_CRLF_BINARY
)
68 return GIT_CRLF_BINARY
;
70 if (ca
->eol
== GIT_EOL_LF
)
71 return GIT_CRLF_INPUT
;
73 if (ca
->eol
== GIT_EOL_CRLF
)
76 return ca
->crlf_action
;
79 static int has_cr_in_index(const git_filter_source
*src
)
81 git_repository
*repo
= git_filter_source_repo(src
);
82 const char *path
= git_filter_source_path(src
);
84 const git_index_entry
*entry
;
86 const void *blobcontent
;
93 if (git_repository_index__weakptr(&index
, repo
) < 0) {
98 if (!(entry
= git_index_get_bypath(index
, path
, 0)) &&
99 !(entry
= git_index_get_bypath(index
, path
, 1)))
102 if (!S_ISREG(entry
->mode
)) /* don't crlf filter non-blobs */
105 if (git_blob_lookup(&blob
, repo
, &entry
->id
) < 0)
108 blobcontent
= git_blob_rawcontent(blob
);
109 blobsize
= git_blob_rawsize(blob
);
110 if (!git__is_sizet(blobsize
))
111 blobsize
= (size_t)-1;
113 found_cr
= (blobcontent
!= NULL
&&
115 memchr(blobcontent
, '\r', (size_t)blobsize
) != NULL
);
121 static int crlf_apply_to_odb(
122 struct crlf_attrs
*ca
,
125 const git_filter_source
*src
)
127 /* Empty file? Nothing to do */
128 if (!git_buf_len(from
))
131 /* Heuristics to see if we can skip the conversion.
132 * Straight from Core Git.
134 if (ca
->crlf_action
== GIT_CRLF_AUTO
|| ca
->crlf_action
== GIT_CRLF_GUESS
) {
135 git_buf_text_stats stats
;
137 /* Check heuristics for binary vs text - returns true if binary */
138 if (git_buf_text_gather_stats(&stats
, from
, false))
139 return GIT_PASSTHROUGH
;
141 /* If there are no CR characters to filter out, then just pass */
143 return GIT_PASSTHROUGH
;
145 /* If safecrlf is enabled, sanity-check the result. */
146 if (stats
.cr
!= stats
.crlf
|| stats
.lf
!= stats
.crlf
) {
147 switch (ca
->safe_crlf
) {
148 case GIT_SAFE_CRLF_FAIL
:
150 GITERR_FILTER
, "LF would be replaced by CRLF in '%s'",
151 git_filter_source_path(src
));
153 case GIT_SAFE_CRLF_WARN
:
154 /* TODO: issue warning when warning API is available */;
162 * We're currently not going to even try to convert stuff
163 * that has bare CR characters. Does anybody do that crazy
166 if (stats
.cr
!= stats
.crlf
)
167 return GIT_PASSTHROUGH
;
169 if (ca
->crlf_action
== GIT_CRLF_GUESS
) {
171 * If the file in the index has any CR in it, do not convert.
172 * This is the new safer autocrlf handling.
174 if (has_cr_in_index(src
))
175 return GIT_PASSTHROUGH
;
179 return GIT_PASSTHROUGH
;
182 /* Actually drop the carriage returns */
183 return git_buf_text_crlf_to_lf(to
, from
);
186 static const char *line_ending(struct crlf_attrs
*ca
)
188 switch (ca
->crlf_action
) {
189 case GIT_CRLF_BINARY
:
197 if (ca
->auto_crlf
== GIT_AUTO_CRLF_FALSE
)
206 goto line_ending_error
;
209 if (ca
->auto_crlf
== GIT_AUTO_CRLF_TRUE
)
211 else if (ca
->auto_crlf
== GIT_AUTO_CRLF_INPUT
)
213 else if (ca
->eol
== GIT_EOL_UNSET
)
214 return GIT_EOL_NATIVE
== GIT_EOL_CRLF
? "\r\n" : "\n";
215 else if (ca
->eol
== GIT_EOL_LF
)
217 else if (ca
->eol
== GIT_EOL_CRLF
)
221 giterr_set(GITERR_INVALID
, "Invalid input to line ending filter");
225 static int crlf_apply_to_workdir(
226 struct crlf_attrs
*ca
, git_buf
*to
, const git_buf
*from
)
228 git_buf_text_stats stats
;
229 const char *workdir_ending
= NULL
;
232 /* Empty file? Nothing to do. */
233 if (git_buf_len(from
) == 0)
236 /* Determine proper line ending */
237 workdir_ending
= line_ending(ca
);
241 /* only LF->CRLF conversion is supported, do nothing on LF platforms */
242 if (strcmp(workdir_ending
, "\r\n") != 0)
243 return GIT_PASSTHROUGH
;
245 /* If there are no LFs, or all LFs are part of a CRLF, nothing to do */
246 is_binary
= git_buf_text_gather_stats(&stats
, from
, false);
248 if (stats
.lf
== 0 || stats
.lf
== stats
.crlf
)
249 return GIT_PASSTHROUGH
;
251 if (ca
->crlf_action
== GIT_CRLF_AUTO
||
252 ca
->crlf_action
== GIT_CRLF_GUESS
) {
254 /* If we have any existing CR or CRLF line endings, do nothing */
255 if (ca
->crlf_action
== GIT_CRLF_GUESS
&&
256 stats
.cr
> 0 && stats
.crlf
> 0)
257 return GIT_PASSTHROUGH
;
259 /* If we have bare CR characters, do nothing */
260 if (stats
.cr
!= stats
.crlf
)
261 return GIT_PASSTHROUGH
;
263 /* Don't filter binary files */
265 return GIT_PASSTHROUGH
;
268 return git_buf_text_lf_to_crlf(to
, from
);
271 static int crlf_check(
273 void **payload
, /* points to NULL ptr on entry, may be set */
274 const git_filter_source
*src
,
275 const char **attr_values
)
278 struct crlf_attrs ca
;
283 ca
.crlf_action
= GIT_CRLF_GUESS
;
284 ca
.eol
= GIT_EOL_UNSET
;
286 ca
.crlf_action
= check_crlf(attr_values
[2]); /* text */
287 if (ca
.crlf_action
== GIT_CRLF_GUESS
)
288 ca
.crlf_action
= check_crlf(attr_values
[0]); /* clrf */
289 ca
.eol
= check_eol(attr_values
[1]); /* eol */
291 ca
.auto_crlf
= GIT_AUTO_CRLF_DEFAULT
;
294 * Use the core Git logic to see if we should perform CRLF for this file
295 * based on its attributes & the value of `core.autocrlf`
297 ca
.crlf_action
= crlf_input_action(&ca
);
299 if (ca
.crlf_action
== GIT_CRLF_BINARY
)
300 return GIT_PASSTHROUGH
;
302 if (ca
.crlf_action
== GIT_CRLF_GUESS
||
303 ((ca
.crlf_action
== GIT_CRLF_AUTO
|| ca
.crlf_action
== GIT_CRLF_TEXT
) &&
304 git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
)) {
306 error
= git_repository__cvar(
307 &ca
.auto_crlf
, git_filter_source_repo(src
), GIT_CVAR_AUTO_CRLF
);
311 if (ca
.crlf_action
== GIT_CRLF_GUESS
&&
312 ca
.auto_crlf
== GIT_AUTO_CRLF_FALSE
)
313 return GIT_PASSTHROUGH
;
315 if (ca
.auto_crlf
== GIT_AUTO_CRLF_INPUT
&&
316 git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
)
317 return GIT_PASSTHROUGH
;
320 if (git_filter_source_mode(src
) == GIT_FILTER_CLEAN
) {
321 error
= git_repository__cvar(
322 &ca
.safe_crlf
, git_filter_source_repo(src
), GIT_CVAR_SAFE_CRLF
);
326 /* downgrade FAIL to WARN if ALLOW_UNSAFE option is used */
327 if ((git_filter_source_flags(src
) & GIT_FILTER_ALLOW_UNSAFE
) &&
328 ca
.safe_crlf
== GIT_SAFE_CRLF_FAIL
)
329 ca
.safe_crlf
= GIT_SAFE_CRLF_WARN
;
332 *payload
= git__malloc(sizeof(ca
));
333 GITERR_CHECK_ALLOC(*payload
);
334 memcpy(*payload
, &ca
, sizeof(ca
));
339 static int crlf_apply(
341 void **payload
, /* may be read and/or set */
344 const git_filter_source
*src
)
346 /* initialize payload in case `check` was bypassed */
348 int error
= crlf_check(self
, payload
, src
, NULL
);
353 if (git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
)
354 return crlf_apply_to_workdir(*payload
, to
, from
);
356 return crlf_apply_to_odb(*payload
, to
, from
, src
);
359 static void crlf_cleanup(
367 git_filter
*git_crlf_filter_new(void)
369 struct crlf_filter
*f
= git__calloc(1, sizeof(struct crlf_filter
));
373 f
->f
.version
= GIT_FILTER_VERSION
;
374 f
->f
.attributes
= "crlf eol text";
375 f
->f
.initialize
= NULL
;
376 f
->f
.shutdown
= git_filter_free
;
377 f
->f
.check
= crlf_check
;
378 f
->f
.apply
= crlf_apply
;
379 f
->f
.cleanup
= crlf_cleanup
;
381 return (git_filter
*)f
;