From 2872df66d70556b9f655c4a33ee3538674f554df Mon Sep 17 00:00:00 2001
From: axel <axel@liljencrantz.se>
Date: Sun, 22 Apr 2007 20:03:12 +1000
Subject: [PATCH] Add source code prettyfier program called fish_indent

darcs-hash:20070422100312-ac50b-a073999b7dc172259c8200f3e740c921647222a0.gz
---
 Makefile.in             |  18 ++-
 configure.ac            |  42 +++--
 doc_src/fish_indent.txt |  17 +++
 fish.spec.in            |  27 ++--
 fish_indent.c           | 328 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 400 insertions(+), 32 deletions(-)
 create mode 100644 doc_src/fish_indent.txt
 create mode 100644 fish_indent.c

diff --git a/Makefile.in b/Makefile.in
index d25f4ce3e..5f5a6d4bc 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -64,6 +64,7 @@ CFLAGS = @CFLAGS@ $(MACROS)
 CPPFLAGS = @CPPFLAGS@ 
 LDFLAGS = @LIBS@ @LDFLAGS@ 
 LDFLAGS_FISH = ${LDFLAGS} @LIBS_FISH@ @LDFLAGS_FISH@
+LDFLAGS_FISH_INDENT = ${LDFLAGS} @LIBS_FISH_INDENT@
 LDFLAGS_FISH_PAGER = ${LDFLAGS} @LIBS_FISH_PAGER@
 LDFLAGS_FISHD = ${LDFLAGS} @LIBS_FISHD@
 LDFLAGS_MIMEDB = ${LDFLAGS} @LIBS_MIMEDB@
@@ -95,6 +96,8 @@ FISH_OBJS := function.o builtin.o complete.o env.o exec.o expand.o		\
 	signal.o io.o parse_util.o common.o screen.o path.o					\
 	parser_keywords.o
 
+FISH_INDENT_OBJS := fish_indent.o print_help.o common.o	\
+parser_keywords.o wutil.o tokenizer.o
 
 #
 # Additional files used by builtin.o
@@ -190,7 +193,7 @@ MAIN_DIR_FILES_UNSORTED := Doxyfile Doxyfile.user Doxyfile.help.in		\
     $(COMMON_FILES) $(COMMON_FILES:.c=.h) $(FISH_OBJS:.o=.c)			\
     fish.spec.in INSTALL README user_doc.head.html xsel-0.9.6.tar		\
     ChangeLog config.sub config.guess fish_tests.c main.c fish_pager.c	\
-    fishd.c seq.in make_vcs_completions.fish
+    fishd.c seq.in make_vcs_completions.fish $(FISH_INDENT_OBJS:.o=.c)
 
 #
 # The sorting is not meaningful in itself, but it has the side effect
