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.

384 lines
9.4KB

  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. unsigned whitespace_rule(struct index_state *istate, const char *pathname)
  67. {
  68. static struct attr_check *attr_whitespace_rule;
  69. const char *value;
  70. if (!attr_whitespace_rule)
  71. attr_whitespace_rule = attr_check_initl("whitespace", NULL);
  72. git_check_attr(istate, pathname, attr_whitespace_rule);
  73. value = attr_whitespace_rule->items[0].value;
  74. if (ATTR_TRUE(value)) {
  75. /* true (whitespace) */
  76. unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
  77. int i;
  78. for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
  79. if (!whitespace_rule_names[i].loosens_error &&
  80. !whitespace_rule_names[i].exclude_default)
  81. all_rule |= whitespace_rule_names[i].rule_bits;
  82. return all_rule;
  83. } else if (ATTR_FALSE(value)) {
  84. /* false (-whitespace) */
  85. return ws_tab_width(whitespace_rule_cfg);
  86. } else if (ATTR_UNSET(value)) {
  87. /* reset to default (!whitespace) */
  88. return whitespace_rule_cfg;
  89. } else {
  90. /* string */
  91. return parse_whitespace_rule(value);
  92. }
  93. }
  94. /* The returned string should be freed by the caller. */
  95. char *whitespace_error_string(unsigned ws)
  96. {
  97. struct strbuf err = STRBUF_INIT;
  98. if ((ws & WS_TRAILING_SPACE) == WS_TRAILING_SPACE)
  99. strbuf_addstr(&err, "trailing whitespace");
  100. else {
  101. if (ws & WS_BLANK_AT_EOL)
  102. strbuf_addstr(&err, "trailing whitespace");
  103. if (ws & WS_BLANK_AT_EOF) {
  104. if (err.len)
  105. strbuf_addstr(&err, ", ");
  106. strbuf_addstr(&err, "new blank line at EOF");
  107. }
  108. }
  109. if (ws & WS_SPACE_BEFORE_TAB) {
  110. if (err.len)
  111. strbuf_addstr(&err, ", ");
  112. strbuf_addstr(&err, "space before tab in indent");
  113. }
  114. if (ws & WS_INDENT_WITH_NON_TAB) {
  115. if (err.len)
  116. strbuf_addstr(&err, ", ");
  117. strbuf_addstr(&err, "indent with spaces");
  118. }
  119. if (ws & WS_TAB_IN_INDENT) {
  120. if (err.len)
  121. strbuf_addstr(&err, ", ");
  122. strbuf_addstr(&err, "tab in indent");
  123. }
  124. return strbuf_detach(&err, NULL);
  125. }
  126. /* If stream is non-NULL, emits the line after checking. */
  127. static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
  128. FILE *stream, const char *set,
  129. const char *reset, const char *ws)
  130. {
  131. unsigned result = 0;
  132. int written = 0;
  133. int trailing_whitespace = -1;
  134. int trailing_newline = 0;
  135. int trailing_carriage_return = 0;
  136. int i;
  137. /* Logic is simpler if we temporarily ignore the trailing newline. */
  138. if (len > 0 && line[len - 1] == '\n') {
  139. trailing_newline = 1;
  140. len--;
  141. }
  142. if ((ws_rule & WS_CR_AT_EOL) &&
  143. len > 0 && line[len - 1] == '\r') {
  144. trailing_carriage_return = 1;
  145. len--;
  146. }
  147. /* Check for trailing whitespace. */
  148. if (ws_rule & WS_BLANK_AT_EOL) {
  149. for (i = len - 1; i >= 0; i--) {
  150. if (isspace(line[i])) {
  151. trailing_whitespace = i;
  152. result |= WS_BLANK_AT_EOL;
  153. }
  154. else
  155. break;
  156. }
  157. }
  158. if (trailing_whitespace == -1)
  159. trailing_whitespace = len;
  160. /* Check indentation */
  161. for (i = 0; i < trailing_whitespace; i++) {
  162. if (line[i] == ' ')
  163. continue;
  164. if (line[i] != '\t')
  165. break;
  166. if ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i) {
  167. result |= WS_SPACE_BEFORE_TAB;
  168. if (stream) {
  169. fputs(ws, stream);
  170. fwrite(line + written, i - written, 1, stream);
  171. fputs(reset, stream);
  172. fwrite(line + i, 1, 1, stream);
  173. }
  174. } else if (ws_rule & WS_TAB_IN_INDENT) {
  175. result |= WS_TAB_IN_INDENT;
  176. if (stream) {
  177. fwrite(line + written, i - written, 1, stream);
  178. fputs(ws, stream);
  179. fwrite(line + i, 1, 1, stream);
  180. fputs(reset, stream);
  181. }
  182. } else if (stream) {
  183. fwrite(line + written, i - written + 1, 1, stream);
  184. }
  185. written = i + 1;
  186. }
  187. /* Check for indent using non-tab. */
  188. if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {
  189. result |= WS_INDENT_WITH_NON_TAB;
  190. if (stream) {
  191. fputs(ws, stream);
  192. fwrite(line + written, i - written, 1, stream);
  193. fputs(reset, stream);
  194. }
  195. written = i;
  196. }
  197. if (stream) {
  198. /*
  199. * Now the rest of the line starts at "written".
  200. * The non-highlighted part ends at "trailing_whitespace".
  201. */
  202. /* Emit non-highlighted (middle) segment. */
  203. if (trailing_whitespace - written > 0) {
  204. fputs(set, stream);
  205. fwrite(line + written,
  206. trailing_whitespace - written, 1, stream);
  207. fputs(reset, stream);
  208. }
  209. /* Highlight errors in trailing whitespace. */
  210. if (trailing_whitespace != len) {
  211. fputs(ws, stream);
  212. fwrite(line + trailing_whitespace,
  213. len - trailing_whitespace, 1, stream);
  214. fputs(reset, stream);
  215. }
  216. if (trailing_carriage_return)
  217. fputc('\r', stream);
  218. if (trailing_newline)
  219. fputc('\n', stream);
  220. }
  221. return result;
  222. }
  223. void ws_check_emit(const char *line, int len, unsigned ws_rule,
  224. FILE *stream, const char *set,
  225. const char *reset, const char *ws)
  226. {
  227. (void)ws_check_emit_1(line, len, ws_rule, stream, set, reset, ws);
  228. }
  229. unsigned ws_check(const char *line, int len, unsigned ws_rule)
  230. {
  231. return ws_check_emit_1(line, len, ws_rule, NULL, NULL, NULL, NULL);
  232. }
  233. int ws_blank_line(const char *line, int len, unsigned ws_rule)
  234. {
  235. /*
  236. * We _might_ want to treat CR differently from other
  237. * whitespace characters when ws_rule has WS_CR_AT_EOL, but
  238. * for now we just use this stupid definition.
  239. */
  240. while (len-- > 0) {
  241. if (!isspace(*line))
  242. return 0;
  243. line++;
  244. }
  245. return 1;
  246. }
  247. /* Copy the line onto the end of the strbuf while fixing whitespaces */
  248. void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule, int *error_count)
  249. {
  250. /*
  251. * len is number of bytes to be copied from src, starting
  252. * at src. Typically src[len-1] is '\n', unless this is
  253. * the incomplete last line.
  254. */
  255. int i;
  256. int add_nl_to_tail = 0;
  257. int add_cr_to_tail = 0;
  258. int fixed = 0;
  259. int last_tab_in_indent = -1;
  260. int last_space_in_indent = -1;
  261. int need_fix_leading_space = 0;
  262. /*
  263. * Strip trailing whitespace
  264. */
  265. if (ws_rule & WS_BLANK_AT_EOL) {
  266. if (0 < len && src[len - 1] == '\n') {
  267. add_nl_to_tail = 1;
  268. len--;
  269. if (0 < len && src[len - 1] == '\r') {
  270. add_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);
  271. len--;
  272. }
  273. }
  274. if (0 < len && isspace(src[len - 1])) {
  275. while (0 < len && isspace(src[len-1]))
  276. len--;
  277. fixed = 1;
  278. }
  279. }
  280. /*
  281. * Check leading whitespaces (indent)
  282. */
  283. for (i = 0; i < len; i++) {
  284. char ch = src[i];
  285. if (ch == '\t') {
  286. last_tab_in_indent = i;
  287. if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
  288. 0 <= last_space_in_indent)
  289. need_fix_leading_space = 1;
  290. } else if (ch == ' ') {
  291. last_space_in_indent = i;
  292. if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
  293. ws_tab_width(ws_rule) <= i - last_tab_in_indent)
  294. need_fix_leading_space = 1;
  295. } else
  296. break;
  297. }
  298. if (need_fix_leading_space) {
  299. /* Process indent ourselves */
  300. int consecutive_spaces = 0;
  301. int last = last_tab_in_indent + 1;
  302. if (ws_rule & WS_INDENT_WITH_NON_TAB) {
  303. /* have "last" point at one past the indent */
  304. if (last_tab_in_indent < last_space_in_indent)
  305. last = last_space_in_indent + 1;
  306. else
  307. last = last_tab_in_indent + 1;
  308. }
  309. /*
  310. * between src[0..last-1], strip the funny spaces,
  311. * updating them to tab as needed.
  312. */
  313. for (i = 0; i < last; i++) {
  314. char ch = src[i];
  315. if (ch != ' ') {
  316. consecutive_spaces = 0;
  317. strbuf_addch(dst, ch);
  318. } else {
  319. consecutive_spaces++;
  320. if (consecutive_spaces == ws_tab_width(ws_rule)) {
  321. strbuf_addch(dst, '\t');
  322. consecutive_spaces = 0;
  323. }
  324. }
  325. }
  326. while (0 < consecutive_spaces--)
  327. strbuf_addch(dst, ' ');
  328. len -= last;
  329. src += last;
  330. fixed = 1;
  331. } else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {
  332. /* Expand tabs into spaces */
  333. int start = dst->len;
  334. int last = last_tab_in_indent + 1;
  335. for (i = 0; i < last; i++) {
  336. if (src[i] == '\t')
  337. do {
  338. strbuf_addch(dst, ' ');
  339. } while ((dst->len - start) % ws_tab_width(ws_rule));
  340. else
  341. strbuf_addch(dst, src[i]);
  342. }
  343. len -= last;
  344. src += last;
  345. fixed = 1;
  346. }
  347. strbuf_add(dst, src, len);
  348. if (add_cr_to_tail)
  349. strbuf_addch(dst, '\r');
  350. if (add_nl_to_tail)
  351. strbuf_addch(dst, '\n');
  352. if (fixed && error_count)
  353. (*error_count)++;
  354. }