1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "fileops.h"
#include "hash.h"
#include "filter.h"
#include "repository.h"
#include "git2/config.h"
#include "blob.h"
/* Tweaked from Core Git. I wonder what we could use this for... */
void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
{
size_t i;
memset(stats, 0, sizeof(*stats));
for (i = 0; i < git_buf_len(text); i++) {
unsigned char c = text->ptr[i];
if (c == '\r') {
stats->cr++;
if (i + 1 < git_buf_len(text) && text->ptr[i + 1] == '\n')
stats->crlf++;
}
else if (c == '\n')
stats->lf++;
else if (c == 0x85)
/* Unicode CR+LF */
stats->crlf++;
else if (c == 127)
/* DEL */
stats->nonprintable++;
else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
switch (c) {
/* BS, HT, ESC and FF */
case '\b': case '\t': case '\033': case '\014':
stats->printable++;
break;
case 0:
stats->nul++;
/* fall through */
default:
stats->nonprintable++;
}
}
else
stats->printable++;
}
/* If file ends with EOF then don't count this EOF as non-printable. */
if (git_buf_len(text) >= 1 && text->ptr[text->size - 1] == '\032')
stats->nonprintable--;
}
/*
* Fresh from Core Git
*/
int git_text_is_binary(git_text_stats *stats)
{
if (stats->nul)
return 1;
if ((stats->printable >> 7) < stats->nonprintable)
return 1;
/*
* Other heuristics? Average line length might be relevant,
* as might LF vs CR vs CRLF counts..
*
* NOTE! It might be normal to have a low ratio of CRLF to LF
* (somebody starts with a LF-only file and edits it with an editor
* that adds CRLF only to lines that are added..). But do we
* want to support CR-only? Probably not.
*/
return 0;
}
int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
{
int error;
if (mode == GIT_FILTER_TO_ODB) {
/* Load the CRLF cleanup filter when writing to the ODB */
error = git_filter_add__crlf_to_odb(filters, repo, path);
if (error < 0)
return error;
} else {
error = git_filter_add__crlf_to_workdir(filters, repo, path);
if (error < 0)
return error;
}
return (int)filters->length;
}
void git_filters_free(git_vector *filters)
{
size_t i;
git_filter *filter;
git_vector_foreach(filters, i, filter) {
if (filter->do_free != NULL)
filter->do_free(filter);
else
git__free(filter);
}
git_vector_free(filters);
}
int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
{
unsigned int i, src;
git_buf *dbuffer[2];
dbuffer[0] = source;
dbuffer[1] = dest;
src = 0;
if (git_buf_len(source) == 0) {
git_buf_clear(dest);
return 0;
}
/* Pre-grow the destination buffer to more or less the size
* we expect it to have */
if (git_buf_grow(dest, git_buf_len(source)) < 0)
return -1;
for (i = 0; i < filters->length; ++i) {
git_filter *filter = git_vector_get(filters, i);
unsigned int dst = 1 - src;
git_buf_clear(dbuffer[dst]);
/* Apply the filter from dbuffer[src] to the other buffer;
* if the filtering is canceled by the user mid-filter,
* we skip to the next filter without changing the source
* of the double buffering (so that the text goes through
* cleanly).
*/
if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
src = dst;
if (git_buf_oom(dbuffer[dst]))
return -1;
}
/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
if (src != 1)
git_buf_swap(dest, source);
return 0;
}
static int unfiltered_blob_contents(git_buf *out, git_repository *repo, const git_oid *blob_id)
{
int retcode = GIT_ERROR;
git_blob *blob;
if (!(retcode = git_blob_lookup(&blob, repo, blob_id)))
{
retcode = git_blob__getbuf(out, blob);
git_blob_free(blob);
}
return retcode;
}
int git_filter_blob_contents(git_buf *out, git_repository *repo, const git_oid *oid, const char *path)
{
int retcode = GIT_ERROR;
git_buf unfiltered = GIT_BUF_INIT;
if (!unfiltered_blob_contents(&unfiltered, repo, oid)) {
git_vector filters = GIT_VECTOR_INIT;
if (git_filters_load(&filters,
repo, path, GIT_FILTER_TO_WORKTREE) >= 0) {
git_buf_clear(out);
retcode = git_filters_apply(out, &unfiltered, &filters);
}
git_filters_free(&filters);
}
git_buf_free(&unfiltered);
return retcode;
}
|