Merge branch 'jc/autogc'
[git] / builtin-stripspace.c
1 #include "builtin.h"
2 #include "cache.h"
3
4 /*
5  * Returns the length of a line, without trailing spaces.
6  *
7  * If the line ends with newline, it will be removed too.
8  */
9 static size_t cleanup(char *line, size_t len)
10 {
11         if (len) {
12                 if (line[len - 1] == '\n')
13                         len--;
14
15                 while (len) {
16                         unsigned char c = line[len - 1];
17                         if (!isspace(c))
18                                 break;
19                         len--;
20                 }
21         }
22         return len;
23 }
24
25 /*
26  * Remove empty lines from the beginning and end
27  * and also trailing spaces from every line.
28  *
29  * Note that the buffer will not be NUL-terminated.
30  *
31  * Turn multiple consecutive empty lines between paragraphs
32  * into just one empty line.
33  *
34  * If the input has only empty lines and spaces,
35  * no output will be produced.
36  *
37  * If last line has a newline at the end, it will be removed.
38  *
39  * Enable skip_comments to skip every line starting with "#".
40  */
41 size_t stripspace(char *buffer, size_t length, int skip_comments)
42 {
43         int empties = -1;
44         size_t i, j, len, newlen;
45         char *eol;
46
47         for (i = j = 0; i < length; i += len, j += newlen) {
48                 eol = memchr(buffer + i, '\n', length - i);
49                 len = eol ? eol - (buffer + i) + 1 : length - i;
50
51                 if (skip_comments && len && buffer[i] == '#') {
52                         newlen = 0;
53                         continue;
54                 }
55                 newlen = cleanup(buffer + i, len);
56
57                 /* Not just an empty line? */
58                 if (newlen) {
59                         if (empties != -1)
60                                 buffer[j++] = '\n';
61                         if (empties > 0)
62                                 buffer[j++] = '\n';
63                         empties = 0;
64                         memmove(buffer + j, buffer + i, newlen);
65                         continue;
66                 }
67                 if (empties < 0)
68                         continue;
69                 empties++;
70         }
71
72         return j;
73 }
74
75 int cmd_stripspace(int argc, const char **argv, const char *prefix)
76 {
77         char *buffer;
78         unsigned long size;
79         int strip_comments = 0;
80
81         if (argc > 1 && (!strcmp(argv[1], "-s") ||
82                                 !strcmp(argv[1], "--strip-comments")))
83                 strip_comments = 1;
84
85         size = 1024;
86         buffer = xmalloc(size);
87         if (read_fd(0, &buffer, &size)) {
88                 free(buffer);
89                 die("could not read the input");
90         }
91
92         size = stripspace(buffer, size, strip_comments);
93         write_or_die(1, buffer, size);
94         if (size)
95                 putc('\n', stdout);
96
97         free(buffer);
98         return 0;
99 }