-
Notifications
You must be signed in to change notification settings - Fork 0
/
minify.cpp
113 lines (101 loc) · 3.1 KB
/
minify.cpp
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdexcept>
auto read_file(const char *path) -> std::string;
void minify_file(const char *, const char *);
auto minify(const std::string &css) -> std::string;
int main(int argc, char **argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " CSSFILE OUTPUTFILE\n";
return 1;
}
const char *filepath = argv[1];
const char *outpath = argv[2];
// TODO: add support for optional OUTFILE, where output filename is derieved from the css filename.
// Should print an error and exit if a file with the derieved name already exists.
minify_file(filepath, outpath);
return 0;
}
auto read_file(const char *path) -> std::string {
constexpr auto read_size = std::size_t(4096); // <- Exactly what a typical modern C++ programmer would do.
auto stream = std::ifstream(path);
stream.exceptions(std::ios_base::badbit);
auto out = std::string();
auto buf = std::string(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
out.append(buf, 0, stream.gcount());
}
out.append(buf, 0, stream.gcount());
return out;
}
void minify_file(const char *cssfilepath, const char *outfilepath) {
auto css = read_file(cssfilepath);
auto minicss = minify(css);
auto out = std::ofstream(outfilepath);
out << minicss;
}
auto minify(const std::string& css) -> std::string {
auto minicss = std::string();
minicss.reserve(css.size());
bool last_was_space = true; // 'true' to deal with the whitespaces at the very beginning of the file.
char c;
try {
for (size_t i = 0; i < css.size(); ++i) {
c = css.at(i);
switch (c) {
case '\t':
case ' ' :
{
// Replace a sequence of whitespaces with a single space character.
// Note: make sure to set last_was_space to false, everywhere else where a not whitespace
// character has been appended to 'minicss'.
if (!last_was_space) {
minicss += ' ';
last_was_space = true;
}
} break;
case '\n': break; // no newlines allowed!
case ':':
case ';':
case ',':
case '>':
case '{':
case '}':
{
// Make sure that no whitespace is present before the above characters.
if (minicss.back() == ' ') minicss.back() = c;
else minicss += c;
// Consume all whitespace after the above characters.
while (i < css.size()-1 && std::isspace(css.at(i+1))) i++;
last_was_space = false;
} break;
case '\'':
case '"' :
{
// Preseve the whitespaces in strings.
// TODO: this fails if there is an escaped quote between the string. (Is it allowed?)
minicss += c;
while (i < css.size()-1 && css.at(++i) != c) minicss += css.at(i);
minicss += c;
last_was_space = false;
} break;
case '/' :
{
if (css.at(i+1) == '*') {
// Consume the comment.
++i;
while (!(css.at(++i) == '*' && css.at(++i) == '/')) {}
}
else minicss += '/';
} break;
default: minicss += c; last_was_space = false;
}
}
} catch (std::out_of_range &e) {
std::cerr << "Unexpected end of file reached, possible syntax error.\n";
std::exit(1);
}
minicss.shrink_to_fit();
return minicss;
}