mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +00:00
391 lines
8.8 KiB
C++
391 lines
8.8 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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"
|
|
#include "fish_version.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)
|
|
{
|
|
wint_t c = fgetwc(f);
|
|
if (c == WEOF)
|
|
{
|
|
if (ferror(f))
|
|
{
|
|
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)
|
|
{
|
|
if (indent > 0)
|
|
out.append((size_t)indent, L'\t');
|
|
|
|
}
|
|
|
|
/**
|
|
Indent the specified input
|
|
*/
|
|
static int indent(wcstring &out, const wcstring &in, int flags)
|
|
{
|
|
int res=0;
|
|
int is_command = 1;
|
|
int indent = 0;
|
|
int do_indent = 1;
|
|
int prev_type = 0;
|
|
int prev_prev_type = 0;
|
|
|
|
tokenizer_t tok(in.c_str(), TOK_SHOW_COMMENTS | TOK_SHOW_BLANK_LINES);
|
|
for (; tok_has_next(&tok); tok_next(&tok))
|
|
{
|
|
int type = tok_last_type(&tok);
|
|
const wchar_t *last = tok_last(&tok);
|
|
|
|
switch (type)
|
|
{
|
|
case TOK_STRING:
|
|
{
|
|
if (is_command)
|
|
{
|
|
int next_indent = indent;
|
|
is_command = 0;
|
|
|
|
wcstring unesc;
|
|
unescape_string(last, &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;
|
|
|
|
}
|
|
|
|
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);
|
|
assert(0 && "Unreachable code reached");
|
|
break;
|
|
}
|
|
|
|
case 'v':
|
|
{
|
|
fwprintf(stderr,
|
|
_(L"%ls, version %s\n"),
|
|
program_name,
|
|
get_fish_version());
|
|
exit(0);
|
|
assert(0 && "Unreachable code reached");
|
|
break;
|
|
}
|
|
|
|
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;
|
|
}
|