5 kx
5 kx /**********************************************************************
5 kx
5 kx Copyright 2019 Andrey V.Kosteltsev
5 kx
9 kx Licensed under the Radix cross Linux License, Version 1.0 .
5 kx you may not use this file except in compliance with the License.
5 kx You may obtain a copy of the License at
5 kx
9 kx https://radix-linux.su/licenses/LICENSE-1.0-en_US.txt
5 kx
5 kx Unless required by applicable law or agreed to in writing, software
5 kx distributed under the License is distributed on an "AS IS" BASIS,
5 kx WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
5 kx implied.
5 kx
5 kx **********************************************************************/
5 kx
5 kx #include <config.h>
5 kx
5 kx #include <stdlib.h>
5 kx #include <stdio.h>
5 kx #include <string.h>
5 kx #include <libgen.h> /* basename(3) */
5 kx
5 kx #include <msglog.h>
5 kx
5 kx #include <jsmin.h>
5 kx
5 kx static FILE *ifile;
5 kx static FILE *ofile;
5 kx
5 kx static const char *input_fname = NULL;
5 kx
5 kx static void error( const char *fname, char *s )
5 kx {
5 kx if( fname )
5 kx ERROR( "JSMIN: %s: %s", basename( (char *)fname ), s );
5 kx else
5 kx ERROR( "JSMIN: %s", s );
5 kx }
5 kx
5 kx static int 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 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 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 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( input_fname, "Unterminated comment" );
5 kx return EOF;
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 action( int d )
5 kx {
5 kx switch( d )
5 kx {
5 kx case 1:
5 kx /* skip first carriage return */
5 kx if( a != '\n' ) 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( input_fname, "Unterminated string literal" );
5 kx return;
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( input_fname, "Unterminated set in Regular Expression literal" );
5 kx return;
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( input_fname, "Unterminated set in Regular Expression literal" );
5 kx return;
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( input_fname, "Unterminated Regular Expression literal" );
5 kx return;
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 static void 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
5 kx int minimize_json( const char *ifname, const char *ofname )
5 kx {
5 kx int status, ret = -1;
5 kx
5 kx if( !ifname || !ofname ) return ret;
5 kx
5 kx status = exit_status; exit_status = 0;
5 kx
5 kx input_fname = ifname;
5 kx
5 kx ret = 0;
5 kx
5 kx ifile = fopen( ifname, "r" );
5 kx if( ifile == NULL )
5 kx {
5 kx ERROR( "JSMIN: Can't open '%s' file", ifname );
5 kx exit_status = status + exit_status;
5 kx return ret;
5 kx }
5 kx
5 kx ofile = fopen( ofname, "w+" );
5 kx if( ofile == NULL )
5 kx {
5 kx ERROR( "JSMIN: Can't open '%s' file", ofname );
5 kx exit_status = status + exit_status;
5 kx return ret;
5 kx }
5 kx
5 kx jsmin();
5 kx
5 kx fclose( ifile ); ifile = NULL;
5 kx fflush( ofile ); fclose( ofile ); ofile = NULL;
5 kx
5 kx if( exit_status == 0 )
5 kx {
5 kx ret = 1;
5 kx exit_status = status;
5 kx }
5 kx else
5 kx {
5 kx exit_status = status + exit_status;
5 kx }
5 kx
5 kx return ret;
5 kx }