5 kx
5 kx #ifdef HAVE_CONFIG_H
5 kx #include <config.h>
5 kx
5 kx #ifdef HAVE_FCNTL_H
5 kx #include <fcntl.h>
5 kx #endif
5 kx #ifdef HAVE_UNISTD_H
5 kx #include <unistd.h>
5 kx #endif
5 kx
5 kx #ifdef HAVE_SYS_STAT_H
5 kx #include <sys/stat.h>
5 kx #endif
5 kx
5 kx #ifdef HAVE_ERRNO_H
5 kx #include <errno.h>
5 kx #endif
5 kx #ifdef HAVE_STRING_H
5 kx #include <string.h>
5 kx #endif
5 kx
5 kx #ifdef HAVE_STDLIB_H
5 kx #include <stdlib.h>
5 kx #endif
5 kx #ifdef HAVE_STDIO_H
5 kx #include <stdio.h>
5 kx #endif
5 kx #ifdef HAVE_GETOPT_H
5 kx #include <getopt.h>
5 kx #endif
5 kx
5 kx #else
5 kx /**************************************************************
5 kx For building outside from source package as a single C file:
5 kx
5 kx $ gcc -o jsmin main.c
5 kx */
5 kx
5 kx #include <fcntl.h>
5 kx #include <unistd.h>
5 kx
5 kx #include <sys/stat.h>
5 kx
5 kx #include <errno.h>
5 kx #include <string.h>
5 kx
5 kx #include <stdlib.h>
5 kx #include <stdio.h>
5 kx #include <getopt.h>
5 kx
5 kx #endif
5 kx
5 kx static char *ifname = NULL;
5 kx static char *ofname = NULL;
5 kx
5 kx FILE *ifile;
5 kx FILE *ofile;
5 kx FILE *tmp;
5 kx
5 kx static char *progname = NULL;
5 kx
5 kx static int opt_usage;
5 kx static int opt_version;
5 kx
5 kx #define DIR_SEPARATOR '/'
5 kx
5 kx static void usage()
5 kx {
5 kx printf( "\n" );
5 kx printf( "Usage: %s [options] [input_file_name]\n", progname );
5 kx printf( "Options:\n" );
5 kx printf( " --output | -o - output file name;\n" );
5 kx printf( " --version | -v - print version numver;\n" );
5 kx printf( " --help | -h | -? - print this message;\n" );
5 kx printf( " -- - an option terminator;\n" );
5 kx printf( " - - use std{io|out} instead of files.\n\n" );
5 kx printf( "Examples:\n\n" );
5 kx printf( " Input file is 'full.json', output file is 'min.json':\n\n" );
5 kx printf( " $ %s -o min.json full.json\n\n", progname );
5 kx printf( " Input file is 'STDIN', output file is 'min.json':\n\n" );
5 kx printf( " $ %s -o min.json -\n\n", progname );
5 kx printf( " Please note that to terminate your input by keyboard you have to use\n" );
5 kx printf( " <Ctrl>+d combination;\n\n" );
5 kx printf( " Input file is 'full.json', output file is 'STDOUT':\n\n" );
5 kx printf( " $ %s -- full.json\n\n", progname );
5 kx printf( " Use stdin, stdout:\n\n" );
5 kx printf( " $ %s - < full.json > min.json\n\n", progname );
5 kx printf( "Enjoj.\n\n" );
5 kx
5 kx exit( 1 );
5 kx }
5 kx
5 kx static void version()
5 kx {
5 kx #ifdef HAVE_CONFIG_H
5 kx printf( "%s\n", (char *)VERSION );
5 kx #else
5 kx printf( "0.0.1\n" );
5 kx #endif
5 kx
5 kx exit( 1 );
5 kx }
5 kx
5 kx static void
5 kx error( char *s )
5 kx {
5 kx fprintf( stderr, "ERROR: %s: ", progname );
5 kx fprintf( stderr, "%s\n", s );
5 kx exit( 1 );
5 kx }
5 kx
5 kx static int
5 kx is_alpha_or_num( int c )
5 kx {
5 kx return( (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
5 kx (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || c > 126 );
5 kx }
5 kx
5 kx static int a;
5 kx static int b;
5 kx static int lookahead = EOF;
5 kx static int x = EOF;
5 kx static int y = EOF;
5 kx
5 kx /*
5 kx get - return the next character from stdin. Watch out for lookahead. If
5 kx the character is a control character, translate it to a space or
5 kx linefeed.
5 kx */
5 kx static int
5 kx get()
5 kx {
5 kx int c = lookahead;
5 kx lookahead = EOF;
5 kx if( c == EOF )
5 kx {
5 kx c = getc( ifile );
5 kx }
5 kx if( c >= ' ' || c == '\n' || c == EOF )
5 kx {
5 kx return c;
5 kx }
5 kx if( c == '\r' )
5 kx {
5 kx return '\n';
5 kx }
5 kx return ' ';
5 kx }
5 kx
5 kx
5 kx /*
5 kx peek - get the next character without getting it.
5 kx */
5 kx static int
5 kx peek()
5 kx {
5 kx lookahead = get();
5 kx return lookahead;
5 kx }
5 kx
5 kx
5 kx /*
5 kx next - get the next character, excluding comments. peek() is used to see
5 kx if a '/' is followed by a '/' or '*'.
5 kx */
5 kx static int
5 kx next()
5 kx {
5 kx int c = get();
5 kx if ( c == '/' )
5 kx {
5 kx switch( peek() )
5 kx {
5 kx case '/':
5 kx for( ;; )
5 kx {
5 kx c = get();
5 kx if( c <= '\n' )
5 kx {
5 kx break;
5 kx }
5 kx }
5 kx break;
5 kx case '*':
5 kx get();
5 kx while( c != ' ' )
5 kx {
5 kx switch( get() )
5 kx {
5 kx case '*':
5 kx if( peek() == '/' )
5 kx {
5 kx get();
5 kx c = ' ';
5 kx }
5 kx break;
5 kx case EOF:
5 kx error( "Unterminated comment" );
5 kx }
5 kx }
5 kx break;
5 kx }
5 kx }
5 kx y = x;
5 kx x = c;
5 kx return c;
5 kx }
5 kx
5 kx
5 kx /*
5 kx action - do something! What you do is determined by the argument:
5 kx 1 Output A. Copy B to A. Get the next B.
5 kx 2 Copy B to A. Get the next B. (Delete A).
5 kx 3 Get the next B. (Delete B).
5 kx action treats a string as a single character. Wow!
5 kx action recognizes a regular expression if it is preceded by ( or , or =.
5 kx */
5 kx static void
5 kx action( int d )
5 kx {
5 kx switch( d )
5 kx {
5 kx case 1:
5 kx putc( a, ofile );
5 kx if( (y == '\n' || y == ' ') &&
5 kx (a == '+' || a == '-' || a == '*' || a == '/') &&
5 kx (b == '+' || b == '-' || b == '*' || b == '/') )
5 kx {
5 kx putc( y, ofile );
5 kx }
5 kx case 2:
5 kx a = b;
5 kx if( a == '\'' || a == '"' || a == '`' )
5 kx {
5 kx for( ;; )
5 kx {
5 kx putc( a, ofile );
5 kx a = get();
5 kx if( a == b )
5 kx {
5 kx break;
5 kx }
5 kx if( a == '\\' )
5 kx {
5 kx putc( a, ofile );
5 kx a = get();
5 kx }
5 kx if( a == EOF )
5 kx {
5 kx error( "Unterminated string literal" );
5 kx }
5 kx }
5 kx }
5 kx case 3:
5 kx b = next();
5 kx if( b == '/' &&
5 kx ( a == '(' || a == ',' || a == '=' || a == ':' ||
5 kx a == '[' || a == '!' || a == '&' || a == '|' ||
5 kx a == '?' || a == '+' || a == '-' || a == '~' ||
5 kx a == '*' || a == '/' || a == '{' || a == '\n' ) )
5 kx {
5 kx putc( a, ofile );
5 kx if( a == '/' || a == '*' )
5 kx {
5 kx putc( ' ', ofile );
5 kx }
5 kx putc( b, ofile );
5 kx for( ;; )
5 kx {
5 kx a = get();
5 kx if( a == '[' )
5 kx {
5 kx for( ;; )
5 kx {
5 kx putc( a, ofile );
5 kx a = get();
5 kx if( a == ']' )
5 kx {
5 kx break;
5 kx }
5 kx if( a == '\\' )
5 kx {
5 kx putc( a, ofile );
5 kx a = get();
5 kx }
5 kx if( a == EOF )
5 kx {
5 kx error( "Unterminated set in Regular Expression literal" );
5 kx }
5 kx }
5 kx }
5 kx else if( a == '/' )
5 kx {
5 kx switch( peek() )
5 kx {
5 kx case '/':
5 kx case '*':
5 kx error( "Unterminated set in Regular Expression literal" );
5 kx }
5 kx break;
5 kx }
5 kx else if( a =='\\' )
5 kx {
5 kx putc( a, ofile );
5 kx a = get();
5 kx }
5 kx if( a == EOF )
5 kx {
5 kx error( "Unterminated Regular Expression literal" );
5 kx }
5 kx putc( a, ofile );
5 kx }
5 kx b = next();
5 kx }
5 kx }
5 kx }
5 kx
5 kx
5 kx /*
5 kx jsmin - Copy the input to the output, deleting the characters which are
5 kx insignificant to JavaScript. Comments will be removed. Tabs will be
5 kx replaced with spaces. Carriage returns will be replaced with linefeeds.
5 kx Most spaces and linefeeds will be removed.
5 kx */
5 kx
5 kx static void
5 kx jsmin()
5 kx {
5 kx if( peek() == 0xEF ) { get(); get(); get(); }
5 kx a = '\n';
5 kx action( 3 );
5 kx
5 kx while( a != EOF )
5 kx {
5 kx switch( a )
5 kx {
5 kx case ' ':
5 kx action(is_alpha_or_num(b) ? 1 : 2);
5 kx break;
5 kx case '\n':
5 kx switch( b )
5 kx {
5 kx case '{': case '[': case '(':
5 kx case '+': case '-': case '!':
5 kx case '~':
5 kx action( 1 );
5 kx break;
5 kx case ' ':
5 kx action( 3 );
5 kx break;
5 kx default:
5 kx action( is_alpha_or_num(b) ? 1 : 2 );
5 kx }
5 kx break;
5 kx default:
5 kx switch( b )
5 kx {
5 kx case ' ':
5 kx action( is_alpha_or_num(a) ? 1 : 3 );
5 kx break;
5 kx case '\n':
5 kx switch( a )
5 kx {
5 kx case '}': case ']': case ')':
5 kx case '+': case '-': case '"':
5 kx case '\'': case '`':
5 kx action( 1 );
5 kx break;
5 kx default:
5 kx action( is_alpha_or_num(a) ? 1 : 3 );
5 kx }
5 kx break;
5 kx default:
5 kx action( 1 );
5 kx break;
5 kx }
5 kx }
5 kx }
5 kx /* lats carriage return */
5 kx putc( '\n', ofile );
5 kx }
5 kx
5 kx int is_file_exist( const char *filename )
5 kx {
5 kx struct stat st;
5 kx int result = stat( filename, &st );
5 kx return result == 0;
5 kx }
5 kx
5 kx static void
5 kx getargs( argc, argv )
5 kx int argc;
5 kx char *argv[];
5 kx {
5 kx int option = 0;
5 kx int option_index = 0;
5 kx static struct option long_options[] =
5 kx {
5 kx { "output", required_argument, 0, 'o' },
5 kx { "help", no_argument, 0, 'h' },
5 kx { "version", no_argument, 0, 'v' },
5 kx { 0, 0, 0, 0 },
5 kx };
5 kx
5 kx opterr = 0;
5 kx
5 kx while( (option = getopt_long( argc, argv, "o:hv", long_options, &option_index )) != -1 )
5 kx {
5 kx switch( option )
5 kx {
5 kx case 'o':
5 kx ofname = optarg;
5 kx break;
5 kx case 'h':
5 kx opt_usage = 1;
5 kx break;
5 kx case 'v':
5 kx opt_version = 1;
5 kx break;
5 kx case '?':
5 kx {
5 kx if( optopt == 'o' )
5 kx fprintf( stderr,"\nERROR: %s: option '-%c' requires an argument\n\n", progname, optopt );
5 kx }
5 kx default:
5 kx usage();
5 kx break;
5 kx }
5 kx }
5 kx
5 kx if( optind < argc )
5 kx {
5 kx ifname = argv[optind++];
5 kx if( optind < argc ) usage();
5 kx }
5 kx
5 kx if( opt_usage ) usage();
5 kx if( opt_version ) version();
5 kx if( opt_usage || ( ! ifname && ! ofname ) ) usage();
5 kx
5 kx if( ! ofname ) ofname = "-" ;
5 kx if( ! ifname ) ifname = "-" ;
5 kx }
5 kx
5 kx int main( int argc, char *argv[] )
5 kx {
5 kx int use_stdin = 0, use_stdout = 0, use_tmpfile = 0;
5 kx
5 kx progname = rindex( argv[0], DIR_SEPARATOR ) + 1;
5 kx getargs( argc, argv );
5 kx
5 kx
5 kx if( ! strncmp( ifname, "-", 1 ) )
5 kx {
5 kx ifile = stdin;
5 kx use_stdin = 1;
5 kx }
5 kx else
5 kx {
5 kx ifile = fopen( ifname, "r" );
5 kx if( ifile == NULL )
5 kx {
5 kx fprintf( stderr, "ERROR: Can't open '%s' file\n", ifname );
5 kx exit( 1 );
5 kx }
5 kx }
5 kx
5 kx if( ! strncmp( ofname, "-", 1 ) )
5 kx {
5 kx ofile = stdout;
5 kx use_stdout = 1;
5 kx }
5 kx else
5 kx {
5 kx if( is_file_exist( ofname ) )
5 kx {
5 kx /*
5 kx use temporary file to presave file content
5 kx in case when input and output the same file.
5 kx */
5 kx ofile = tmpfile();
5 kx if( ofile == NULL )
5 kx {
5 kx fprintf( stderr, "ERROR: Can't open TEMPORARY file\n" );
5 kx exit( 1 );
5 kx }
5 kx use_tmpfile = 1;
5 kx }
5 kx else
5 kx {
5 kx ofile = fopen( ofname, "w+" );
5 kx if( ofile == NULL )
5 kx {
5 kx fprintf( stderr, "ERROR: Can't open '%s' file\n", ofname );
5 kx exit( 1 );
5 kx }
5 kx }
5 kx }
5 kx
5 kx
5 kx jsmin();
5 kx
5 kx
5 kx if( use_tmpfile )
5 kx {
5 kx tmp = fopen( ofname, "w+" );
5 kx if( tmp == NULL )
5 kx {
5 kx fprintf( stderr, "ERROR: Can't open '%s' file\n", ofname );
5 kx exit( 1 );
5 kx }
5 kx if( !fseek( ofile, 0, SEEK_SET ) )
5 kx {
5 kx int c;
5 kx while( (c = getc( ofile )) != EOF )
5 kx {
5 kx putc( c, tmp );
5 kx }
5 kx fclose( tmp );
5 kx }
5 kx else
5 kx {
5 kx fprintf( stderr, "ERROR: Can't seek to beginning of the '%s' file\n", ofname );
5 kx fclose( tmp );
5 kx exit( 1 );
5 kx }
5 kx }
5 kx
5 kx if( ! use_stdin ) { fclose( ifile ); ifile = NULL; }
5 kx if( ! use_stdout ) { fclose( ofile ); ofile = NULL; }
5 kx exit( 0 );
5 kx }