From f84701650cb9f48ae55290e4091d70b334b907d5 Mon Sep 17 00:00:00 2001 From: kx Date: Fri, 7 Apr 2023 06:34:02 +0300 Subject: Version 0.0.1 --- .gitignore | 25 +++ AUTHORS | 3 + ChangeLog | 4 + Makefile.am | 11 + NEWS | 3 + README | 3 + acsite.m4 | 56 +++++ auto-clean | 40 ++++ bootstrap | 6 + configure.ac | 165 ++++++++++++++ doc/README | 47 ++++ doc/cross-compilation.txt | 13 ++ perl/Makefile.am | 7 + perl/jsmin.pl.in | 389 +++++++++++++++++++++++++++++++++ src/Makefile.am | 6 + src/main.c | 533 ++++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 1311 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 acsite.m4 create mode 100755 auto-clean create mode 100755 bootstrap create mode 100644 configure.ac create mode 100644 doc/README create mode 100644 doc/cross-compilation.txt create mode 100644 perl/Makefile.am create mode 100755 perl/jsmin.pl.in create mode 100644 src/Makefile.am create mode 100644 src/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1fe6a6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ + +autom4te.cache/ + +Makefile +Makefile.in +config.h +config.h.in +config.log +config.status +compile +config.guess +config.sub +configure +install-sh +missing +stamp-h1 +aclocal.m4 +depcomp + +perl/Makefile.in + +src/.deps/ +src/Makefile.in + +*~ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..3f3e5e0 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ + +Douglas Crockford (www.crockford.com) +Andrey V.Kosteltsev (radix.pro) diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..5dac465 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4 @@ +2015-09-24 Andrey V.Kosteltsev + + * all-files: The first release of jsmin with using getopt functionality. + Perl version also present. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..7844b8d --- /dev/null +++ b/Makefile.am @@ -0,0 +1,11 @@ + + +# +# In compilation order. +# ==================== +# +SUBDIRS = perl src + + +EXTRA_DIST = doc auto-clean .gitignore bootstrap + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..f8750b1 --- /dev/null +++ b/NEWS @@ -0,0 +1,3 @@ + +24 Sep 2015 - first release with getopt functionality. + Perl version also present. diff --git a/README b/README new file mode 100644 index 0000000..0ad8ed8 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ + +jsmin - is a JavaScript, Json minimizator which removes +comments and unnecessary whitespace from JS, JSON files. diff --git a/acsite.m4 b/acsite.m4 new file mode 100644 index 0000000..9840042 --- /dev/null +++ b/acsite.m4 @@ -0,0 +1,56 @@ + +dnl ============================================================ +dnl Display Configuration Headers +dnl +dnl configure.in: +dnl AC_MSG_CFG_PART() +dnl ============================================================ + +define(AC_MSG_CFG_PART,[dnl + AC_MSG_RESULT() + AC_MSG_RESULT(${TB}$1:${TN}) +])dnl + + +AC_DEFUN(AC_JSMIN_HEADLINE,[dnl + # Use a `Quadrigaph'. @<:@ gives you [ and @:>@ gives you ] : + TB=`echo -n -e '\033@<:@1m'` + TN=`echo -n -e '\033@<:@0m'` + echo "" + echo "Configuring ${TB}$1${TN} ($2), Version ${TB}${PACKAGE_VERSION}${TN}" + echo "$3" +])dnl + + +dnl ============================================================ +dnl Test for build_host `ln -s' . +dnl ============================ +dnl +dnl Usage: +dnl ----- +dnl AC_PATH_PROG_LN_S +dnl AC_SUBST(LN) +dnl AC_SUBST(LN_S) +dnl +dnl ============================================================ +AC_DEFUN(AC_PATH_PROG_LN_S, +[AC_PATH_PROG(LN, ln, no, /usr/local/bin:/usr/bin:/bin:$PATH) +AC_MSG_CHECKING(whether ln -s works on build host) +AC_CACHE_VAL(ac_cv_path_prog_LN_S, +[rm -f conftestdata +if $LN -s X conftestdata 2>/dev/null +then + rm -f conftestdata + ac_cv_path_prog_LN_S="$LN -s" +else + ac_cv_path_prog_LN_S="$LN" +fi])dnl +LN_S="$ac_cv_path_prog_LN_S" +if test "$ac_cv_path_prog_LN_S" = "$LN -s"; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_SUBST(LN)dnl +AC_SUBST(LN_S)dnl +]) diff --git a/auto-clean b/auto-clean new file mode 100755 index 0000000..84d1b59 --- /dev/null +++ b/auto-clean @@ -0,0 +1,40 @@ +#!/bin/bash + +CWD=`pwd` + +program=`basename $0` + +usage() { + cat << EOF + +Usage: $program [options] + +Options: + -h,--help Display this message. + +EOF +} + +if [ -f "${CWD}/Makefile" ] ; then + make distclean +fi + +gitignore='.gitignore' + +while read ln; do + line=`echo "${ln}" | sed 's,^[ \t],,' | sed 's,[ \t]$,,'` + if [ "x$line" != "x" -a "${line:0:1}" != "#" ] ; then + if `echo "${line}" | grep -q '\*~$'` ; then + find "`dirname "${line}"`" -type f -iname '*~' -print0 | while IFS= read -r -d '' file ; do + rm -f "$file" + done + elif `echo "${line}" | grep -q '\*'` ; then + find "`dirname "${line}"`" -type f -iname "`basename "${line}"`" -print0 | while IFS= read -r -d '' file ; do + rm -f "$file" + done + else + if [ -d "${line}" ] ; then rm -rf "${line}" ; fi + if [ -f "${line}" ] ; then rm -f "${line}" ; fi + fi + fi +done < ${CWD}/${gitignore} diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..22100eb --- /dev/null +++ b/bootstrap @@ -0,0 +1,6 @@ +#!/bin/sh + +aclocal +autoheader +automake --gnu --add-missing --copy +autoconf diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..6bdc349 --- /dev/null +++ b/configure.ac @@ -0,0 +1,165 @@ + +dnl ============================================================ +dnl Process this file with autoconf to produce +dnl a configure script. +dnl ============================================================ + +AC_PREREQ(2.71)dnl dnl Minimum Autoconf version required. + +AC_INIT([jsmin],[0.0.1], + [support@radix.pro],[jsmin],[http://radix.pro]) + +AC_JSMIN_HEADLINE([jsmin], [JSmin], [Copyright (c) 2015-2023 Andrey V.Kosteltsev]) + + +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl $$ $$ +dnl $$ PART: Init Automake environment $$ +dnl $$ $$ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +AC_MSG_CFG_PART(Init Automake environment) + +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects foreign no-dist-gzip dist-xz tar-pax]) + +AC_CONFIG_HEADERS([config.h]) + +AC_PREFIX_DEFAULT(/usr/local) + + +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl $$ $$ +dnl $$ PART: Test for Build Tools $$ +dnl $$ $$ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +AC_MSG_CFG_PART(Test for Build Tools) +AC_CHECK_TOOL([GCC], [gcc], [:]) + + +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl $$ $$ +dnl $$ PART: Test for Auxiliary (my be version sensitive) $$ +dnl $$ programs $$ +dnl $$ $$ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +AC_MSG_CFG_PART(Test for Auxiliary (my be version sensitive) programs) + +dnl ============================================================ +dnl Locate tools( on build machine! ) . +dnl ================= +dnl ============================================================ +AC_PATH_PROG_LN_S +AC_SUBST(LN) +AC_SUBST(LN_S) + +dnl Нам нужен исполняемый файл pwd. Встроенный "pwd -P" нас +dnl не устраивает( из соображений переносимости ) . +AC_PATH_PROG(PWD_P, pwd, no) +if test "$PWD_P" = no; then + AC_MSG_ERROR(******** A pwd binary could not be found.) +fi + +test -n "$aux_missing" && AC_MSG_WARN([ +******** These auxiliary programs are missing or too old: $aux_missing +******** some features will be disabled. +******** Check the INSTALL file for required versions.]) + + +dnl +dnl Remove TAR option '-o' for allow long file names in DIST archive. +dnl Standard tar.m4 from aclocal-1.9 package probvides following +dnl 'am__tar' command: +dnl ${AMTAR} chof - "$$tardir" +dnl and uses '-chof' options for backward compatibility. +dnl +am__tar='${AMTAR} chf - "$$tardir"' +AC_SUBST(am__tar) + +AC_PATH_PROGS(TAR, tar gtar, no, /usr/local/bin:/usr/bin:/bin:$PATH) + + + +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl $$ $$ +dnl $$ PART: Build Parameters $$ +dnl $$ $$ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +AC_MSG_CFG_PART(Build Parameters) + +dnl +dnl Check for system header files. +dnl ============================= +dnl /* GetText это проверил. А мы делаем для себя. */ +AC_HEADER_STAT +AC_CHECK_HEADERS(fcntl.h unistd.h stdlib.h stdio.h errno.h string.h getopt.h) + + +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl $$ $$ +dnl $$ PART: OUTPUT Substitution $$ +dnl $$ $$ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +dnl ============================================================ +AC_MSG_CFG_PART(OUTPUT) + + +AC_CONFIG_FILES([ +Makefile +perl/Makefile +perl/jsmin.pl +src/Makefile +]) +AC_OUTPUT + +chmod 0755 perl/jsmin.pl + +if test -f "Makefile"; then + echo "" + echo "Now please type:" + echo " \`${TB}make${TN}' to compile," + echo " \`${TB}make install${TN}' to make and install ${TB}jsmin${TN}," + echo " \`${TB}make dist${TN}' to create distributable tarball, or" + echo " \`${TB}make distclean${TN}' to clean befor configure for another target." + echo "Enjoy." + echo "" +fi diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..4d34754 --- /dev/null +++ b/doc/README @@ -0,0 +1,47 @@ + +/* + This file contant RUSSIAN letters( code-page: utf-8 ) + ***************************************************************/ + +/* begin * + + Создание дистрибутива: + ===================== + + 1. Создание (в корне) файлов: + ../AUTHORS + ../ChangeLog + ../NEWS + ../README + это произвольным образом, на данном этапе + необходимо лишь их наличие (для automake). + + 2. Создание (в корне) файлов: ++ ../Makefile.am ++ ../acsite.m4 ++ ../configure.ac ++ ../perl/Makefile.am ++ ../src/Makefile.am + это фактически и есть разработка средств + компиляции, инсталляции и, в конечном итоге, + создание дистрибутива. + + 3. Выполнил script bootstrap: + $ cd .. + $ ./bootstrap + + + Изменение версии дистрибутива: + ============================= + + 1. В файле configure.ac меняем определение + AC_INIT([jsmin],[0.0.1], + [support@radix.pro],[jsmin],[http://radix.pro]) + на + AC_INIT([jsmin],[0.0.2], + [support@radix.pro],[jsmin],[http://radix.pro]) + . + + Затем выполняем скрипт bootstrap . + + * end */ diff --git a/doc/cross-compilation.txt b/doc/cross-compilation.txt new file mode 100644 index 0000000..4bebf9a --- /dev/null +++ b/doc/cross-compilation.txt @@ -0,0 +1,13 @@ + +This is an example: + + $ tar xJvf jsmin-0.0.1.tar.xz + $ cd jsmin-0.0.1 + + $ CC=/opt/toolchain/mipsel-BCM74X-linux-eglibc/1.0.8/bin/mipsel-bcm74x-linux-gnu-gcc \ + ./configure --prefix=/usr \ + --target=mipsel-bcm74x-linux-gnu \ + --host=mipsel-bcm74x-linux-gnu + + $ make + $ make install [DESTDIR=dir] diff --git a/perl/Makefile.am b/perl/Makefile.am new file mode 100644 index 0000000..7cd8778 --- /dev/null +++ b/perl/Makefile.am @@ -0,0 +1,7 @@ + +############################################################ +# Don't strip jsmin.pl script when `make install-strip' # +############################################################ +INSTALL_SCRIPT = ${INSTALL} + +bin_SCRIPTS = jsmin.pl diff --git a/perl/jsmin.pl.in b/perl/jsmin.pl.in new file mode 100755 index 0000000..ff648b2 --- /dev/null +++ b/perl/jsmin.pl.in @@ -0,0 +1,389 @@ +#!/usr/bin/perl -w + +use strict; +use Getopt::Long qw(:config no_ignore_case bundling); +use File::Basename; +use utf8; + +my $ifname = ''; +my $ofname = ''; +my $stdio = ''; +my $last = ''; + +my $version = "@VERSION@"; + +my ( $if, $of ); + +sub usage +{ + my $p = basename( $0 ); + + print <+d combination; + + Input file is 'full.json', output file is 'STDOUT': + + \$ $p -- full.json + + Use stdin, stdout: + + \$ $p - < full.json > min.json + +Enjoj. + +EOF + exit; +} + +sub version +{ + print <= ord(" ") || $c == ord("\n") || $c == 0 ) + { + return $c; + } + if( $c == ord("\r") ) + { + return ord("\n"); + } + return ord(" "); +} + +sub peek +{ + $lookahead = get(); + return $lookahead; +} + +sub next_cn +{ + my $c = get(); + + if( $c == ord("/") ) + { + my $char = chr( peek() ); + if( $char eq "/" ) + { + for(;;) { $c = get(); if( $c <= ord("\n") ) { last; } } + } + elsif( $char eq "*" ) + { + get(); + while( $c != ord(" ") ) + { + my $cn = get(); + if( chr( $cn ) eq "*" ) + { + if( chr( peek() ) eq "/" ) { get(); $c = ord(" "); } + } + elsif( $cn == 0 ) + { + print STDERR "Unterminated comment.\n"; exit 1; + } + } + } + } + $y = $x; + $x = chr( $c ); + + return $c; +} + +sub action +{ + my $d = $_[0]; + + if( $d == 1 ) + { + print $of $a; + if( + ( $y eq "\n" || $y eq " " ) && + ( $a eq "+" || $a eq "-" || $a eq "*" || $a eq "/" ) && + ( $b eq "+" || $b eq "-" || $b eq "*" || $b eq "/" ) + ) + { + print $of $y; + } + $d = 2; + } + + if( $d == 2 ) + { + $a = $b; + if( $a eq "\'" || $a eq "\"" || $a eq "`" ) + { + for(;;) + { + my $c; + + print $of $a; + $c = get(); $a = chr( $c ); + if( $a eq $b ) { last; } + if( $a eq "\\" ) + { + print $of $a; + $c = get(); $a = chr( $c ); + } + if( $c == 0 ) + { + print STDERR "Unterminated string literal.\n"; exit 1; + } + } + } + $d = 3; + } + + if( $d == 3 ) + { + $b = chr( next_cn() ); + if( $b eq "/" && + ( $a eq "(" || $a eq "," || $a eq "=" || $a eq ":" || + $a eq "[" || $a eq "!" || $a eq "&" || $a eq "|" || + $a eq "?" || $a eq "+" || $a eq "-" || $a eq "~" || + $a eq "*" || $a eq "/" || $a eq "{" || $a eq "\n" + ) + ) + { + print $of $a; + if( $a eq "/" || $a eq "*" ) { print $of " "; } + print $of $b; + for(;;) + { + my $c; + + $c = get(); $a = chr( $c ); + if( $a eq "[" ) + { + for(;;) + { + print $of $a; + $c = get(); $a = chr( $c ); + if( $a eq "]" ) + { + last; + } + if( $a eq "\\" ) + { + print $of $a; + $c = get(); $a = chr( $c ); + } + if( $c == 0 ) + { + print STDERR "Unterminated regular expression literal.\n"; exit 1; + } + } + } + elsif( $a eq "/" ) + { + my $ch = chr( peek() ); + if( $ch eq "/" || $ch eq "*" ) + { + print STDERR "Unterminated regular expression literal.\n"; exit 1; + } + last; + } + elsif( $a eq "\\" ) + { + print $of $a; + $c = get(); $a = chr( $c ); + } + elsif( $c == 0 ) + { + print STDERR "Unterminated regular expression literal.\n"; exit 1; + } + print $of $a; + } + $b = chr( next_cn() ); + } + } +} + + +sub isalnum +{ + my $ch = $_[0]; + + if( ($ch ge "a" && $ch le "z") || ($ch ge "0" && $ch le "9") || + ($ch ge "A" && $ch le "Z") || $ch eq "_" || $ch eq "\$" || + $ch eq "\\" || $ch gt "~" + ) { return 1; } else { return 0; } +} + + +sub jsmin +{ + if( peek() == 0xEF ) { get(); get(); get(); } + $a = "\n"; + action( 3 ); + while( $a ne "" && $a ne "\0" ) + { + if( $a eq " " ) + { + action( isalnum( $b ) ? 1 : 2 ); + } + elsif( $a eq "\n" ) + { + if( $b eq "{" || $b eq "[" || $b eq "(" || $b eq "+" || + $b eq "-" || $b eq "!" || $b eq "~" + ) + { + action( 1 ); + } + elsif( $b eq " " ) + { + action( 3 ); + } + else + { + action( isalnum( $b ) ? 1 : 2 ); + } + } + else + { + if( $b eq " " ) + { + action( isalnum( $a ) ? 1 : 3 ); + } + elsif( $b eq "\n" ) + { + if( $a eq "}" || $a eq "]" || $a eq ")" || $a eq "+" || + $a eq "-" || $a eq "\"" || $a eq "\'" || $a eq "`" + ) + { + action( 1 ); + } + else + { + action( isalnum( $a ) ? 1 : 3 ); + } + } + else + { + action( 1 ); + } + } + } + #lats carriage return + print $of "\n"; +} + + +local $SIG{__WARN__} = sub { + my $message = shift; + print STDERR "ERROR: " . $message; + usage(); +}; + +if( ! GetOptions( 'o=s' => \$ofname, + 'output=s' => \$ofname, + '' => \$stdio, + 'help|h|?' => sub { usage() }, + 'version|v' => sub { version() } + ) + ) +{ + usage(); +} + +foreach( @ARGV ) +{ + $last = $_; + + if( $#ARGV ) + { + usage(); + } + +# NOTE: The '--' is an option terminator! +# So ./script -- - returns last equal to '-'. +# +# The $last argument is a ifname by default; +# +} + + +if( $ifname eq '' && $ofname eq '' && ! $stdio ) +{ + usage(); +} + +if( $ofname ne '' && $stdio && $last ne '' ) +{ + # like that: + # ./script -o min.json full.json - + # ./script -o min.json - full.json + # + print "ERROR: Input file defined twice: as 'stdin' and as a orfinary file '$last'\n"; + usage(); +} +else +{ + $ifname = $last; $last = ''; +} + +if( $ofname eq '' ) { $ofname = '-'; } +if( $ifname eq '' ) { $ifname = '-'; } +if( $ifname eq '-' ) { $if = *STDIN; } else { open( $if, "< $ifname" ); } +if( $ofname eq '-' ) { $of = *STDOUT; } else { open( $of, "> $ofname" ); } + + +jsmin(); + + +close $if; +close $of; + +exit 0; diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..f08fea3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,6 @@ + +bin_PROGRAMS = jsmin + +jsmin_SOURCES = main.c + +jsmin_CFLAGS = -Wall -funsigned-char -I$(top_srcdir) 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 + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif + +#else +/************************************************************** + For building outside from source package as a single C file: + + $ gcc -o jsmin main.c + */ + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#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( " +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 ); +} -- cgit v1.2.3