]>
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 git_buf safe
= GIT_BUF_INIT
;
130 /* Empty file? Nothing to do */
131 if (!git_buf_len(from
))
134 /* Heuristics to see if we can skip the conversion.
135 * Straight from Core Git.
137 if (ca
->crlf_action
== GIT_CRLF_AUTO
|| ca
->crlf_action
== GIT_CRLF_GUESS
) {
138 git_buf_text_stats stats
;
140 /* Check heuristics for binary vs text - returns true if binary */
141 if (git_buf_text_gather_stats(&stats
, from
, false))
142 return GIT_PASSTHROUGH
;
145 * We're currently not going to even try to convert stuff
146 * that has bare CR characters. Does anybody do that crazy
149 if (stats
.cr
!= stats
.crlf
)
150 return GIT_PASSTHROUGH
;
152 if (ca
->crlf_action
== GIT_CRLF_GUESS
) {
154 * If the file in the index has any CR in it, do not convert.
155 * This is the new safer autocrlf handling.
157 if (has_cr_in_index(src
))
158 return GIT_PASSTHROUGH
;
161 if (!stats
.cr
&& !ca
->safe_crlf
)
162 return GIT_PASSTHROUGH
;
165 /* Actually drop the carriage returns */
166 if ((error
= git_buf_text_crlf_to_lf(to
, from
)) < 0)
169 /* If safecrlf is enabled, sanity-check the result. */
171 if ((error
= git_buf_grow(&safe
, max(from
->size
, to
->size
))) < 0 ||
172 (error
= git_buf_text_lf_to_crlf(&safe
, to
)) < 0)
175 if (git_buf_cmp(from
, &safe
) != 0) {
176 giterr_set(GITERR_FILTER
, "LF would be replaced by CRLF in '%s'",
177 git_filter_source_path(src
));
188 static const char *line_ending(struct crlf_attrs
*ca
)
190 switch (ca
->crlf_action
) {
191 case GIT_CRLF_BINARY
:
204 goto line_ending_error
;
209 return GIT_EOL_NATIVE
== GIT_EOL_CRLF
? "\r\n" : "\n";
218 goto line_ending_error
;
222 giterr_set(GITERR_INVALID
, "Invalid input to line ending filter");
226 static int crlf_apply_to_workdir(
227 struct crlf_attrs
*ca
, git_buf
*to
, const git_buf
*from
)
229 const char *workdir_ending
= NULL
;
231 /* Empty file? Nothing to do. */
232 if (git_buf_len(from
) == 0)
235 /* Don't filter binary files */
236 if (git_buf_text_is_binary(from
))
237 return GIT_PASSTHROUGH
;
239 /* Determine proper line ending */
240 workdir_ending
= line_ending(ca
);
244 /* only LF->CRLF conversion is supported, do nothing on LF platforms */
245 if (strcmp(workdir_ending
, "\r\n") != 0)
246 return GIT_PASSTHROUGH
;
248 return git_buf_text_lf_to_crlf(to
, from
);
251 static int crlf_check(
253 void **payload
, /* points to NULL ptr on entry, may be set */
254 const git_filter_source
*src
,
255 const char **attr_values
)
258 struct crlf_attrs ca
;
263 ca
.crlf_action
= GIT_CRLF_GUESS
;
264 ca
.eol
= GIT_EOL_UNSET
;
266 ca
.crlf_action
= check_crlf(attr_values
[2]); /* text */
267 if (ca
.crlf_action
== GIT_CRLF_GUESS
)
268 ca
.crlf_action
= check_crlf(attr_values
[0]); /* clrf */
269 ca
.eol
= check_eol(attr_values
[1]); /* eol */
271 ca
.auto_crlf
= GIT_AUTO_CRLF_DEFAULT
;
274 * Use the core Git logic to see if we should perform CRLF for this file
275 * based on its attributes & the value of `core.autocrlf`
277 ca
.crlf_action
= crlf_input_action(&ca
);
279 if (ca
.crlf_action
== GIT_CRLF_BINARY
)
280 return GIT_PASSTHROUGH
;
282 if (ca
.crlf_action
== GIT_CRLF_GUESS
||
283 (ca
.crlf_action
== GIT_CRLF_AUTO
&&
284 git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
)) {
285 error
= git_repository__cvar(
286 &ca
.auto_crlf
, git_filter_source_repo(src
), GIT_CVAR_AUTO_CRLF
);
290 if (ca
.auto_crlf
== GIT_AUTO_CRLF_FALSE
)
291 return GIT_PASSTHROUGH
;
293 if (ca
.auto_crlf
== GIT_AUTO_CRLF_INPUT
&&
294 git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
)
295 return GIT_PASSTHROUGH
;
298 if (git_filter_source_mode(src
) == GIT_FILTER_CLEAN
) {
299 error
= git_repository__cvar(
300 &ca
.safe_crlf
, git_filter_source_repo(src
), GIT_CVAR_SAFE_CRLF
);
305 *payload
= git__malloc(sizeof(ca
));
306 GITERR_CHECK_ALLOC(*payload
);
307 memcpy(*payload
, &ca
, sizeof(ca
));
312 static int crlf_apply(
314 void **payload
, /* may be read and/or set */
317 const git_filter_source
*src
)
319 /* initialize payload in case `check` was bypassed */
321 int error
= crlf_check(self
, payload
, src
, NULL
);
322 if (error
< 0 && error
!= GIT_PASSTHROUGH
)
326 if (git_filter_source_mode(src
) == GIT_FILTER_SMUDGE
)
327 return crlf_apply_to_workdir(*payload
, to
, from
);
329 return crlf_apply_to_odb(*payload
, to
, from
, src
);
332 static void crlf_cleanup(
340 git_filter
*git_crlf_filter_new(void)
342 struct crlf_filter
*f
= git__calloc(1, sizeof(struct crlf_filter
));
344 f
->f
.version
= GIT_FILTER_VERSION
;
345 f
->f
.attributes
= "crlf eol text";
346 f
->f
.initialize
= NULL
;
347 f
->f
.shutdown
= git_filter_free
;
348 f
->f
.check
= crlf_check
;
349 f
->f
.apply
= crlf_apply
;
350 f
->f
.cleanup
= crlf_cleanup
;
352 return (git_filter
*)f
;