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