@@ -241,7 +244,7 @@ FUNCTIONS_DIR_FILES := $(wildcard share/functions/*.fish)
 # Programs to install
 #
 
-SIMPLE_PROGRAMS := fish set_color mimedb count fish_pager fishd
+SIMPLE_PROGRAMS := fish set_color mimedb count fish_pager fishd fish_indent
 PROGRAMS := $(SIMPLE_PROGRAMS) @XSEL@ @SEQ_FALLBACK@ 
 
 
@@ -745,6 +748,14 @@ tokenizer_test: tokenizer.c tokenizer.h wutil.o common.o
 	$(CC) $(CFLAGS) tokenizer.c wutil.o common.o -D TOKENIZER_TEST $(LDFLAGS) -o $@
 
 
+#
+# Build the fish_indent program.
+#
+
+fish_indent: $(FISH_INDENT_OBJS)
+	$(CC) $(FISH_INDENT_OBJS) $(LDFLAGS_FISH_INDENT) -o $@
+
+
 #
 # Neat little program to show output from terminal
 #
@@ -869,8 +880,7 @@ distclean: clean
 clean:
 	rm -f *.o doc.h doc.tmp doc_src/*.doxygen doc_src/*.c doc_src/*.o doc_src/commands.hdr
 	rm -f tests/tmp.err tests/tmp.out tests/tmp.status tests/foo.txt
-	rm -f tokenizer_test fish key_reader set_color mimedb
-	rm -f fishd fish_pager count fish_tests 
+	rm -f $(PROGRAMS) fish_tests tokenizer_test key_reader 
 	rm -f fish-@PACKAGE_VERSION@.tar 
 	rm -f fish-@PACKAGE_VERSION@.tar.gz 
 	rm -f fish-@PACKAGE_VERSION@.tar.bz2
diff --git a/configure.ac b/configure.ac
index 0d044bc15..d410f2d5f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,6 +20,7 @@ AC_SUBST( docdir )
 AC_SUBST( HAVE_GETTEXT )
 AC_SUBST( LDFLAGS_FISH )
 AC_SUBST( LIBS_FISH )
+AC_SUBST( LIBS_FISH_INDENT )
 AC_SUBST( LIBS_FISH_PAGER )
 AC_SUBST( LIBS_FISHD )
 AC_SUBST( LIBS_MIMEDB )
@@ -481,6 +482,31 @@ AC_SEARCH_LIBS( iconv_open, iconv, , [AC_MSG_ERROR([Could not find an iconv impl
 LIBS_FISH=$LIBS
 LIBS=$LIBS_COMMON
 
+#
+# Check for libraries needed by fish_indent. 
+#
+
+LIBS_COMMON=$LIBS
+LIBS=""
+if test x$local_gettext != xno; then 
+	AC_SEARCH_LIBS( gettext, intl,,)
+fi
+LIBS_FISH_INDENT=$LIBS
+LIBS=$LIBS_COMMON
+
+#
+# Check for libraries needed by fish_pager. 
+#
+
+LIBS_COMMON=$LIBS
+LIBS=""
+if test x$local_gettext != xno; then 
+	AC_SEARCH_LIBS( gettext, intl,,)
+fi
+AC_SEARCH_LIBS( connect, socket, , [AC_MSG_ERROR([Cannot find the socket library, needed to build this package.] )] )
+AC_SEARCH_LIBS( setupterm, [ncurses curses], , [AC_MSG_ERROR([Could not find a curses implementation, needed to build fish])] )
+LIBS_FISH_PAGER=$LIBS
+LIBS=$LIBS_COMMON
 
 #
 # Check for libraries needed by fishd. 
@@ -495,7 +521,6 @@ AC_SEARCH_LIBS( connect, socket, , [AC_MSG_ERROR([Cannot find the socket library
 LIBS_FISHD=$LIBS
 LIBS=$LIBS_COMMON
 
-
 #
 # Check for libraries needed by mimedb. 
 #
@@ -509,21 +534,6 @@ LIBS_FISHD=$LIBS
 LIBS=$LIBS_COMMON
 
 
-#
-# Check for libraries needed by fish_pager. 
-#
-
-LIBS_COMMON=$LIBS
-LIBS=""
-if test x$local_gettext != xno; then 
-	AC_SEARCH_LIBS( gettext, intl,,)
-fi
-AC_SEARCH_LIBS( connect, socket, , [AC_MSG_ERROR([Cannot find the socket library, needed to build this package.] )] )
-AC_SEARCH_LIBS( setupterm, [ncurses curses], , [AC_MSG_ERROR([Could not find a curses implementation, needed to build fish])] )
-LIBS_FISH_PAGER=$LIBS
-LIBS=$LIBS_COMMON
-
-
 #
 # Check for libraries needed by set_color 
 #
diff --git a/doc_src/fish_indent.txt b/doc_src/fish_indent.txt
new file mode 100644
index 000000000..b05a40daf
--- /dev/null
+++ b/doc_src/fish_indent.txt
@@ -0,0 +1,17 @@
+\section fish_indent fish_indent - indenter and prettyfier
+
+\subsection fish_indent-synopsis Synopsis
+ <tt>fish_indent [options]</tt>
+
+\subsection fish_indent-description Description
+
+\c fish_indent is used to indent or otherwise prettyfy a piece of fish
+code. \c fish_indent reads commands from standard input and outputs
+them to standard output.
+
+\c fish_indent underatands the following options:
+
+- <tt>-h</tt> or <tt>--help</tt> displays this help message and then exits
+- <tt>-i</tt> or <tt>--no-indent</tt> do not indent commands
+- <tt>-v</tt> or <tt>--version</tt> displays the current fish version and then exits
+
diff --git a/fish.spec.in b/fish.spec.in
index 31be307dc..ee47dfcd8 100644
--- a/fish.spec.in
+++ b/fish.spec.in
@@ -115,22 +115,24 @@ fi
 %doc %_datadir/doc/%{name}-%{version}
 
 # man files
+%_mandir/man1/count.1*
 %_mandir/man1/fish.1*
-%_mandir/man1/xsel.1x*
+%_mandir/man1/fish_pager.1*
+%_mandir/man1/fish_indent.1*
+%_mandir/man1/fishd.1*
 %_mandir/man1/mimedb.1*
 %_mandir/man1/set_color.1*
-%_mandir/man1/count.1*
-%_mandir/man1/fishd.1*
-%_mandir/man1/fish_pager.1*
+%_mandir/man1/xsel.1x*
 
 # The program binaries
-%attr(0755,root,root) %_bindir/fish
-%attr(0755,root,root) %_bindir/fishd
-%attr(0755,root,root) %_bindir/fish_pager
-%attr(0755,root,root) %_bindir/xsel
-%attr(0755,root,root) %_bindir/set_color
-%attr(0755,root,root) %_bindir/mimedb
 %attr(0755,root,root) %_bindir/count
+%attr(0755,root,root) %_bindir/fish
+%attr(0755,root,root) %_bindir/fish_indent
+%attr(0755,root,root) %_bindir/fish_pager
+%attr(0755,root,root) %_bindir/fishd
+%attr(0755,root,root) %_bindir/mimedb
+%attr(0755,root,root) %_bindir/set_color
+%attr(0755,root,root) %_bindir/xsel
 
 # Configuration files
 %config %_sysconfdir/fish/config.fish
@@ -156,9 +158,10 @@ fi
 
 
 
-
-
 %changelog
+* Sat Apr 21 2007 Axel Liljencrantz<axel@liljencrantz.se> 1.23.0-0
+- Add fish_indent command
+
 * Thu Feb 8 2007 Axel Liljencrantz<axel@liljencrantz.se> 1.22.3-0
 - Tell rpm about the help pages in %_datadir/fish/man/
 
diff --git a/fish_indent.c b/fish_indent.c
new file mode 100644
index 000000000..edf8cd423
--- /dev/null
+++ b/fish_indent.c
@@ -0,0 +1,328 @@
+/*
+Copyright (C) 2005-2006 Axel Liljencrantz
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+
+/** \file main.c
+	The main loop of <tt>fish</tt>.
+*/
+
+#include "config.h"
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#include <locale.h>
+#include <signal.h>
+
+#include "fallback.h"
+#include "util.h"
+
+#include "common.h"
+#include "wutil.h"
+#include "halloc.h"
+#include "halloc_util.h"
+#include "tokenizer.h"
+#include "print_help.h"
+#include "parser_keywords.h"
+
+/**
+   The string describing the single-character options accepted by the main fish binary
+*/
+#define GETOPT_STRING "hvi"
+
+void read_file( FILE *f, string_buffer_t *b )
+{
+	while( 1 )
+	{
+		wint_t c = fgetwc( f );
+		if( c == WEOF )
+		{
+			break;
+		}
+		
+		sb_append_char( b, c );
+	}
+}
+
+static void insert_tabs( string_buffer_t *out, int indent )
+{
+	int i;
+	
+	for( i=0; i<indent; i++ )
+	{
+		sb_append( out, L"\t" );
+	}
+	
+}
+
+static int indent( string_buffer_t *out, wchar_t *in, int flags )
+{
+	tokenizer tok;
+	int res=0;
+	int is_command = 1;
+	int indent = 0;
+	int do_indent = 1;
+
+	tok_init( &tok, in, TOK_SHOW_COMMENTS );
+	
+	for( ; tok_has_next( &tok ); tok_next( &tok ) )
+	{
+		int type = tok_last_type( &tok );
+		wchar_t *last = tok_last( &tok );
+		
+		switch( type )
+		{
+			case TOK_STRING:
+			{
+				if( is_command )
+				{
+					int next_indent = indent;
+					is_command = 0;
+
+					wchar_t *unesc = unescape( last, UNESCAPE_SPECIAL );
+					
+					if( parser_keywords_is_block( unesc ) )
+					{
+						next_indent++;
+					}
+					else if( wcscmp( unesc, L"else" ) == 0 )
+					{
+						indent--;
+					}
+					else if( wcscmp( unesc, L"end" ) == 0 )
+					{
+						indent--;
+						next_indent--;
+					}
+					
+	
+					if( do_indent && flags)
+					{
+						insert_tabs( out, indent );
+					}
+					
+					sb_printf( out, L"%ls ", last );
+					
+					indent = next_indent;
+					
+				}
+				else
+				{
+					sb_printf( out, L"%ls ", last );
+				}
+				
+				break;
+			}
+			
+			case TOK_END:
+			{
+				sb_append( out, L"\n" );
+				do_indent = 1;
+				is_command = 1;
+				break;
+			}
+
+			case TOK_PIPE:
+			{
+				sb_append( out, L"| " );
+				is_command = 1;
+				break;
+			}
+			
+			case TOK_REDIRECT_OUT:
+			case TOK_REDIRECT_APPEND:
+			case TOK_REDIRECT_IN:
+			case TOK_REDIRECT_FD:
+			{
+				sb_append( out, last );
+				switch( type )
+				{
+					case TOK_REDIRECT_OUT:
+						sb_append( out, L"> " );
+						break;
+						
+					case TOK_REDIRECT_APPEND:
+						sb_append( out, L">> " );
+						break;
+						
+					case TOK_REDIRECT_IN:
+						sb_append( out, L"< " );
+						break;
+						
+					case TOK_REDIRECT_FD:
+						sb_append( out, L">& " );
+						break;
+															
+				}
+				break;
+			}
+			
+
+			case TOK_BACKGROUND:
+			{
+				sb_append( out, L"&\n" );
+				do_indent = 1;
+				is_command = 1;
+				break;
+			}
+			
+
+			case TOK_COMMENT:
+			{
+				sb_printf( out, L"%ls", last );
+				do_indent = 1;
+				break;				
+			}
+			
+			default:
+			{
+				debug( 0, L"Unknown wha? %ls", last );
+				exit(1);
+			}			
+		}
+				
+	}
+	
+	tok_destroy( &tok );
+
+	return res;
+}
+
+
+int main( int argc, char **argv )
+{
+	string_buffer_t sb_in;
+	string_buffer_t sb_out;
+	
+	int do_indent=1;
+	
+	wsetlocale( LC_ALL, L"" );
+	program_name=L"fish_indent";
+
+	while( 1 )
+	{
+		static struct option
+			long_options[] =
+			{
+				{
+					"no-indent", no_argument, 0, 'i' 
+				}
+				,
+				{
+					"help", no_argument, 0, 'h' 
+				}
+				,
+				{
+					"version", no_argument, 0, 'v' 
+				}
+				,
+				{ 
+					0, 0, 0, 0 
+				}
+			}
+		;
+		
+		int opt_index = 0;
+		
+		int opt = getopt_long( argc,
+							   argv, 
+							   GETOPT_STRING,
+							   long_options, 
+							   &opt_index );
+		
+		if( opt == -1 )
+			break;
+		
+		switch( opt )
+		{
+			case 0:
+			{
+				break;
+			}
+			
+			case 'h':
+			{
+				print_help( "fish_indent", 1 );
+				exit( 0 );				
+				break;
+			}
+			
+			case 'v':
+			{
+				fwprintf( stderr, 
+						  _(L"%ls, version %s\n"), 
+						  program_name,
+						  PACKAGE_VERSION );
+				exit( 0 );				
+			}
+
+			case 'i':
+			{
+				do_indent = 0;
+				break;
+			}
+			
+			
+			case '?':
+			{
+				exit( 1 );
+			}
+			
+		}		
+	}
+
+	halloc_util_init();	
+
+	sb_init( &sb_in );
+	sb_init( &sb_out );
+
+	read_file( stdin, &sb_in );
+	
+	wutil_init();
+
+	if( !indent( &sb_out, (wchar_t *)sb_in.buff, do_indent ) )
+	{
+	
+		fwprintf( stdout, L"%ls", sb_out.buff );
+	}
+	else
+	{
+		/*
+		  Indenting failed - print original input
+		*/
+		fwprintf( stdout, L"%ls", sb_in.buff );
+	}
+	
+
+	wutil_destroy();
+
+	halloc_util_destroy();
+
+	return 0;
+}