summaryrefslogtreecommitdiff
path: root/builtin-stripspace.c
blob: 55716873dc4f4a8baa5f2b37d6dca1de488b38e4 (plain)
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
#include "builtin.h"
#include "cache.h"

/*
 * Returns the length of a line, without trailing spaces.
 *
 * If the line ends with newline, it will be removed too.
 */
static size_t cleanup(char *line, size_t len)
{
	if (len) {
		if (line[len - 1] == '\n')
			len--;

		while (len) {
			unsigned char c = line[len - 1];
			if (!isspace(c))
				break;
			len--;
		}
	}
	return len;
}

/*
 * Remove empty lines from the beginning and end
 * and also trailing spaces from every line.
 *
 * Note that the buffer will not be NUL-terminated.
 *
 * Turn multiple consecutive empty lines between paragraphs
 * into just one empty line.
 *
 * If the input has only empty lines and spaces,
 * no output will be produced.
 *
 * If last line has a newline at the end, it will be removed.
 *
 * Enable skip_comments to skip every line starting with "#".
 */
size_t stripspace(char *buffer, size_t length, int skip_comments)
{
	int empties = -1;
	size_t i, j, len, newlen;
	char *eol;

	for (i = j = 0; i < length; i += len, j += newlen) {
		eol = memchr(buffer + i, '\n', length - i);
		len = eol ? eol - (buffer + i) + 1 : length - i;

		if (skip_comments && len && buffer[i] == '#') {
			newlen = 0;
			continue;
		}
		newlen = cleanup(buffer + i, len);

		/* Not just an empty line? */
		if (newlen) {
			if (empties != -1)
				buffer[j++] = '\n';
			if (empties > 0)
				buffer[j++] = '\n';
			empties = 0;
			memmove(buffer + j, buffer + i, newlen);
			continue;
		}
		if (empties < 0)
			continue;
		empties++;
	}

	return j;
}

int cmd_stripspace(int argc, const char **argv, const char *prefix)
{
	char *buffer;
	unsigned long size;

	size = 1024;
	buffer = xmalloc(size);
	if (read_fd(0, &buffer, &size)) {
		free(buffer);
		die("could not read the input");
	}

	size = stripspace(buffer, size, 0);
	write_or_die(1, buffer, size);
	if (size)
		putc('\n', stdout);

	free(buffer);
	return 0;
}