THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

396 lines
9.6 KiB

  1. /*
  2. * Whitespace rules
  3. *
  4. * Copyright (c) 2007 Junio C Hamano
  5. */
  6. #include "cache.h"
  7. #include "attr.h"
  8. static struct whitespace_rule {
  9. const char *rule_name;
  10. unsigned rule_bits;
  11. unsigned loosens_error:1,
  12. exclude_default:1;
  13. } whitespace_rule_names[] = {
  14. { "trailing-space", WS_TRAILING_SPACE, 0 },
  15. { "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
  16. { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB, 0 },
  17. { "cr-at-eol", WS_CR_AT_EOL, 1 },
  18. { "blank-at-eol", WS_BLANK_AT_EOL, 0 },
  19. { "blank-at-eof", WS_BLANK_AT_EOF, 0 },
  20. { "tab-in-indent", WS_TAB_IN_INDENT, 0, 1 },
  21. };
  22. unsigned parse_whitespace_rule(const char *string)
  23. {
  24. unsigned rule = WS_DEFAULT_RULE;
  25. while (string) {
  26. int i;
  27. size_t len;
  28. const char *ep;
  29. int negated = 0;
  30. string = string + strspn(string, ", \t\n\r");
  31. ep = strchrnul(string, ',');
  32. len = ep - string;
  33. if (*string == '-') {
  34. negated = 1;
  35. string++;
  36. len--;
  37. }
  38. if (!len)
  39. break;
  40. for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) {
  41. if (strncmp(whitespace_rule_names[i].rule_name,
  42. string, len))
  43. continue;
  44. if (negated)
  45. rule &= ~whitespace_rule_names[i].rule_bits;
  46. else
  47. rule |= whitespace_rule_names[i].rule_bits;
  48. break;
  49. }
  50. if (strncmp(string, "tabwidth=", 9) == 0) {
  51. unsigned tabwidth = atoi(string + 9);
  52. if (0 < tabwidth && tabwidth < 0100) {
  53. rule &= ~WS_TAB_WIDTH_MASK;
  54. rule |= tabwidth;
  55. }
  56. else
  57. warning("tabwidth %.*s out of range",
  58. (int)(len - 9), string + 9);
  59. }
  60. string = ep;
  61. }
  62. if (rule & WS_TAB_IN_INDENT && rule & WS_INDENT_WITH_NON_TAB)
  63. die("cannot enforce both tab-in-indent and indent-with-non-tab");
  64. return rule;
  65. }
  66. static void setup_whitespace_attr_check(struct git_attr_check *check)
  67. {
  68. static struct git_attr *attr_whitespace;
  69. if (!attr_whitespace)
  70. attr_whitespace = git_attr("whitespace");
  71. check[0].attr = attr_whitespace;
  72. }
  73. unsigned whitespace_rule(const char *pathname)
  74. {
  75. struct git_attr_check attr_whitespace_rule;
  76. setup_whitespace_attr_check(&attr_whitespace_rule);
  77. if (!git_check_attr(pathname, 1, &attr_whitespace_rule)) {
  78. const char *value;
  79. value = attr_whitespace_rule.value;
  80. if (ATTR_TRUE(value)) {
  81. /* true (whitespace) */
  82. unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
  83. int i;
  84. for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
  85. if (!whitespace_rule_names[i].loosens_error &&
  86. !whitespace_rule_names[i].exclude_default)
  87. all_rule |= whitespace_rule_names[i].rule_bits;
  88. return all_rule;
  89. } else if (ATTR_FALSE(value)) {
  90. /* false (-whitespace) */
  91. return ws_tab_width(whitespace_rule_cfg);
  92. } else if (ATTR_UNSET(value)) {
  93. /* reset to default (!whitespace) */
  94. return whitespace_rule_cfg;
  95. } else {
  96. /* string */
  97. return parse_whitespace_rule(value);
  98. }
  99. } else {
  100. return whitespace_rule_cfg;
  101. }
  102. }
  103. /* The returned string should be freed by the caller. */
  104. char *whitespace_error_string(unsigned ws)
  105. {
  106. struct strbuf err = STRBUF_INIT;
  107. if ((ws & WS_TRAILING_SPACE) == WS_TRAILING_SPACE)
  108. strbuf_addstr(&err, "trailing whitespace");
  109. else {
  110. if (ws & WS_BLANK_AT_EOL)
  111. strbuf_addstr(&err, "trailing whitespace");
  112. if (ws & WS_BLANK_AT_EOF) {
  113. if (err.len)
  114. strbuf_addstr(&err, ", ");
  115. strbuf_addstr(&err, "new blank line at EOF");
  116. }
  117. }
  118. if (ws & WS_SPACE_BEFORE_TAB) {
  119. if (err.len)
  120. strbuf_addstr(&err, ", ");
  121. strbuf_addstr(&err, "space before tab in indent");
  122. }
  123. if (ws & WS_INDENT_WITH_NON_TAB) {
  124. if (err.len)
  125. strbuf_addstr(&err, ", ");
  126. strbuf_addstr(&err, "indent with spaces");
  127. }
  128. if (ws & WS_TAB_IN_INDENT) {
  129. if (err.len)
  130. strbuf_addstr(&err, ", ");
  131. strbuf_addstr(&err, "tab in indent");
  132. }
  133. return strbuf_detach(&err, NULL);
  134. }
  135. /* If stream is non-NULL, emits the line after checking. */
  136. static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
  137. FILE *stream, const char *set,
  138. const char *reset, const char *ws)
  139. {
  140. unsigned result = 0;
  141. int written = 0;
  142. int trailing_whitespace = -1;
  143. int trailing_newline = 0;
  144. int trailing_carriage_return = 0;
  145. int i;
  146. /* Logic is simpler if we temporarily ignore the trailing newline. */
  147. if (len > 0 && line[len - 1] == '\n') {
  148. trailing_newline = 1;
  149. len--;
  150. }
  151. if ((ws_rule & WS_CR_AT_EOL) &&
  152. len > 0 && line[len - 1] == '\r') {
  153. trailing_carriage_return = 1;
  154. len--;
  155. }
  156. /* Check for trailing whitespace. */
  157. if (ws_rule & WS_BLANK_AT_EOL) {
  158. for (i = len - 1; i >= 0; i--) {
  159. if (isspace(line[i])) {
  160. trailing_whitespace = i;
  161. result |= WS_BLANK_AT_EOL;
  162. }
  163. else
  164. break;
  165. }
  166. }
  167. if (trailing_whitespace == -1)
  168. trailing_whitespace = len;
  169. /* Check indentation */
  170. for (i = 0; i < trailing_whitespace; i++) {
  171. if (line[i] == ' ')
  172. continue;
  173. if (line[i] != '\t')
  174. break;
  175. if ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i) {
  176. result |= WS_SPACE_BEFORE_TAB;
  177. if (stream) {
  178. fputs(ws, stream);
  179. fwrite(line + written, i - written, 1, stream);
  180. fputs(reset, stream);
  181. fwrite(line + i, 1, 1, stream);
  182. }
  183. } else if (ws_rule & WS_TAB_IN_INDENT) {
  184. result |= WS_TAB_IN_INDENT;
  185. if (stream) {
  186. fwrite(line + written, i - written, 1, stream);
  187. fputs(ws, stream);
  188. fwrite(line + i, 1, 1, stream);
  189. fputs(reset, stream);
  190. }
  191. } else if (stream) {
  192. fwrite(line + written, i - written + 1, 1, stream);
  193. }
  194. written = i + 1;
  195. }
  196. /* Check for indent using non-tab. */
  197. if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {
  198. result |= WS_INDENT_WITH_NON_TAB;
  199. if (stream) {
  200. fputs(ws, stream);
  201. fwrite(line + written, i - written, 1, stream);
  202. fputs(reset, stream);
  203. }
  204. written = i;
  205. }
  206. if (stream) {
  207. /*
  208. * Now the rest of the line starts at "written".
  209. * The non-highlighted part ends at "trailing_whitespace".
  210. */
  211. /* Emit non-highlighted (middle) segment. */
  212. if (trailing_whitespace - written > 0) {
  213. fputs(set, stream);
  214. fwrite(line + written,
  215. trailing_whitespace - written, 1, stream);
  216. fputs(reset, stream);
  217. }
  218. /* Highlight errors in trailing whitespace. */
  219. if (trailing_whitespace != len) {
  220. fputs(ws, stream);
  221. fwrite(line + trailing_whitespace,
  222. len - trailing_whitespace, 1, stream);
  223. fputs(reset, stream);
  224. }
  225. if (trailing_carriage_return)
  226. fputc('\r', stream);
  227. if (trailing_newline)
  228. fputc('\n', stream);
  229. }
  230. return result;
  231. }
  232. void ws_check_emit(const char *line, int len, unsigned ws_rule,
  233. FILE *stream, const char *set,
  234. const char *reset, const char *ws)
  235. {
  236. (void)ws_check_emit_1(line, len, ws_rule, stream, set, reset, ws);
  237. }
  238. unsigned ws_check(const char *line, int len, unsigned ws_rule)
  239. {
  240. return ws_check_emit_1(line, len, ws_rule, NULL, NULL, NULL, NULL);
  241. }
  242. int ws_blank_line(const char *line, int len, unsigned ws_rule)
  243. {
  244. /*
  245. * We _might_ want to treat CR differently from other
  246. * whitespace characters when ws_rule has WS_CR_AT_EOL, but
  247. * for now we just use this stupid definition.
  248. */
  249. while (len-- > 0) {
  250. if (!isspace(*line))
  251. return 0;
  252. line++;
  253. }
  254. return 1;
  255. }
  256. /* Copy the line onto the end of the strbuf while fixing whitespaces */
  257. void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule, int *error_count)
  258. {
  259. /*
  260. * len is number of bytes to be copied from src, starting
  261. * at src. Typically src[len-1] is '\n', unless this is
  262. * the incomplete last line.
  263. */
  264. int i;
  265. int add_nl_to_tail = 0;
  266. int add_cr_to_tail = 0;
  267. int fixed = 0;
  268. int last_tab_in_indent = -1;
  269. int last_space_in_indent = -1;
  270. int need_fix_leading_space = 0;
  271. /*
  272. * Strip trailing whitespace
  273. */
  274. if (ws_rule & WS_BLANK_AT_EOL) {
  275. if (0 < len && src[len - 1] == '\n') {
  276. add_nl_to_tail = 1;
  277. len--;
  278. if (0 < len && src[len - 1] == '\r') {
  279. add_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);
  280. len--;
  281. }
  282. }
  283. if (0 < len && isspace(src[len - 1])) {
  284. while (0 < len && isspace(src[len-1]))
  285. len--;
  286. fixed = 1;
  287. }
  288. }
  289. /*
  290. * Check leading whitespaces (indent)
  291. */
  292. for (i = 0; i < len; i++) {
  293. char ch = src[i];
  294. if (ch == '\t') {
  295. last_tab_in_indent = i;
  296. if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
  297. 0 <= last_space_in_indent)
  298. need_fix_leading_space = 1;
  299. } else if (ch == ' ') {
  300. last_space_in_indent = i;
  301. if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
  302. ws_tab_width(ws_rule) <= i - last_tab_in_indent)
  303. need_fix_leading_space = 1;
  304. } else
  305. break;
  306. }
  307. if (need_fix_leading_space) {
  308. /* Process indent ourselves */
  309. int consecutive_spaces = 0;
  310. int last = last_tab_in_indent + 1;
  311. if (ws_rule & WS_INDENT_WITH_NON_TAB) {
  312. /* have "last" point at one past the indent */
  313. if (last_tab_in_indent < last_space_in_indent)
  314. last = last_space_in_indent + 1;
  315. else
  316. last = last_tab_in_indent + 1;
  317. }
  318. /*
  319. * between src[0..last-1], strip the funny spaces,
  320. * updating them to tab as needed.
  321. */
  322. for (i = 0; i < last; i++) {
  323. char ch = src[i];
  324. if (ch != ' ') {
  325. consecutive_spaces = 0;
  326. strbuf_addch(dst, ch);
  327. } else {
  328. consecutive_spaces++;
  329. if (consecutive_spaces == ws_tab_width(ws_rule)) {
  330. strbuf_addch(dst, '\t');
  331. consecutive_spaces = 0;
  332. }
  333. }
  334. }
  335. while (0 < consecutive_spaces--)
  336. strbuf_addch(dst, ' ');
  337. len -= last;
  338. src += last;
  339. fixed = 1;
  340. } else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {
  341. /* Expand tabs into spaces */
  342. int start = dst->len;
  343. int last = last_tab_in_indent + 1;
  344. for (i = 0; i < last; i++) {
  345. if (src[i] == '\t')
  346. do {
  347. strbuf_addch(dst, ' ');
  348. } while ((dst->len - start) % ws_tab_width(ws_rule));
  349. else
  350. strbuf_addch(dst, src[i]);
  351. }
  352. len -= last;
  353. src += last;
  354. fixed = 1;
  355. }
  356. strbuf_add(dst, src, len);
  357. if (add_cr_to_tail)
  358. strbuf_addch(dst, '\r');
  359. if (add_nl_to_tail)
  360. strbuf_addch(dst, '\n');
  361. if (fixed && error_count)
  362. (*error_count)++;
  363. }