Project

General

Profile

1
/* jsmin.c
2
   2006-05-04
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
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
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
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
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
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 <= '\n') {
146
                    fprintf(stderr,
147
"Error: JSMIN unterminated string literal: %c\n", theA);
148
                    exit(1);
149
                }
150
                if (theA == '\\') {
151
                    putc(theA, stdout);
152
                    theA = get();
153
                }
154
            }
155
        }
156
    case 3:
157
        theB = next();
158
        if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' ||
159
                theA == ':' || theA == '[' || theA == '!' || theA == '&' || 
160
                theA == '|')) {
161
            putc(theA, stdout);
162
            putc(theB, stdout);
163
            for (;;) {
164
                theA = get();
165
                if (theA == '/') {
166
                    break;
167
                } else if (theA =='\\') {
168
                    putc(theA, stdout);
169
                    theA = get();
170
                } else if (theA <= '\n') {
171
                    fprintf(stderr,
172
"Error: JSMIN unterminated Regular Expression literal.\n", theA);
173
                    exit(1);
174
                }
175
                putc(theA, stdout);
176
            }
177
            theB = next();
178
        }
179
    }
180
}
181

    
182

    
183
/* jsmin -- Copy the input to the output, deleting the characters which are
184
        insignificant to JavaScript. Comments will be removed. Tabs will be
185
        replaced with spaces. Carriage returns will be replaced with linefeeds.
186
        Most spaces and linefeeds will be removed.
187
*/
188

    
189
static void
190
jsmin()
191
{
192
    theA = '\n';
193
    action(3);
194
    while (theA != EOF) {
195
        switch (theA) {
196
        case ' ':
197
            if (isAlphanum(theB)) {
198
                action(1);
199
            } else {
200
                action(2);
201
            }
202
            break;
203
        case '\n':
204
            switch (theB) {
205
            case '{':
206
            case '[':
207
            case '(':
208
            case '+':
209
            case '-':
210
                action(1);
211
                break;
212
            case ' ':
213
                action(3);
214
                break;
215
            default:
216
                if (isAlphanum(theB)) {
217
                    action(1);
218
                } else {
219
                    action(2);
220
                }
221
            }
222
            break;
223
        default:
224
            switch (theB) {
225
            case ' ':
226
                if (isAlphanum(theA)) {
227
                    action(1);
228
                    break;
229
                }
230
                action(3);
231
                break;
232
            case '\n':
233
                switch (theA) {
234
                case '}':
235
                case ']':
236
                case ')':
237
                case '+':
238
                case '-':
239
                case '"':
240
                case '\'':
241
                    action(1);
242
                    break;
243
                default:
244
                    if (isAlphanum(theA)) {
245
                        action(1);
246
                    } else {
247
                        action(3);
248
                    }
249
                }
250
                break;
251
            default:
252
                action(1);
253
                break;
254
            }
255
        }
256
    }
257
}
258

    
259

    
260
/* main -- Output any command line arguments as comments
261
        and then minify the input.
262
*/
263
extern int
264
main(int argc, char* argv[])
265
{
266
    int i;
267
    for (i = 1; i < argc; i += 1) {
268
        fprintf(stdout, "// %s\n", argv[i]);
269
    }
270
    jsmin();
271
    return 0;
272
}
(2-2/6)