1 /*
2 * Javascript Compressor
3 *
4 * Copyright (c) 2008-2018 Fabrice Bellard
5 * Copyright (c) 2017-2018 Charlie Gordon
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <getopt.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <inttypes.h>
31 #include <unistd.h>
32
33 #include "cutils.h"
34
35 typedef struct JSToken {
36 int tok;
37 char buf[20];
38 char *str;
39 int len;
40 int size;
41 int line_num; /* line number for start of token */
42 int lines; /* number of embedded linefeeds in token */
43 } JSToken;
44
45 enum {
46 TOK_EOF = 256,
47 TOK_IDENT,
48 TOK_STR1,
49 TOK_STR2,
50 TOK_STR3,
51 TOK_NUM,
52 TOK_COM,
53 TOK_LCOM,
54 };
55
tok_reset(JSToken * tt)56 void tok_reset(JSToken *tt)
57 {
58 if (tt->str != tt->buf) {
59 free(tt->str);
60 tt->str = tt->buf;
61 tt->size = sizeof(tt->buf);
62 }
63 tt->len = 0;
64 }
65
tok_add_ch(JSToken * tt,int c)66 void tok_add_ch(JSToken *tt, int c)
67 {
68 if (tt->len + 1 > tt->size) {
69 tt->size *= 2;
70 if (tt->str == tt->buf) {
71 tt->str = malloc(tt->size);
72 memcpy(tt->str, tt->buf, tt->len);
73 } else {
74 tt->str = realloc(tt->str, tt->size);
75 }
76 }
77 tt->str[tt->len++] = c;
78 }
79
80 FILE *infile;
81 const char *filename;
82 int output_line_num;
83 int line_num;
84 int ch;
85 JSToken tokc;
86
87 int skip_mask;
88 #define DEFINE_MAX 20
89 char *define_tab[DEFINE_MAX];
90 int define_len;
91
error(const char * fmt,...)92 void error(const char *fmt, ...)
93 {
94 va_list ap;
95 va_start(ap, fmt);
96 if (filename) {
97 fprintf(stderr, "%s:%d: ", filename, line_num);
98 } else {
99 fprintf(stderr, "jscompress: ");
100 }
101 vfprintf(stderr, fmt, ap);
102 fprintf(stderr, "\n");
103 va_end(ap);
104 exit(1);
105 }
106
define_symbol(const char * def)107 void define_symbol(const char *def)
108 {
109 int i;
110 for (i = 0; i < define_len; i++) {
111 if (!strcmp(tokc.str, define_tab[i]))
112 return;
113 }
114 if (define_len >= DEFINE_MAX)
115 error("too many defines");
116 define_tab[define_len++] = strdup(def);
117 }
118
undefine_symbol(const char * def)119 void undefine_symbol(const char *def)
120 {
121 int i, j;
122 for (i = j = 0; i < define_len; i++) {
123 if (!strcmp(tokc.str, define_tab[i])) {
124 free(define_tab[i]);
125 } else {
126 define_tab[j++] = define_tab[i];
127 }
128 }
129 define_len = j;
130 }
131
find_symbol(const char * def)132 const char *find_symbol(const char *def)
133 {
134 int i;
135 for (i = 0; i < define_len; i++) {
136 if (!strcmp(tokc.str, define_tab[i]))
137 return "1";
138 }
139 return NULL;
140 }
141
142 void next(void);
143
nextch(void)144 void nextch(void)
145 {
146 ch = fgetc(infile);
147 if (ch == '\n')
148 line_num++;
149 }
150
skip_blanks(void)151 int skip_blanks(void)
152 {
153 for (;;) {
154 next();
155 if (tokc.tok != ' ' && tokc.tok != '\t' &&
156 tokc.tok != TOK_COM && tokc.tok != TOK_LCOM)
157 return tokc.tok;
158 }
159 }
160
parse_directive(void)161 void parse_directive(void)
162 {
163 int ifdef, mask = skip_mask;
164 /* simplistic preprocessor:
165 #define / #undef / #ifdef / #ifndef / #else / #endif
166 no symbol substitution.
167 */
168 skip_mask = 0; /* disable skipping to parse preprocessor line */
169 nextch();
170 if (skip_blanks() != TOK_IDENT)
171 error("expected preprocessing directive after #");
172
173 if (!strcmp(tokc.str, "define")) {
174 if (skip_blanks() != TOK_IDENT)
175 error("expected identifier after #define");
176 define_symbol(tokc.str);
177 } else if (!strcmp(tokc.str, "undef")) {
178 if (skip_blanks() != TOK_IDENT)
179 error("expected identifier after #undef");
180 undefine_symbol(tokc.str);
181 } else if ((ifdef = 1, !strcmp(tokc.str, "ifdef")) ||
182 (ifdef = 0, !strcmp(tokc.str, "ifndef"))) {
183 if (skip_blanks() != TOK_IDENT)
184 error("expected identifier after #ifdef/#ifndef");
185 mask = (mask << 2) | 2 | ifdef;
186 if (find_symbol(tokc.str))
187 mask ^= 1;
188 } else if (!strcmp(tokc.str, "else")) {
189 if (!(mask & 2))
190 error("#else without a #if");
191 mask ^= 1;
192 } else if (!strcmp(tokc.str, "endif")) {
193 if (!(mask & 2))
194 error("#endif without a #if");
195 mask >>= 2;
196 } else {
197 error("unsupported preprocessing directive");
198 }
199 if (skip_blanks() != '\n')
200 error("extra characters on preprocessing line");
201 skip_mask = mask;
202 }
203
204 /* return -1 if invalid char */
hex_to_num(int ch)205 static int hex_to_num(int ch)
206 {
207 if (ch >= 'a' && ch <= 'f')
208 return ch - 'a' + 10;
209 else if (ch >= 'A' && ch <= 'F')
210 return ch - 'A' + 10;
211 else if (ch >= '0' && ch <= '9')
212 return ch - '0';
213 else
214 return -1;
215 }
216
next(void)217 void next(void)
218 {
219 again:
220 tok_reset(&tokc);
221 tokc.line_num = line_num;
222 tokc.lines = 0;
223 switch(ch) {
224 case EOF:
225 tokc.tok = TOK_EOF;
226 if (skip_mask)
227 error("missing #endif");
228 break;
229 case 'a' ... 'z':
230 case 'A' ... 'Z':
231 case '_':
232 case '$':
233 tok_add_ch(&tokc, ch);
234 nextch();
235 while ((ch >= 'a' && ch <= 'z') ||
236 (ch >= 'A' && ch <= 'Z') ||
237 (ch >= '0' && ch <= '9') ||
238 (ch == '_' || ch == '$')) {
239 tok_add_ch(&tokc, ch);
240 nextch();
241 }
242 tok_add_ch(&tokc, '\0');
243 tokc.tok = TOK_IDENT;
244 break;
245 case '.':
246 nextch();
247 if (ch >= '0' && ch <= '9') {
248 tok_add_ch(&tokc, '.');
249 goto has_dot;
250 }
251 tokc.tok = '.';
252 break;
253 case '0':
254 tok_add_ch(&tokc, ch);
255 nextch();
256 if (ch == 'x' || ch == 'X') {
257 /* hexa */
258 tok_add_ch(&tokc, ch);
259 nextch();
260 while ((ch >= 'a' && ch <= 'f') ||
261 (ch >= 'A' && ch <= 'F') ||
262 (ch >= '0' && ch <= '9')) {
263 tok_add_ch(&tokc, ch);
264 nextch();
265 }
266 tok_add_ch(&tokc, '\0');
267 tokc.tok = TOK_NUM;
268 break;
269 }
270 goto has_digit;
271
272 case '1' ... '9':
273 tok_add_ch(&tokc, ch);
274 nextch();
275 has_digit:
276 /* decimal */
277 while (ch >= '0' && ch <= '9') {
278 tok_add_ch(&tokc, ch);
279 nextch();
280 }
281 if (ch == '.') {
282 tok_add_ch(&tokc, ch);
283 nextch();
284 has_dot:
285 while (ch >= '0' && ch <= '9') {
286 tok_add_ch(&tokc, ch);
287 nextch();
288 }
289 }
290 if (ch == 'e' || ch == 'E') {
291 tok_add_ch(&tokc, ch);
292 nextch();
293 if (ch == '+' || ch == '-') {
294 tok_add_ch(&tokc, ch);
295 nextch();
296 }
297 while (ch >= '0' && ch <= '9') {
298 tok_add_ch(&tokc, ch);
299 nextch();
300 }
301 }
302 tok_add_ch(&tokc, '\0');
303 tokc.tok = TOK_NUM;
304 break;
305 case '`':
306 {
307 nextch();
308 while (ch != '`' && ch != EOF) {
309 if (ch == '\\') {
310 tok_add_ch(&tokc, ch);
311 nextch();
312 if (ch == EOF) {
313 error("unexpected char after '\\'");
314 }
315 tok_add_ch(&tokc, ch);
316 } else {
317 tok_add_ch(&tokc, ch);
318 nextch();
319 }
320 }
321 nextch();
322 tok_add_ch(&tokc, 0);
323 tokc.tok = TOK_STR3;
324 }
325 break;
326 case '\"':
327 case '\'':
328 {
329 int n, i, c, hex_digit_count;
330 int quote_ch;
331 quote_ch = ch;
332 nextch();
333 while (ch != quote_ch && ch != EOF) {
334 if (ch == '\\') {
335 nextch();
336 switch(ch) {
337 case 'n':
338 tok_add_ch(&tokc, '\n');
339 nextch();
340 break;
341 case 'r':
342 tok_add_ch(&tokc, '\r');
343 nextch();
344 break;
345 case 't':
346 tok_add_ch(&tokc, '\t');
347 nextch();
348 break;
349 case 'v':
350 tok_add_ch(&tokc, '\v');
351 nextch();
352 break;
353 case '\"':
354 case '\'':
355 case '\\':
356 tok_add_ch(&tokc, ch);
357 nextch();
358 break;
359 case '0' ... '7':
360 n = 0;
361 while (ch >= '0' && ch <= '7') {
362 n = n * 8 + (ch - '0');
363 nextch();
364 }
365 tok_add_ch(&tokc, n);
366 break;
367 case 'x':
368 case 'u':
369 if (ch == 'x')
370 hex_digit_count = 2;
371 else
372 hex_digit_count = 4;
373 nextch();
374 n = 0;
375 for(i = 0; i < hex_digit_count; i++) {
376 c = hex_to_num(ch);
377 if (c < 0)
378 error("unexpected char after '\\x'");
379 n = n * 16 + c;
380 nextch();
381 }
382 if (n >= 256)
383 error("unicode is currently unsupported");
384 tok_add_ch(&tokc, n);
385 break;
386
387 default:
388 error("unexpected char after '\\'");
389 }
390 } else {
391 /* XXX: should refuse embedded newlines */
392 tok_add_ch(&tokc, ch);
393 nextch();
394 }
395 }
396 nextch();
397 tok_add_ch(&tokc, 0);
398 if (quote_ch == '\'')
399 tokc.tok = TOK_STR1;
400 else
401 tokc.tok = TOK_STR2;
402 }
403 break;
404 case '/':
405 nextch();
406 if (ch == '/') {
407 tok_add_ch(&tokc, '/');
408 tok_add_ch(&tokc, ch);
409 nextch();
410 while (ch != '\n' && ch != EOF) {
411 tok_add_ch(&tokc, ch);
412 nextch();
413 }
414 tok_add_ch(&tokc, '\0');
415 tokc.tok = TOK_LCOM;
416 } else if (ch == '*') {
417 int last;
418 tok_add_ch(&tokc, '/');
419 tok_add_ch(&tokc, ch);
420 last = 0;
421 for(;;) {
422 nextch();
423 if (ch == EOF)
424 error("unterminated comment");
425 if (ch == '\n')
426 tokc.lines++;
427 tok_add_ch(&tokc, ch);
428 if (last == '*' && ch == '/')
429 break;
430 last = ch;
431 }
432 nextch();
433 tok_add_ch(&tokc, '\0');
434 tokc.tok = TOK_COM;
435 } else {
436 tokc.tok = '/';
437 }
438 break;
439 case '#':
440 parse_directive();
441 goto again;
442 case '\n':
443 /* adjust line number */
444 tokc.line_num--;
445 tokc.lines++;
446 /* fall thru */
447 default:
448 tokc.tok = ch;
449 nextch();
450 break;
451 }
452 if (skip_mask & 1)
453 goto again;
454 }
455
print_tok(FILE * f,JSToken * tt)456 void print_tok(FILE *f, JSToken *tt)
457 {
458 /* keep output lines in sync with input lines */
459 while (output_line_num < tt->line_num) {
460 putc('\n', f);
461 output_line_num++;
462 }
463
464 switch(tt->tok) {
465 case TOK_IDENT:
466 case TOK_COM:
467 case TOK_LCOM:
468 fprintf(f, "%s", tt->str);
469 break;
470 case TOK_NUM:
471 {
472 unsigned long a;
473 char *p;
474 a = strtoul(tt->str, &p, 0);
475 if (*p == '\0' && a <= 0x7fffffff) {
476 /* must be an integer */
477 fprintf(f, "%d", (int)a);
478 } else {
479 fprintf(f, "%s", tt->str);
480 }
481 }
482 break;
483 case TOK_STR3:
484 fprintf(f, "`%s`", tt->str);
485 break;
486 case TOK_STR1:
487 case TOK_STR2:
488 {
489 int i, c, quote_ch;
490 if (tt->tok == TOK_STR1)
491 quote_ch = '\'';
492 else
493 quote_ch = '\"';
494 fprintf(f, "%c", quote_ch);
495 for(i = 0; i < tt->len - 1; i++) {
496 c = (uint8_t)tt->str[i];
497 switch(c) {
498 case '\r':
499 fprintf(f, "\\r");
500 break;
501 case '\n':
502 fprintf(f, "\\n");
503 break;
504 case '\t':
505 fprintf(f, "\\t");
506 break;
507 case '\v':
508 fprintf(f, "\\v");
509 break;
510 case '\"':
511 case '\'':
512 if (c == quote_ch)
513 fprintf(f, "\\%c", c);
514 else
515 fprintf(f, "%c", c);
516 break;
517 case '\\':
518 fprintf(f, "\\\\");
519 break;
520 default:
521 /* XXX: no utf-8 support! */
522 if (c >= 32 && c <= 255) {
523 fprintf(f, "%c", c);
524 } else if (c <= 255)
525 fprintf(f, "\\x%02x", c);
526 else
527 fprintf(f, "\\u%04x", c);
528 break;
529 }
530 }
531 fprintf(f, "%c", quote_ch);
532 }
533 break;
534 default:
535 if (tokc.tok >= 256)
536 error("unsupported token in print_tok: %d", tt->tok);
537 fprintf(f, "%c", tt->tok);
538 break;
539 }
540 output_line_num += tt->lines;
541 }
542
543 /* check if token pasting could occur */
compat_token(int c1,int c2)544 static BOOL compat_token(int c1, int c2)
545 {
546 if ((c1 == TOK_IDENT || c1 == TOK_NUM) &&
547 (c2 == TOK_IDENT || c2 == TOK_NUM))
548 return FALSE;
549
550 if ((c1 == c2 && strchr("+-<>&|=*/.", c1))
551 || (c2 == '=' && strchr("+-<>&|!*/^%", c1))
552 || (c1 == '=' && c2 == '>')
553 || (c1 == '/' && c2 == '*')
554 || (c1 == '.' && c2 == TOK_NUM)
555 || (c1 == TOK_NUM && c2 == '.'))
556 return FALSE;
557
558 return TRUE;
559 }
560
js_compress(const char * filename,const char * outfilename,BOOL do_strip,BOOL keep_header)561 void js_compress(const char *filename, const char *outfilename,
562 BOOL do_strip, BOOL keep_header)
563 {
564 FILE *outfile;
565 int ltok, seen_space;
566
567 line_num = 1;
568 infile = fopen(filename, "rb");
569 if (!infile) {
570 perror(filename);
571 exit(1);
572 }
573
574 output_line_num = 1;
575 outfile = fopen(outfilename, "wb");
576 if (!outfile) {
577 perror(outfilename);
578 exit(1);
579 }
580
581 nextch();
582 next();
583 ltok = 0;
584 seen_space = 0;
585 if (do_strip) {
586 if (keep_header) {
587 while (tokc.tok == ' ' ||
588 tokc.tok == '\n' ||
589 tokc.tok == '\t' ||
590 tokc.tok == '\v' ||
591 tokc.tok == '\b' ||
592 tokc.tok == '\f') {
593 seen_space = 1;
594 next();
595 }
596 if (tokc.tok == TOK_COM) {
597 print_tok(outfile, &tokc);
598 //fprintf(outfile, "\n");
599 ltok = tokc.tok;
600 seen_space = 0;
601 next();
602 }
603 }
604
605 for(;;) {
606 if (tokc.tok == TOK_EOF)
607 break;
608 if (tokc.tok == ' ' ||
609 tokc.tok == '\r' ||
610 tokc.tok == '\t' ||
611 tokc.tok == '\v' ||
612 tokc.tok == '\b' ||
613 tokc.tok == '\f' ||
614 tokc.tok == TOK_LCOM ||
615 tokc.tok == TOK_COM) {
616 /* don't print spaces or comments */
617 seen_space = 1;
618 } else if (tokc.tok == TOK_STR3) {
619 print_tok(outfile, &tokc);
620 ltok = tokc.tok;
621 seen_space = 0;
622 } else if (tokc.tok == TOK_STR1 || tokc.tok == TOK_STR2) {
623 int count, i;
624 /* find the optimal quote char */
625 count = 0;
626 for(i = 0; i < tokc.len; i++) {
627 if (tokc.str[i] == '\'')
628 count++;
629 else if (tokc.str[i] == '\"')
630 count--;
631 }
632 if (count > 0)
633 tokc.tok = TOK_STR2;
634 else if (count < 0)
635 tokc.tok = TOK_STR1;
636 print_tok(outfile, &tokc);
637 ltok = tokc.tok;
638 seen_space = 0;
639 } else {
640 if (seen_space && !compat_token(ltok, tokc.tok)) {
641 fprintf(outfile, " ");
642 }
643 print_tok(outfile, &tokc);
644 ltok = tokc.tok;
645 seen_space = 0;
646 }
647 next();
648 }
649 } else {
650 /* just handle preprocessing */
651 while (tokc.tok != TOK_EOF) {
652 print_tok(outfile, &tokc);
653 next();
654 }
655 }
656
657 fclose(outfile);
658 fclose(infile);
659 }
660
661 #define HASH_SIZE 30011
662 #define MATCH_LEN_MIN 3
663 #define MATCH_LEN_MAX (4 + 63)
664 #define DIST_MAX 65535
665
find_longest_match(int * pdist,const uint8_t * src,int src_len,const int * hash_next,int cur_pos)666 static int find_longest_match(int *pdist, const uint8_t *src, int src_len,
667 const int *hash_next, int cur_pos)
668 {
669 int pos, i, match_len, match_pos, pos_min, len_max;
670
671 len_max = min_int(src_len - cur_pos, MATCH_LEN_MAX);
672 match_len = 0;
673 match_pos = 0;
674 pos_min = max_int(cur_pos - DIST_MAX - 1, 0);
675 pos = hash_next[cur_pos];
676 while (pos >= pos_min) {
677 for(i = 0; i < len_max; i++) {
678 if (src[cur_pos + i] != src[pos + i])
679 break;
680 }
681 if (i > match_len) {
682 match_len = i;
683 match_pos = pos;
684 }
685 pos = hash_next[pos];
686 }
687 *pdist = cur_pos - match_pos - 1;
688 return match_len;
689 }
690
lz_compress(uint8_t ** pdst,const uint8_t * src,int src_len)691 int lz_compress(uint8_t **pdst, const uint8_t *src, int src_len)
692 {
693 int *hash_table, *hash_next;
694 uint32_t h, v;
695 int i, dist, len, len1, dist1;
696 uint8_t *dst, *q;
697
698 /* build the hash table */
699
700 hash_table = malloc(sizeof(hash_table[0]) * HASH_SIZE);
701 for(i = 0; i < HASH_SIZE; i++)
702 hash_table[i] = -1;
703 hash_next = malloc(sizeof(hash_next[0]) * src_len);
704 for(i = 0; i < src_len; i++)
705 hash_next[i] = -1;
706
707 for(i = 0; i < src_len - MATCH_LEN_MIN + 1; i++) {
708 h = ((src[i] << 16) | (src[i + 1] << 8) | src[i + 2]) % HASH_SIZE;
709 hash_next[i] = hash_table[h];
710 hash_table[h] = i;
711 }
712 for(;i < src_len; i++) {
713 hash_next[i] = -1;
714 }
715 free(hash_table);
716
717 dst = malloc(src_len + 4); /* never larger than the source */
718 q = dst;
719 *q++ = src_len >> 24;
720 *q++ = src_len >> 16;
721 *q++ = src_len >> 8;
722 *q++ = src_len >> 0;
723 /* compress */
724 i = 0;
725 while (i < src_len) {
726 if (src[i] >= 128)
727 return -1;
728 len = find_longest_match(&dist, src, src_len, hash_next, i);
729 if (len >= MATCH_LEN_MIN) {
730 /* heuristic: see if better length just after */
731 len1 = find_longest_match(&dist1, src, src_len, hash_next, i + 1);
732 if (len1 > len)
733 goto no_match;
734 }
735 if (len < MATCH_LEN_MIN) {
736 no_match:
737 *q++ = src[i];
738 i++;
739 } else if (len <= (3 + 15) && dist < (1 << 10)) {
740 v = 0x8000 | ((len - 3) << 10) | dist;
741 *q++ = v >> 8;
742 *q++ = v;
743 i += len;
744 } else if (len >= 4 && len <= (4 + 63) && dist < (1 << 16)) {
745 v = 0xc00000 | ((len - 4) << 16) | dist;
746 *q++ = v >> 16;
747 *q++ = v >> 8;
748 *q++ = v;
749 i += len;
750 } else {
751 goto no_match;
752 }
753 }
754 free(hash_next);
755 *pdst = dst;
756 return q - dst;
757 }
758
load_file(uint8_t ** pbuf,const char * filename)759 static int load_file(uint8_t **pbuf, const char *filename)
760 {
761 FILE *f;
762 uint8_t *buf;
763 int buf_len;
764
765 f = fopen(filename, "rb");
766 if (!f) {
767 perror(filename);
768 exit(1);
769 }
770 fseek(f, 0, SEEK_END);
771 buf_len = ftell(f);
772 fseek(f, 0, SEEK_SET);
773 buf = malloc(buf_len + 1);
774 fread(buf, 1, buf_len, f);
775 buf[buf_len] = '\0';
776 fclose(f);
777 *pbuf = buf;
778 return buf_len;
779 }
780
save_file(const char * filename,const uint8_t * buf,int buf_len)781 static void save_file(const char *filename, const uint8_t *buf, int buf_len)
782 {
783 FILE *f;
784
785 f = fopen(filename, "wb");
786 if (!f) {
787 perror(filename);
788 exit(1);
789 }
790 fwrite(buf, 1, buf_len, f);
791 fclose(f);
792 }
793
save_c_source(const char * filename,const uint8_t * buf,int buf_len,const char * var_name)794 static void save_c_source(const char *filename, const uint8_t *buf, int buf_len,
795 const char *var_name)
796 {
797 FILE *f;
798 int i;
799
800 f = fopen(filename, "wb");
801 if (!f) {
802 perror(filename);
803 exit(1);
804 }
805 fprintf(f, "/* This file is automatically generated - do not edit */\n\n");
806 fprintf(f, "const uint8_t %s[] = {\n", var_name);
807 for(i = 0; i < buf_len; i++) {
808 fprintf(f, " 0x%02x,", buf[i]);
809 if ((i % 8) == 7 || (i == buf_len - 1))
810 fprintf(f, "\n");
811 }
812 fprintf(f, "};\n");
813 fclose(f);
814 }
815
816 #define DEFAULT_OUTPUT_FILENAME "out.js"
817
help(void)818 void help(void)
819 {
820 printf("jscompress version 1.0 Copyright (c) 2008-2018 Fabrice Bellard\n"
821 "usage: jscompress [options] filename\n"
822 "Javascript compressor\n"
823 "\n"
824 "-h print this help\n"
825 "-n do not compress spaces\n"
826 "-H keep the first comment\n"
827 "-c compress to file\n"
828 "-C name compress to C source ('name' is the variable name)\n"
829 "-D symbol define preprocessor symbol\n"
830 "-U symbol undefine preprocessor symbol\n"
831 "-o outfile set the output filename (default=%s)\n",
832 DEFAULT_OUTPUT_FILENAME);
833 exit(1);
834 }
835
main(int argc,char ** argv)836 int main(int argc, char **argv)
837 {
838 int c, do_strip, keep_header, compress;
839 const char *out_filename, *c_var, *fname;
840 char tmpfilename[1024];
841
842 do_strip = 1;
843 keep_header = 0;
844 out_filename = DEFAULT_OUTPUT_FILENAME;
845 compress = 0;
846 c_var = NULL;
847 for(;;) {
848 c = getopt(argc, argv, "hno:HcC:D:U:");
849 if (c == -1)
850 break;
851 switch(c) {
852 case 'h':
853 help();
854 break;
855 case 'n':
856 do_strip = 0;
857 break;
858 case 'o':
859 out_filename = optarg;
860 break;
861 case 'H':
862 keep_header = 1;
863 break;
864 case 'c':
865 compress = 1;
866 break;
867 case 'C':
868 c_var = optarg;
869 compress = 1;
870 break;
871 case 'D':
872 define_symbol(optarg);
873 break;
874 case 'U':
875 undefine_symbol(optarg);
876 break;
877 }
878 }
879 if (optind >= argc)
880 help();
881
882 filename = argv[optind++];
883
884 if (compress) {
885 #if defined(__ANDROID__)
886 /* XXX: use another directory ? */
887 snprintf(tmpfilename, sizeof(tmpfilename), "out.%d", getpid());
888 #else
889 snprintf(tmpfilename, sizeof(tmpfilename), "/tmp/out.%d", getpid());
890 #endif
891 fname = tmpfilename;
892 } else {
893 fname = out_filename;
894 }
895 js_compress(filename, fname, do_strip, keep_header);
896
897 if (compress) {
898 uint8_t *buf1, *buf2;
899 int buf1_len, buf2_len;
900
901 buf1_len = load_file(&buf1, fname);
902 unlink(fname);
903 buf2_len = lz_compress(&buf2, buf1, buf1_len);
904 if (buf2_len < 0) {
905 fprintf(stderr, "Could not compress file (UTF8 chars are forbidden)\n");
906 exit(1);
907 }
908
909 if (c_var) {
910 save_c_source(out_filename, buf2, buf2_len, c_var);
911 } else {
912 save_file(out_filename, buf2, buf2_len);
913 }
914 free(buf1);
915 free(buf2);
916 }
917 return 0;
918 }
919