1 /* jsmin.c
2 2011-01-22
3
4 Copyright (c) 2002 Douglas Crockford (www.crockford.com)
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy of
7 this software and associated documentation files (the "Software"), to deal in
8 the Software without restriction, including without limitation the rights to
9 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10 of the Software, and to permit persons to whom the Software is furnished to do
11 so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15
16 The Software shall be used for Good, not Evil.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25 */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29
30 static int theA;
31 static int theB;
32 static int theLookahead = EOF;
33
34
35 /* isAlphanum -- return true if the character is a letter, digit, underscore,
36 dollar sign, or non-ASCII character.
37 */
38
39 static int
isAlphanum(int c)40 isAlphanum(int c)
41 {
42 return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
43 (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
44 c > 126);
45 }
46
47
48 /* get -- return the next character from stdin. Watch out for lookahead. If
49 the character is a control character, translate it to a space or
50 linefeed.
51 */
52
53 static int
get()54 get()
55 {
56 int c = theLookahead;
57 theLookahead = EOF;
58 if (c == EOF) {
59 c = getc(stdin);
60 }
61 if (c >= ' ' || c == '\n' || c == EOF) {
62 return c;
63 }
64 if (c == '\r') {
65 return '\n';
66 }
67 return ' ';
68 }
69
70
71 /* peek -- get the next character without getting it.
72 */
73
74 static int
peek()75 peek()
76 {
77 theLookahead = get();
78 return theLookahead;
79 }
80
81
82 /* next -- get the next character, excluding comments. peek() is used to see
83 if a '/' is followed by a '/' or '*'.
84 */
85
86 static int
next()87 next()
88 {
89 int c = get();
90 if (c == '/') {
91 switch (peek()) {
92 case '/':
93 for (;;) {
94 c = get();
95 if (c <= '\n') {
96 return c;
97 }
98 }
99 case '*':
100 get();
101 for (;;) {
102 switch (get()) {
103 case '*':
104 if (peek() == '/') {
105 get();
106 return ' ';
107 }
108 break;
109 case EOF:
110 fprintf(stderr, "Error: JSMIN Unterminated comment.\n");
111 exit(1);
112 }
113 }
114 default:
115 return c;
116 }
117 }
118 return c;
119 }
120
121
122 /* action -- do something! What you do is determined by the argument:
123 1 Output A. Copy B to A. Get the next B.
124 2 Copy B to A. Get the next B. (Delete A).
125 3 Get the next B. (Delete B).
126 action treats a string as a single character. Wow!
127 action recognizes a regular expression if it is preceded by ( or , or =.
128 */
129
130 static void
action(int d)131 action(int d)
132 {
133 switch (d) {
134 case 1:
135 putc(theA, stdout);
136 case 2:
137 theA = theB;
138 if (theA == '\'' || theA == '"') {
139 for (;;) {
140 putc(theA, stdout);
141 theA = get();
142 if (theA == theB) {
143 break;
144 }
145 if (theA == '\\') {
146 putc(theA, stdout);
147 theA = get();
148 }
149 if (theA == EOF) {
150 fprintf(stderr, "Error: JSMIN unterminated string literal.");
151 exit(1);
152 }
153 }
154 }
155 case 3:
156 theB = next();
157 if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' ||
158 theA == ':' || theA == '[' || theA == '!' ||
159 theA == '&' || theA == '|' || theA == '?' ||
160 theA == '{' || theA == '}' || theA == ';' ||
161 theA == '\n')) {
162 putc(theA, stdout);
163 putc(theB, stdout);
164 for (;;) {
165 theA = get();
166 if (theA == '[') {
167 for (;;) {
168 putc(theA, stdout);
169 theA = get();
170 if (theA == ']') {
171 break;
172 }
173 if (theA == '\\') {
174 putc(theA, stdout);
175 theA = get();
176 }
177 if (theA == EOF) {
178 fprintf(stderr,
179 "Error: JSMIN unterminated set in Regular Expression literal.\n");
180 exit(1);
181 }
182 }
183 } else if (theA == '/') {
184 break;
185 } else if (theA =='\\') {
186 putc(theA, stdout);
187 theA = get();
188 }
189 if (theA == EOF) {
190 fprintf(stderr,
191 "Error: JSMIN unterminated Regular Expression literal.\n");
192 exit(1);
193 }
194 putc(theA, stdout);
195 }
196 theB = next();
197 }
198 }
199 }
200
201
202 /* jsmin -- Copy the input to the output, deleting the characters which are
203 insignificant to JavaScript. Comments will be removed. Tabs will be
204 replaced with spaces. Carriage returns will be replaced with linefeeds.
205 Most spaces and linefeeds will be removed.
206 */
207
208 static void
jsmin()209 jsmin()
210 {
211 theA = '\n';
212 action(3);
213 while (theA != EOF) {
214 switch (theA) {
215 case ' ':
216 if (isAlphanum(theB)) {
217 action(1);
218 } else {
219 action(2);
220 }
221 break;
222 case '\n':
223 switch (theB) {
224 case '{':
225 case '[':
226 case '(':
227 case '+':
228 case '-':
229 action(1);
230 break;
231 case ' ':
232 action(3);
233 break;
234 default:
235 if (isAlphanum(theB)) {
236 action(1);
237 } else {
238 action(2);
239 }
240 }
241 break;
242 default:
243 switch (theB) {
244 case ' ':
245 if (isAlphanum(theA)) {
246 action(1);
247 break;
248 }
249 action(3);
250 break;
251 case '\n':
252 switch (theA) {
253 case '}':
254 case ']':
255 case ')':
256 case '+':
257 case '-':
258 case '"':
259 case '\'':
260 action(1);
261 break;
262 default:
263 if (isAlphanum(theA)) {
264 action(1);
265 } else {
266 action(3);
267 }
268 }
269 break;
270 default:
271 action(1);
272 break;
273 }
274 }
275 }
276 }
277
278
279 /* main -- Output any command line arguments as comments
280 and then minify the input.
281 */
282 extern int
main(int argc,char * argv[])283 main(int argc, char* argv[])
284 {
285 int i;
286 for (i = 1; i < argc; i += 1) {
287 fprintf(stdout, "// %s\n", argv[i]);
288 }
289 jsmin();
290 return 0;
291 }
292