fish-shell/fish_indent.cpp

383 lines
6.5 KiB
C++

/*
Copyright (C) 2005-2008 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 fish_indent.cpp
The fish_indent proegram.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <locale.h>
#include "fallback.h"
#include "util.h"
#include "common.h"
#include "wutil.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"
/**
Read the entire contents of a file into the specified string
*/
static void read_file( FILE *f, wcstring &b )
{
while( 1 )
{
errno=0;
wint_t c = fgetwc( f );
if( c == WEOF )
{
if( errno )
{
wperror(L"fgetwc");
exit(1);
}
break;
}
b.push_back((wchar_t)c);
}
}
/**
Insert the specified number of tabs into the output buffer
*/
static void insert_tabs( wcstring &out, int indent )
{
out.append(indent, L'\t');
}
/**
Indent the specified input
*/
static int indent( wcstring &out, const wcstring &in, int flags )
{
tokenizer tok;
int res=0;
int is_command = 1;
int indent = 0;
int do_indent = 1;
int prev_type = 0;
int prev_prev_type = 0;
tok_init( &tok, in.c_str(), 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;
wcstring unesc = last;
unescape_string(unesc, UNESCAPE_SPECIAL);
if( parser_keywords_is_block(unesc))
{
next_indent++;
}
else if (unesc == L"else")
{
indent--;
}
/* case should have the same indent level as switch*/
else if (unesc == L"case")
{
indent--;
}
else if (unesc == L"end")
{
indent--;
next_indent--;
}
if( do_indent && flags && prev_type != TOK_PIPE )
{
insert_tabs( out, indent );
}
append_format(out, L"%ls", last );
indent = next_indent;
}
else
{
if ( prev_type != TOK_REDIRECT_FD )
out.append( L" " );
out.append( last );
}
break;
}
case TOK_END:
{
if( prev_type != TOK_END || prev_prev_type != TOK_END )
out.append( L"\n" );
do_indent = 1;
is_command = 1;
break;
}
case TOK_PIPE:
{
out.append( L" " );
if ( last[0] == '2' && !last[1] ) {
out.append( L"^" );
} else if ( last[0] != '1' || last[1] ) {
out.append( last);
out.append( L">" );
}
out.append( L" | " );
is_command = 1;
break;
}
case TOK_REDIRECT_OUT:
{
out.append( L" " );
if ( wcscmp( last, L"2" ) == 0 ) {
out.append( L"^" );
} else {
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L"> " );
}
break;
}
case TOK_REDIRECT_APPEND:
{
out.append( L" " );
if ( wcscmp( last, L"2" ) == 0 ) {
out.append( L"^^" );
} else {
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L">> " );
}
break;
}
case TOK_REDIRECT_IN:
{
out.append( L" " );
if ( wcscmp( last, L"0" ) != 0 )
out.append( last );
out.append( L"< " );
break;
}
case TOK_REDIRECT_FD:
{
out.append( L" " );
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L">& " );
break;
}
case TOK_BACKGROUND:
{
out.append( L"&\n" );
do_indent = 1;
is_command = 1;
break;
}
case TOK_COMMENT:
{
if( do_indent && flags)
{
insert_tabs( out, indent );
}
append_format( out, L"%ls", last );
do_indent = 1;
break;
}
default:
{
debug( 0, L"Unknown token '%ls'", last );
exit(1);
}
}
prev_prev_type = prev_type;
prev_type = type;
}
tok_destroy( &tok );
return res;
}
/**
Remove any prefix and suffix newlines from the specified
string.
*/
static void trim( wcstring &str )
{
if (str.empty())
return;
size_t pos = str.find_first_not_of(L" \n");
if (pos > 0)
str.erase(0, pos);
pos = str.find_last_not_of(L" \n");
if (pos != wcstring::npos && pos + 1 < str.length())
str.erase(pos + 1);
}
/**
The main mathod. Run the program.
*/
int main( int argc, char **argv )
{
int do_indent=1;
set_main_thread();
setup_fork_guards();
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 );
}
}
}
wcstring sb_in, sb_out;
read_file( stdin, sb_in );
wutil_init();
if( !indent( sb_out, sb_in, do_indent ) )
{
trim(sb_out);
fwprintf( stdout, L"%ls", sb_out.c_str() );
}
else
{
/*
Indenting failed - print original input
*/
fwprintf( stdout, L"%ls", sb_in.c_str() );
}
wutil_destroy();
return 0;
}