4 * Copyright (c) 2007 Junio C Hamano
10 static struct whitespace_rule
{
11 const char *rule_name
;
13 } whitespace_rule_names
[] = {
14 { "trailing-space", WS_TRAILING_SPACE
},
15 { "space-before-tab", WS_SPACE_BEFORE_TAB
},
16 { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB
},
19 unsigned parse_whitespace_rule(const char *string
)
21 unsigned rule
= WS_DEFAULT_RULE
;
29 string
= string
+ strspn(string
, ", \t\n\r");
30 ep
= strchr(string
, ',');
43 for (i
= 0; i
< ARRAY_SIZE(whitespace_rule_names
); i
++) {
44 if (strncmp(whitespace_rule_names
[i
].rule_name
,
48 rule
&= ~whitespace_rule_names
[i
].rule_bits
;
50 rule
|= whitespace_rule_names
[i
].rule_bits
;
58 static void setup_whitespace_attr_check(struct git_attr_check
*check
)
60 static struct git_attr
*attr_whitespace
;
63 attr_whitespace
= git_attr("whitespace", 10);
64 check
[0].attr
= attr_whitespace
;
67 unsigned whitespace_rule(const char *pathname
)
69 struct git_attr_check attr_whitespace_rule
;
71 setup_whitespace_attr_check(&attr_whitespace_rule
);
72 if (!git_checkattr(pathname
, 1, &attr_whitespace_rule
)) {
75 value
= attr_whitespace_rule
.value
;
76 if (ATTR_TRUE(value
)) {
77 /* true (whitespace) */
78 unsigned all_rule
= 0;
80 for (i
= 0; i
< ARRAY_SIZE(whitespace_rule_names
); i
++)
81 all_rule
|= whitespace_rule_names
[i
].rule_bits
;
83 } else if (ATTR_FALSE(value
)) {
84 /* false (-whitespace) */
86 } else if (ATTR_UNSET(value
)) {
87 /* reset to default (!whitespace) */
88 return whitespace_rule_cfg
;
91 return parse_whitespace_rule(value
);
94 return whitespace_rule_cfg
;
98 /* The returned string should be freed by the caller. */
99 char *whitespace_error_string(unsigned ws
)
102 strbuf_init(&err
, 0);
103 if (ws
& WS_TRAILING_SPACE
)
104 strbuf_addstr(&err
, "Adds trailing whitespace");
105 if (ws
& WS_SPACE_BEFORE_TAB
) {
107 strbuf_addstr(&err
, ", ");
108 strbuf_addstr(&err
, "Space in indent is followed by a tab");
110 if (ws
& WS_INDENT_WITH_NON_TAB
) {
112 strbuf_addstr(&err
, ", ");
113 strbuf_addstr(&err
, "Indent more than 8 places with spaces");
115 return strbuf_detach(&err
, NULL
);
118 /* If stream is non-NULL, emits the line after checking. */
119 unsigned check_and_emit_line(const char *line
, int len
, unsigned ws_rule
,
120 FILE *stream
, const char *set
,
121 const char *reset
, const char *ws
)
124 int leading_space
= -1;
125 int trailing_whitespace
= -1;
126 int trailing_newline
= 0;
129 /* Logic is simpler if we temporarily ignore the trailing newline. */
130 if (len
> 0 && line
[len
- 1] == '\n') {
131 trailing_newline
= 1;
135 /* Check for trailing whitespace. */
136 if (ws_rule
& WS_TRAILING_SPACE
) {
137 for (i
= len
- 1; i
>= 0; i
--) {
138 if (isspace(line
[i
])) {
139 trailing_whitespace
= i
;
140 result
|= WS_TRAILING_SPACE
;
147 /* Check for space before tab in initial indent. */
148 for (i
= 0; i
< len
; i
++) {
149 if (line
[i
] == '\t') {
150 if ((ws_rule
& WS_SPACE_BEFORE_TAB
) &&
151 (leading_space
!= -1))
152 result
|= WS_SPACE_BEFORE_TAB
;
155 else if (line
[i
] == ' ')
161 /* Check for indent using non-tab. */
162 if ((ws_rule
& WS_INDENT_WITH_NON_TAB
) && leading_space
>= 8)
163 result
|= WS_INDENT_WITH_NON_TAB
;
166 /* Highlight errors in leading whitespace. */
167 if ((result
& WS_SPACE_BEFORE_TAB
) ||
168 (result
& WS_INDENT_WITH_NON_TAB
)) {
170 fwrite(line
, leading_space
+ 1, 1, stream
);
171 fputs(reset
, stream
);
177 /* Now the rest of the line starts at leading_space.
178 * The non-highlighted part ends at trailing_whitespace. */
179 if (trailing_whitespace
== -1)
180 trailing_whitespace
= len
;
182 /* Emit non-highlighted (middle) segment. */
183 if (trailing_whitespace
- leading_space
> 0) {
185 fwrite(line
+ leading_space
,
186 trailing_whitespace
- leading_space
, 1, stream
);
187 fputs(reset
, stream
);
190 /* Highlight errors in trailing whitespace. */
191 if (trailing_whitespace
!= len
) {
193 fwrite(line
+ trailing_whitespace
,
194 len
- trailing_whitespace
, 1, stream
);
195 fputs(reset
, stream
);
197 if (trailing_newline
)