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