fish-shell/output.cpp

653 lines
13 KiB
C++
Raw Normal View History

/** \file output.c
Generic output functions
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_TERMIOS_H
#include <sys/termios.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <sys/time.h>
#include <unistd.h>
#include <wctype.h>
#if HAVE_NCURSES_H
#include <ncurses.h>
#else
#include <curses.h>
#endif
#if HAVE_TERMIO_H
#include <termio.h>
#endif
#if HAVE_TERM_H
#include <term.h>
#elif HAVE_NCURSES_TERM_H
#include <ncurses/term.h>
#endif
#include <signal.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>
#include <wchar.h>
#include "fallback.h"
#include "util.h"
#include "wutil.h"
#include "expand.h"
#include "common.h"
#include "output.h"
#include "highlight.h"
#include "env.h"
/**
Number of color names in the col array
*/
#define COLORS (sizeof(col)/sizeof(wchar_t *))
static int writeb_internal( char c );
/**
Names of different colors.
*/
static const wchar_t *col[]=
{
L"black",
L"red",
L"green",
L"brown",
L"yellow",
L"blue",
L"magenta",
L"purple",
L"cyan",
L"white"
L"normal"
}
;
/**
Mapping from color name (the 'col' array) to color index as used in
ANSI color terminals, and also the fish_color_* constants defined
in highlight.h. Non-ANSI terminals will display the wrong colors,
since they use a different mapping.
*/
static const int col_idx[]=
{
0,
1,
2,
3,
3,
4,
5,
5,
6,
7,
FISH_COLOR_NORMAL,
2012-02-08 14:44:10 +08:00
};
/**
The function used for output
*/
static int (*out)(char c) = &writeb_internal;
/**
Name of terminal
*/
static wcstring current_term;
2012-03-06 02:44:08 +08:00
/* Whether term256 is supported */
static bool support_term256 = false;
void output_set_writer( int (*writer)(char) )
{
CHECK( writer, );
out = writer;
}
int (*output_get_writer())(char)
{
return out;
}
static bool term256_support_is_native(void) {
/* Return YES if we think the term256 support is "native" as opposed to forced. */
return max_colors == 256;
}
2012-03-06 02:44:08 +08:00
bool output_get_supports_term256() {
return support_term256;
}
void output_set_supports_term256(bool val) {
support_term256 = val;
}
static unsigned char index_for_color(rgb_color_t c) {
2012-03-06 02:44:08 +08:00
if (c.is_named() || ! output_get_supports_term256()) {
return c.to_name_index();
} else {
return c.to_term256_index();
}
}
static bool write_color(char *todo, unsigned char idx, bool is_fg) {
bool result = false;
if (idx < 16 || term256_support_is_native()) {
/* Use tparm */
writembs( tparm( todo, idx ) );
result = true;
} else {
/* We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. */
char stridx[128];
format_long_safe(stridx, idx);
char buff[128] = "\x1b[";
strcat(buff, is_fg ? "38;5;" : "48;5;");
strcat(buff, stridx);
strcat(buff, "m");
int (*writer)(char) = output_get_writer();
if (writer) {
for (size_t i=0; buff[i]; i++) {
writer(buff[i]);
}
}
result = true;
}
return result;
}
static bool write_foreground_color(unsigned char idx) {
if (set_a_foreground && set_a_foreground[0]) {
return write_color(set_a_foreground, idx, true);
} else if (set_foreground && set_foreground[0]) {
return write_color(set_foreground, idx, true);
} else {
return false;
}
}
static bool write_background_color(unsigned char idx) {
if (set_a_background && set_a_background[0]) {
return write_color(set_a_background, idx, false);
} else if (set_background && set_background[0]) {
return write_color(set_background, idx, false);
} else {
return false;
}
}
2012-02-12 09:07:56 +08:00
void set_color(rgb_color_t c, rgb_color_t c2)
{
#if 0
wcstring tmp = c.description();
wcstring tmp2 = c2.description();
printf("set_color %ls : %ls\n", tmp.c_str(), tmp2.c_str());
#endif
2012-02-12 09:07:56 +08:00
ASSERT_IS_MAIN_THREAD();
const rgb_color_t normal = rgb_color_t::normal();
static rgb_color_t last_color = rgb_color_t::normal();
static rgb_color_t last_color2 = rgb_color_t::normal();
static int was_bold=0;
static int was_underline=0;
int bg_set=0, last_bg_set=0;
2012-02-12 09:07:56 +08:00
int is_bold = 0;
int is_underline = 0;
2012-02-12 09:07:56 +08:00
/*
Test if we have at least basic support for setting fonts, colors
and related bits - otherwise just give up...
*/
2012-02-12 09:07:56 +08:00
if( !exit_attribute_mode )
{
return;
}
2012-02-12 09:07:56 +08:00
is_bold |= c.is_bold();
is_bold |= c2.is_bold();
2012-02-12 09:07:56 +08:00
is_underline |= c.is_underline();
is_underline |= c2.is_underline();
2012-02-12 09:07:56 +08:00
if( c.is_reset() || c2.is_reset())
{
c = c2 = normal;
was_bold=0;
was_underline=0;
/*
If we exit attibute mode, we must first set a color, or
previously coloured text might lose it's
color. Terminals are weird...
*/
write_foreground_color(0);
2012-02-12 09:07:56 +08:00
writembs( exit_attribute_mode );
return;
}
2012-02-12 09:07:56 +08:00
if( was_bold && !is_bold )
{
/*
Only way to exit bold mode is a reset of all attributes.
*/
2012-02-12 09:07:56 +08:00
writembs( exit_attribute_mode );
last_color = normal;
last_color2 = normal;
was_bold=0;
was_underline=0;
}
if( ! last_color2.is_normal() &&
! last_color2.is_reset() &&
! last_color2.is_ignore() )
2012-02-12 09:07:56 +08:00
{
/*
Background was set
*/
2012-02-12 09:07:56 +08:00
last_bg_set=1;
}
if( ! c2.is_normal() &&
! c2.is_ignore())
2012-02-12 09:07:56 +08:00
{
/*
Background is set
*/
2012-02-12 09:07:56 +08:00
bg_set=1;
Squashed commit of the following: commit 5b7659ec3d5e67b8dad8d3543d87a0169dc9a9e9 Merge: 57f3df3 22a4cd6 Author: ridiculousfish <corydoras@ridiculousfish.com> Date: Thu Jun 21 10:15:41 2012 -0700 Merge branch 'master' of https://github.com/maxfl/fish-shell into maxfl-master-base commit 22a4cd686fbe4a6730859aa1a84b21bc9c832203 Author: maxfl <gmaxfl@gmail.com> Date: Tue Jun 19 15:51:43 2012 +0400 set now expands the variable size, if index is outside it commit 9b0ffa83157ce0cfa36d246fa2e3179d6b790dea Author: maxfl <gmaxfl@gmail.com> Date: Mon Jun 18 21:30:44 2012 +0400 fixes #78 commit 78387fb3915b01342a981059780a164eedc0f8eb Merge: c0e6096 93dc7d4 Author: maxfl <gmaxfl@gmail.com> Date: Mon Jun 18 21:27:47 2012 +0400 Merge remote-tracking branch 'fishfish/master' commit c0e60963c179f80cb1d5d6a18b44510e55c95e10 Merge: 32a98e7 1bead8a Author: maxfl <gmaxfl@gmail.com> Date: Mon Jun 18 10:29:42 2012 +0400 Merge remote-tracking branch 'fishfish/master' commit 32a98e799eb2f016f8bad5287851f6353b835014 Merge: 6e71021 f2b5292 Author: maxfl <gmaxfl@gmail.com> Date: Sat Jun 16 18:42:07 2012 +0400 Merge remote-tracking branch 'fishfish/master' commit 6e710211bca0bca73d738d71e22d20e700db2a63 Author: maxfl <gmaxfl@gmail.com> Date: Thu Jun 14 11:01:13 2012 +0400 revert fish_pager commit 731a29f35bdbaa4dfaad78c7428ab2e5edb45a6c Author: maxfl <gmaxfl@gmail.com> Date: Thu Jun 14 10:57:41 2012 +0400 revert fish_pager.cpp commit 72c1bfc7bfa77bb723d6df3f030a1918db2aca8a Merge: ea74ffa 9b781c4 Author: maxfl <gmaxfl@gmail.com> Date: Wed Jun 13 17:54:11 2012 +0400 Merge branch 'master' into maxfl_completions commit ea74ffa08689a35fd08bc3520a0d52cf30365568 Author: maxfl <gmaxfl@gmail.com> Date: Wed Jun 13 17:35:20 2012 +0400 __fish_complete_command now can understand '--arg=option' tokens latexmk completion is updated commit 45b667826f3f181972e4987a3609d4ccac16a675 Author: maxfl <gmaxfl@gmail.com> Date: Wed Jun 13 16:46:47 2012 +0400 . completion commit 1c9f8ffc9e7ea45eee8aedba5a48e21c75a08882 Author: maxfl <gmaxfl@gmail.com> Date: Wed Jun 13 16:46:13 2012 +0400 a lot of new completions commit 8224d9f984a678fd49cdf78f76770977e0ae257b Author: Maxim Gonchar <gonchar@myhost.localdomain> Date: Tue Jun 12 20:19:31 2012 +0400 A lot of new completions. Some small updates and fixes of old functions and completions. commit 234ed8f5dab209fd657ccaf0dd23d636d2a06355 Author: Maxim Gonchar <gonchar@myhost.localdomain> Date: Tue Jun 12 20:03:44 2012 +0400 step-coloring initial set_color correction
2012-06-22 01:24:49 +08:00
if ( c==c2 )
c = (c2==rgb_color_t::white())?rgb_color_t::black():rgb_color_t::white();
2012-02-12 09:07:56 +08:00
}
2012-02-12 09:07:56 +08:00
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0))
{
if(bg_set && !last_bg_set)
{
/*
Background color changed and is set, so we enter bold
mode to make reading easier. This means bold mode is
_always_ on when the background color is set.
*/
2012-02-12 09:07:56 +08:00
writembs( enter_bold_mode );
}
if(!bg_set && last_bg_set)
{
/*
Background color changed and is no longer set, so we
exit bold mode
*/
2012-02-12 09:07:56 +08:00
writembs( exit_attribute_mode );
was_bold=0;
was_underline=0;
/*
We don't know if exit_attribute_mode resets colors, so
we set it to something known.
*/
if( write_foreground_color(0))
2012-02-12 09:07:56 +08:00
{
last_color=rgb_color_t::black();
}
}
}
2012-02-12 09:07:56 +08:00
if( last_color != c )
{
if( c.is_normal() )
2012-02-12 09:07:56 +08:00
{
write_foreground_color(0);
2012-02-12 09:07:56 +08:00
writembs( exit_attribute_mode );
2012-02-12 09:07:56 +08:00
last_color2 = rgb_color_t::normal();
was_bold=0;
was_underline=0;
}
else if( ! c.is_special() )
2012-02-12 09:07:56 +08:00
{
write_foreground_color(index_for_color(c));
2012-02-12 09:07:56 +08:00
}
}
2012-02-12 09:07:56 +08:00
last_color = c;
2012-02-12 09:07:56 +08:00
if( last_color2 != c2 )
{
if( c2.is_normal() )
2012-02-12 09:07:56 +08:00
{
write_background_color(0);
2012-02-12 09:07:56 +08:00
writembs( exit_attribute_mode );
if( ! last_color.is_normal())
2012-02-12 09:07:56 +08:00
{
write_foreground_color(index_for_color(last_color));
2012-02-12 09:07:56 +08:00
}
2012-02-12 09:07:56 +08:00
was_bold=0;
was_underline=0;
last_color2 = c2;
}
else if ( ! c2.is_special() )
2012-02-12 09:07:56 +08:00
{
write_background_color(index_for_color(c2));
2012-02-12 09:07:56 +08:00
last_color2 = c2;
}
}
2012-02-12 09:07:56 +08:00
/*
Lastly, we set bold mode and underline mode correctly
*/
2012-02-12 09:07:56 +08:00
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set )
{
if( is_bold && !was_bold )
{
if( enter_bold_mode )
{
writembs( tparm( enter_bold_mode ) );
}
}
was_bold = is_bold;
}
2012-02-12 09:07:56 +08:00
if( was_underline && !is_underline )
{
writembs( exit_underline_mode );
}
if( !was_underline && is_underline )
{
writembs( enter_underline_mode );
}
was_underline = is_underline;
}
/**
Default output method, simply calls write() on stdout
*/
static int writeb_internal( char c )
{
write_loop( 1, &c, 1 );
return 0;
}
int writeb( tputs_arg_t b )
{
out( b );
return 0;
}
int writembs_internal( char *str )
{
CHECK( str, 1 );
return tputs(str,1,&writeb)==ERR?1:0;
}
int writech( wint_t ch )
{
mbstate_t state;
size_t i;
char buff[MB_LEN_MAX+1];
size_t bytes;
if( ( ch >= ENCODE_DIRECT_BASE) &&
( ch < ENCODE_DIRECT_BASE+256) )
{
buff[0] = ch - ENCODE_DIRECT_BASE;
bytes=1;
}
else
{
memset( &state, 0, sizeof(state) );
bytes= wcrtomb( buff, ch, &state );
switch( bytes )
{
case (size_t)(-1):
{
return 1;
}
}
}
for( i=0; i<bytes; i++ )
{
out( buff[i] );
}
return 0;
}
void writestr( const wchar_t *str )
{
char *pos;
CHECK( str, );
// while( *str )
// writech( *str++ );
/*
Check amount of needed space
*/
size_t len = wcstombs( 0, str, 0 );
if( len == (size_t)-1 )
{
debug( 1, L"Tried to print invalid wide character string" );
return;
}
len++;
/*
Convert
*/
2012-02-08 14:44:10 +08:00
char *buffer, static_buffer[256];
if (len <= sizeof static_buffer)
buffer = static_buffer;
else
buffer = new char[len];
wcstombs( buffer,
str,
len );
/*
Write
*/
2012-02-08 14:44:10 +08:00
for( pos = buffer; *pos; pos++ )
{
out( *pos );
}
2012-02-08 14:44:10 +08:00
if (buffer != static_buffer)
delete[] buffer;
}
int write_escaped_str( const wchar_t *str, int max_len )
{
wchar_t *out;
int i;
int len;
int written=0;
CHECK( str, 0 );
out = escape( str, 1 );
len = my_wcswidth( out );
if( max_len && (max_len < len))
{
for( i=0; (written+fish_wcwidth(out[i]))<=(max_len-1); i++ )
{
writech( out[i] );
written += fish_wcwidth( out[i] );
}
for( i=written; i<max_len; i++ )
{
writech( L' ' );
written++;
}
}
else
{
written = len;
writestr( out );
}
free( out );
return written;
}
int output_color_code( const wcstring &val, bool is_background ) {
size_t i;
int color=FISH_COLOR_NORMAL;
int is_bold=0;
int is_underline=0;
if (val.empty())
return FISH_COLOR_NORMAL;
wcstring_list_t el;
2012-02-10 17:37:30 +08:00
tokenize_variable_array( val, el );
for(size_t j=0; j < el.size(); j++ ) {
const wcstring &next = el.at(j);
wcstring color_name;
if (is_background) {
// look for something like "--background=red"
const wcstring prefix = L"--background=";
if (string_prefixes_string(prefix, next)) {
color_name = wcstring(next, prefix.size());
}
} else {
if (next == L"--bold" || next == L"-o")
is_bold = true;
else if (next == L"--underline" || next == L"-u")
is_underline = true;
else
color_name = next;
}
if (! color_name.empty()) {
for( i=0; i<COLORS; i++ )
{
if( wcscasecmp( col[i], color_name.c_str() ) == 0 )
{
color = col_idx[i];
break;
}
}
}
}
2012-02-12 09:07:56 +08:00
return color | (is_bold?FISH_COLOR_BOLD:0) | (is_underline?FISH_COLOR_UNDERLINE:0);
}
rgb_color_t parse_color( const wcstring &val, bool is_background ) {
int is_bold=0;
int is_underline=0;
std::vector<rgb_color_t> candidates;
2012-02-12 09:07:56 +08:00
wcstring_list_t el;
tokenize_variable_array( val, el );
2012-02-12 09:07:56 +08:00
for(size_t j=0; j < el.size(); j++ ) {
const wcstring &next = el.at(j);
wcstring color_name;
if (is_background) {
// look for something like "--background=red"
const wcstring prefix = L"--background=";
if (string_prefixes_string(prefix, next)) {
color_name = wcstring(next, prefix.size());
}
} else {
if (next == L"--bold" || next == L"-o")
is_bold = true;
else if (next == L"--underline" || next == L"-u")
is_underline = true;
else
color_name = next;
}
if (! color_name.empty()) {
rgb_color_t color = rgb_color_t(color_name);
if (! color.is_none()) {
candidates.push_back(color);
}
2012-02-12 09:07:56 +08:00
}
}
// Pick the best candidate
rgb_color_t first_rgb = rgb_color_t::none(), first_named = rgb_color_t::none();
for (size_t i=0; i < candidates.size(); i++) {
const rgb_color_t &color = candidates.at(i);
if (color.is_rgb() && first_rgb.is_none())
first_rgb = color;
if (color.is_named() && first_named.is_none())
first_named = color;
}
// If we have both RGB and named colors, then prefer rgb if term256 is supported
rgb_color_t result;
2012-03-06 02:44:08 +08:00
if ((!first_rgb.is_none() && output_get_supports_term256()) || first_named.is_none()) {
result = first_rgb;
} else {
result = first_named;
}
if (result.is_none())
result = rgb_color_t::normal();
result.set_bold(is_bold);
result.set_underline(is_underline);
#if 0
wcstring desc = result.description();
printf("Parsed %ls from %ls (%s)\n", desc.c_str(), val.c_str(), is_background ? "background" : "foreground");
#endif
2012-02-12 09:07:56 +08:00
return result;
}
void output_set_term( const wchar_t *term )
{
current_term = term;
}
const wchar_t *output_get_term()
{
return current_term.empty() ? L"<unknown>" : current_term.c_str();
}