JavaScript, Json minimizator

jsmin – is a JavaScript, Json minimizator which removes comments and unnecessary whitespace from JS, JSON files

6 Commits   0 Branches   1 Tag
     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 }