/** \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 "halloc_util.h" #include "highlight.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 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 int col_idx[]= { 0, 1, 2, 3, 3, 4, 5, 5, 6, 7, FISH_COLOR_NORMAL, } ; /** Size of writestr_buff */ static size_t writestr_buff_sz=0; /** Temp buffer used for converting from wide to narrow strings */ static char *writestr_buff = 0; /** The function used for output */ static int (*out)(char c) = &writeb_internal; /** Cleanup function. Run automatically through halloc */ static void output_destroy() { free( writestr_buff ); } void output_set_writer( int (*writer)(char) ) { CHECK( writer, ); out = writer; } int (*output_get_writer())(char) { return out; } void set_color( int c, int c2 ) { static int last_color = FISH_COLOR_NORMAL; static int last_color2 = FISH_COLOR_NORMAL; static int was_bold=0; static int was_underline=0; int bg_set=0, last_bg_set=0; char *fg = 0, *bg=0; int is_bold = 0; int is_underline = 0; /* Test if we have at least basic support for setting fonts, colors and related bits - otherwise just give up... */ if( !exit_attribute_mode ) { return; } is_bold |= (c&FISH_COLOR_BOLD)!=0; is_bold |= (c2&FISH_COLOR_BOLD)!=0; is_underline |= (c&FISH_COLOR_UNDERLINE)!=0; is_underline |= (c2&FISH_COLOR_UNDERLINE)!=0; c = c&(~(FISH_COLOR_BOLD|FISH_COLOR_UNDERLINE)); c2 = c2&(~(FISH_COLOR_BOLD|FISH_COLOR_UNDERLINE)); if( (set_a_foreground != 0) && (strlen( set_a_foreground) != 0 ) ) { fg = set_a_foreground; bg = set_a_background; } else if( (set_foreground != 0) && (strlen( set_foreground) != 0 ) ) { fg = set_foreground; bg = set_background; } if( (c == FISH_COLOR_RESET) || (c2 == FISH_COLOR_RESET)) { c = c2 = FISH_COLOR_NORMAL; was_bold=0; was_underline=0; if( fg ) { /* If we exit attibute mode, we must first set a color, or previously coloured text might lose it's color. Terminals are weird... */ writembs( tparm( fg, 0 ) ); } writembs( exit_attribute_mode ); return; } if( was_bold && !is_bold ) { /* Only way to exit bold mode is a reset of all attributes. */ writembs( exit_attribute_mode ); last_color = FISH_COLOR_NORMAL; last_color2 = FISH_COLOR_NORMAL; was_bold=0; was_underline=0; } if( last_color2 != FISH_COLOR_NORMAL && last_color2 != FISH_COLOR_RESET && last_color2 != FISH_COLOR_IGNORE ) { /* Background was set */ last_bg_set=1; } if( c2 != FISH_COLOR_NORMAL && c2 != FISH_COLOR_IGNORE ) { /* Background is set */ bg_set=1; c = (c2==FISH_COLOR_WHITE)?FISH_COLOR_BLACK:FISH_COLOR_WHITE; } 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. */ writembs( enter_bold_mode ); } if(!bg_set && last_bg_set) { /* Background color changed and is no longer set, so we exit bold mode */ 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( fg ) { writembs( tparm( fg, 0 ) ); last_color=0; } } } if( last_color != c ) { if( c==FISH_COLOR_NORMAL ) { if( fg ) { writembs( tparm( fg, 0 ) ); } writembs( exit_attribute_mode ); last_color2 = FISH_COLOR_NORMAL; was_bold=0; was_underline=0; } else if( ( c >= 0 ) && ( c < FISH_COLOR_NORMAL ) ) { if( fg ) { writembs( tparm( fg, c ) ); } } } last_color = c; if( last_color2 != c2 ) { if( c2 == FISH_COLOR_NORMAL ) { if( bg ) { writembs( tparm( bg, 0 ) ); } writembs( exit_attribute_mode ); if( ( last_color != FISH_COLOR_NORMAL ) && fg ) { if( fg ) { writembs( tparm( fg, last_color ) ); } } was_bold=0; was_underline=0; last_color2 = c2; } else if ( ( c2 >= 0 ) && ( c2 < FISH_COLOR_NORMAL ) ) { if( bg ) { writembs( tparm( bg, c2 ) ); } last_color2 = c2; } } /* Lastly, we set bold mode and underline mode correctly */ 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; } 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( 1, &c, 1 ); return 0; } int writeb( tputs_arg_t b ) { out( b ); return 0; } int writembs( char *str ) { CHECK( str, 1 ); return tputs(str,1,&writeb)==ERR?1:0; } int writech( wint_t ch ) { mbstate_t state; int 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++; /* Reallocate if needed */ if( writestr_buff_sz < len ) { if( !writestr_buff ) { halloc_register_function_void( global_context, &output_destroy ); } writestr_buff = realloc( writestr_buff, len ); if( !writestr_buff ) { DIE_MEM(); } writestr_buff_sz = len; } /* Convert */ wcstombs( writestr_buff, str, writestr_buff_sz ); /* Write */ for( pos = writestr_buff; *pos; pos++ ) { out( *pos ); } } void writestr_ellipsis( const wchar_t *str, int max_width ) { int written=0; int tot; CHECK( str, ); tot = my_wcswidth(str); if( tot <= max_width ) { writestr( str ); return; } while( *str != 0 ) { int w = wcwidth( *str ); if( written+w+wcwidth( ellipsis_char )>max_width ) { break; } written+=w; writech( *(str++) ); } written += wcwidth( ellipsis_char ); writech( ellipsis_char ); while( written < max_width ) { written++; writestr( L" " ); } } 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+wcwidth(out[i]))<=(max_len-1); i++ ) { writech( out[i] ); written += wcwidth( out[i] ); } writech( ellipsis_char ); written += wcwidth( ellipsis_char ); for( i=written; i<max_len; i++ ) { writech( L' ' ); written++; } } else { written = len; writestr( out ); } free( out ); return written; } int output_color_code( const wchar_t *val ) { int j, i, color=FISH_COLOR_NORMAL; array_list_t el; int is_bold=0; int is_underline=0; if( !val ) return FISH_COLOR_NORMAL; al_init( &el ); tokenize_variable_array( val, &el ); for( j=0; j<al_get_count( &el ); j++ ) { wchar_t *next = (wchar_t *)al_get( &el, j ); is_bold |= (wcsncmp( next, L"--bold", wcslen(next) ) == 0 ) && wcslen(next)>=3; is_bold |= wcscmp( next, L"-o" ) == 0; is_underline |= (wcsncmp( next, L"--underline", wcslen(next) ) == 0 ) && wcslen(next)>=3; is_underline |= wcscmp( next, L"-u" ) == 0; for( i=0; i<COLORS; i++ ) { if( wcscasecmp( col[i], next ) == 0 ) { color = col_idx[i]; break; } } } al_foreach( &el, &free ); al_destroy( &el ); return color | (is_bold?FISH_COLOR_BOLD:0) | (is_underline?FISH_COLOR_UNDERLINE:0); }