summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkx <kx@radix.pro>2023-04-07 06:34:02 +0300
committerkx <kx@radix.pro>2023-04-07 06:34:02 +0300
commitf84701650cb9f48ae55290e4091d70b334b907d5 (patch)
treec9279e9123858cbd82a11f8e1a27691d8b554c81
parent890f8e901f0ea339efae789a00ac4fc561303e29 (diff)
downloadjsmin-trunk.tar.xz
Version 0.0.1HEADtrunk
-rw-r--r--.gitignore25
-rw-r--r--AUTHORS3
-rw-r--r--ChangeLog4
-rw-r--r--Makefile.am11
-rw-r--r--NEWS3
-rw-r--r--README3
-rw-r--r--acsite.m456
-rwxr-xr-xauto-clean40
-rwxr-xr-xbootstrap6
-rw-r--r--configure.ac165
-rw-r--r--doc/README47
-rw-r--r--doc/cross-compilation.txt13
-rw-r--r--perl/Makefile.am7
-rwxr-xr-xperl/jsmin.pl.in389
-rw-r--r--src/Makefile.am6
-rw-r--r--src/main.c533
16 files changed, 1311 insertions, 0 deletions
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 <kx@radix.pro>
+
+ * 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(<text>)
+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 <<EOF;
+
+Usage: $p [options] [input_file_name]
+Options:
+ --output | -o - output file name;
+ --version | -v - print version numver;
+ --help | -h | -? - print this message;
+ -- - an option terminator;
+ - - use std{io|out} instead of files.
+
+Examples:
+
+ Input file is 'full.json', output file is 'min.json':
+
+ \$ $p -o min.json full.json
+
+ Input file is 'STDIN', output file is 'min.json':
+
+ \$ $p -o min.json -
+
+ Please note that to terminate your input by keyboard you have to use
+ <Ctrl>+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 <<EOF;
+$version
+EOF
+ exit;
+}
+
+
+my $a = '';
+my $b = '';
+my $x = '';
+my $y = '';
+
+my $lookahead = 0;
+
+
+sub get
+{
+ my $c = 0;
+
+ $c = $lookahead;
+ $lookahead = 0;
+
+ if( $c == 0 )
+ {
+ if( ! eof( $if ) )
+ {
+ my $ch = getc( $if );
+ $c = ord( $ch );
+ }
+ else
+ {
+ $c = 0;
+ }
+ }
+ if( $c >= 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 <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 );
+}