mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-12-12 16:53:37 +08:00
993 lines
18 KiB
C
993 lines
18 KiB
C
|
#include "config.h"
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <wchar.h>
|
||
|
#include <unistd.h>
|
||
|
#include <termios.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <dirent.h>
|
||
|
#include <fcntl.h>
|
||
|
|
||
|
#include <locale.h>
|
||
|
|
||
|
#if HAVE_NCURSES_H
|
||
|
#include <ncurses.h>
|
||
|
#else
|
||
|
#include <curses.h>
|
||
|
#endif
|
||
|
|
||
|
#if HAVE_TERMIO_H
|
||
|
#include <termio.h>
|
||
|
#endif
|
||
|
|
||
|
#include <term.h>
|
||
|
#include <signal.h>
|
||
|
|
||
|
#include "util.h"
|
||
|
#include "wutil.h"
|
||
|
#include "common.h"
|
||
|
#include "complete.h"
|
||
|
#include "output.h"
|
||
|
#include "input_common.h"
|
||
|
#include "env_universal.h"
|
||
|
|
||
|
#define WCHAR_END 0x80000000
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
LINE_UP = R_NULL+1,
|
||
|
LINE_DOWN,
|
||
|
PAGE_UP,
|
||
|
PAGE_DOWN
|
||
|
}
|
||
|
;
|
||
|
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
HIGHLIGHT_PAGER_PREFIX,
|
||
|
HIGHLIGHT_PAGER_COMPLETION,
|
||
|
HIGHLIGHT_PAGER_DESCRIPTION,
|
||
|
HIGHLIGHT_PAGER_PROGRESS
|
||
|
}
|
||
|
;
|
||
|
|
||
|
/**
|
||
|
This struct should be continually updated by signals as the term
|
||
|
resizes, and as such always contain the correct current size.
|
||
|
*/
|
||
|
|
||
|
static struct winsize termsize;
|
||
|
|
||
|
static struct termios saved_modes;
|
||
|
static struct termios pager_modes;
|
||
|
|
||
|
static int is_ca_mode = 0;
|
||
|
|
||
|
|
||
|
/**
|
||
|
The environment variables used to specify the color of different
|
||
|
tokens.
|
||
|
*/
|
||
|
static wchar_t *hightlight_var[] =
|
||
|
{
|
||
|
L"fish_pager_color_prefix",
|
||
|
L"fish_pager_color_completion",
|
||
|
L"fish_pager_color_description",
|
||
|
L"fish_pager_color_progress"
|
||
|
}
|
||
|
;
|
||
|
|
||
|
static string_buffer_t out_buff;
|
||
|
static FILE *out_file;
|
||
|
|
||
|
|
||
|
int get_color( int highlight )
|
||
|
{
|
||
|
if( highlight < 0 )
|
||
|
return FISH_COLOR_NORMAL;
|
||
|
if( highlight >= (4) )
|
||
|
return FISH_COLOR_NORMAL;
|
||
|
|
||
|
wchar_t *val = env_universal_get( hightlight_var[highlight]);
|
||
|
|
||
|
if( val == 0 )
|
||
|
return FISH_COLOR_NORMAL;
|
||
|
|
||
|
if( val == 0 )
|
||
|
{
|
||
|
return FISH_COLOR_NORMAL;
|
||
|
}
|
||
|
|
||
|
return output_color_code( val );
|
||
|
}
|
||
|
|
||
|
int try_sequence( char *seq )
|
||
|
{
|
||
|
int j, k;
|
||
|
wint_t c=0;
|
||
|
|
||
|
for( j=0;
|
||
|
seq[j] != '\0' && seq[j] == (c=input_common_readch( j>0 ));
|
||
|
j++ )
|
||
|
;
|
||
|
|
||
|
if( seq[j] == '\0' )
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
input_common_unreadch(c);
|
||
|
for(k=j-1; k>=0; k--)
|
||
|
input_common_unreadch(seq[k]);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static wint_t readch()
|
||
|
{
|
||
|
struct mapping
|
||
|
{
|
||
|
char *seq;
|
||
|
wint_t bnd;
|
||
|
}
|
||
|
;
|
||
|
|
||
|
struct mapping m[]=
|
||
|
{
|
||
|
{
|
||
|
"\e[A", LINE_UP
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
key_up, LINE_UP
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
"\e[B", LINE_DOWN
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
key_down, LINE_DOWN
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
key_ppage, PAGE_UP
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
key_npage, PAGE_DOWN
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
" ", PAGE_DOWN
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
"\t", PAGE_DOWN
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
0, 0
|
||
|
}
|
||
|
|
||
|
}
|
||
|
;
|
||
|
int i;
|
||
|
|
||
|
for( i=0; m[i].seq; i++ )
|
||
|
{
|
||
|
if( try_sequence(m[i].seq ) )
|
||
|
return m[i].bnd;
|
||
|
}
|
||
|
return input_common_readch(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Print the specified part of the completion list, using the
|
||
|
specified column offsets and quoting style.
|
||
|
|
||
|
\param l The list of completions to print
|
||
|
\param cols number of columns to print in
|
||
|
\param width An array specifying the width of each column
|
||
|
\param row_start The first row to print
|
||
|
\param row_stop the row after the last row to print
|
||
|
\param prefix The string to print before each completion
|
||
|
\param is_quoted Whether to print the completions are in a quoted environment
|
||
|
*/
|
||
|
|
||
|
static void completion_print( int cols,
|
||
|
int *width,
|
||
|
int row_start,
|
||
|
int row_stop,
|
||
|
wchar_t *prefix,
|
||
|
int is_quoted,
|
||
|
array_list_t *l)
|
||
|
{
|
||
|
|
||
|
int rows = (al_get_count( l )-1)/cols+1;
|
||
|
int i, j;
|
||
|
int prefix_width= my_wcswidth(prefix);
|
||
|
|
||
|
for( i = row_start; i<row_stop; i++ )
|
||
|
{
|
||
|
for( j = 0; j < cols; j++ )
|
||
|
{
|
||
|
wchar_t *el, *el_end;
|
||
|
|
||
|
if( al_get_count( l ) <= j*rows + i )
|
||
|
continue;
|
||
|
|
||
|
el = (wchar_t *)al_get( l, j*rows + i );
|
||
|
el_end= wcschr( el, COMPLETE_SEP );
|
||
|
|
||
|
set_color( get_color(HIGHLIGHT_PAGER_PREFIX),FISH_COLOR_NORMAL );
|
||
|
|
||
|
writestr( prefix );
|
||
|
|
||
|
set_color( get_color(HIGHLIGHT_PAGER_COMPLETION),FISH_COLOR_IGNORE );
|
||
|
|
||
|
if( el_end == 0 )
|
||
|
{
|
||
|
/* We do not have a description for this completion */
|
||
|
int written = 0;
|
||
|
int max_written = width[j] - prefix_width - (j==cols-1?0:2);
|
||
|
|
||
|
if( is_quoted )
|
||
|
{
|
||
|
for( i=0; i<max_written; i++ )
|
||
|
{
|
||
|
if( !el[i] )
|
||
|
break;
|
||
|
writech( el[i] );
|
||
|
written+= wcwidth( el[i] );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
written = write_escaped_str( el, max_written );
|
||
|
}
|
||
|
|
||
|
set_color( get_color( HIGHLIGHT_PAGER_DESCRIPTION ),
|
||
|
FISH_COLOR_IGNORE );
|
||
|
|
||
|
writespace( width[j]-
|
||
|
written-
|
||
|
prefix_width );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int whole_desc_width = my_wcswidth(el_end+1);
|
||
|
int whole_comp_width;
|
||
|
|
||
|
/*
|
||
|
Temporarily drop the description so that wcswidth et
|
||
|
al only calculate the width of the completion.
|
||
|
*/
|
||
|
*el_end = L'\0';
|
||
|
|
||
|
/*
|
||
|
Calculate preferred completion width
|
||
|
*/
|
||
|
if( is_quoted )
|
||
|
{
|
||
|
whole_comp_width = my_wcswidth(el);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wchar_t *tmp = escape( wcsdup(el), 1 );
|
||
|
whole_comp_width = my_wcswidth( tmp );
|
||
|
free(tmp);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Calculate how wide this entry 'wants' to be
|
||
|
*/
|
||
|
int pref_width = whole_desc_width + 4 + prefix_width + 2 -
|
||
|
(j==cols-1?2:0) + whole_comp_width;
|
||
|
|
||
|
int comp_width, desc_width;
|
||
|
|
||
|
if( pref_width <= width[j] )
|
||
|
{
|
||
|
/*
|
||
|
The entry fits, we give it as much space as it wants
|
||
|
*/
|
||
|
comp_width = whole_comp_width;
|
||
|
desc_width = whole_desc_width;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
The completion and description won't fit on the
|
||
|
allocated space. Give a maximum of 2/3 of the
|
||
|
space to the completion, and whatever is left to
|
||
|
the description.
|
||
|
*/
|
||
|
int sum = width[j] - prefix_width - 4 - 2 + (j==cols-1?2:0);
|
||
|
|
||
|
comp_width = maxi( mini( whole_comp_width,
|
||
|
2*sum/3 ),
|
||
|
sum - whole_desc_width );
|
||
|
desc_width = sum-comp_width;
|
||
|
}
|
||
|
|
||
|
/* First we must print the completion. */
|
||
|
if( is_quoted )
|
||
|
{
|
||
|
writestr_ellipsis( el, comp_width);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
write_escaped_str( el, comp_width );
|
||
|
}
|
||
|
|
||
|
/* Put the description back */
|
||
|
*el_end = COMPLETE_SEP;
|
||
|
|
||
|
/* And print it */
|
||
|
set_color( get_color(HIGHLIGHT_PAGER_DESCRIPTION),
|
||
|
FISH_COLOR_IGNORE );
|
||
|
writespace( maxi( 2,
|
||
|
width[j]
|
||
|
- comp_width
|
||
|
- desc_width
|
||
|
- 4
|
||
|
- prefix_width
|
||
|
+ (j==cols-1?2:0) ) );
|
||
|
/* Print description */
|
||
|
writestr(L"(");
|
||
|
writestr_ellipsis( el_end+1, desc_width);
|
||
|
writestr(L")");
|
||
|
|
||
|
if( j != cols-1)
|
||
|
writestr( L" " );
|
||
|
|
||
|
}
|
||
|
}
|
||
|
writech( L'\n' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Calculates how long the specified string would be when printed on the command line.
|
||
|
|
||
|
\param str The string to be printed.
|
||
|
\param is_quoted Whether the string would be printed quoted or unquoted
|
||
|
\param pref_width the preferred width for this item
|
||
|
\param min_width the minimum width for this item
|
||
|
*/
|
||
|
static void printed_length( wchar_t *str,
|
||
|
int is_quoted,
|
||
|
int *pref_width,
|
||
|
int *min_width )
|
||
|
{
|
||
|
if( is_quoted )
|
||
|
{
|
||
|
wchar_t *sep = wcschr(str,COMPLETE_SEP);
|
||
|
if( sep )
|
||
|
{
|
||
|
*sep=0;
|
||
|
int cw = my_wcswidth( str );
|
||
|
int dw = my_wcswidth(sep+1);
|
||
|
|
||
|
if( termsize.ws_col > 80 )
|
||
|
dw = mini( dw, termsize.ws_col/3 );
|
||
|
|
||
|
|
||
|
*pref_width = cw+dw+4;
|
||
|
|
||
|
if( dw > termsize.ws_col/3 )
|
||
|
{
|
||
|
dw = termsize.ws_col/3;
|
||
|
}
|
||
|
|
||
|
*min_width=cw+dw+4;
|
||
|
|
||
|
*sep= COMPLETE_SEP;
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pref_width=*min_width= my_wcswidth( str );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int comp_len=0, desc_len=0;
|
||
|
int has_description = 0;
|
||
|
while( *str != 0 )
|
||
|
{
|
||
|
switch( *str )
|
||
|
{
|
||
|
case L'\n':
|
||
|
case L'\b':
|
||
|
case L'\r':
|
||
|
case L'\e':
|
||
|
case L'\t':
|
||
|
case L'\\':
|
||
|
case L'&':
|
||
|
case L'$':
|
||
|
case L' ':
|
||
|
case L'#':
|
||
|
case L'^':
|
||
|
case L'<':
|
||
|
case L'>':
|
||
|
case L'@':
|
||
|
case L'(':
|
||
|
case L')':
|
||
|
case L'{':
|
||
|
case L'}':
|
||
|
case L'?':
|
||
|
case L'*':
|
||
|
case L'|':
|
||
|
case L';':
|
||
|
case L':':
|
||
|
if( has_description )
|
||
|
desc_len++;
|
||
|
else
|
||
|
comp_len+=2;
|
||
|
break;
|
||
|
|
||
|
case COMPLETE_SEP:
|
||
|
has_description = 1;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if( has_description )
|
||
|
desc_len+= wcwidth(*str);
|
||
|
else
|
||
|
comp_len+= wcwidth(*str);
|
||
|
break;
|
||
|
}
|
||
|
str++;
|
||
|
}
|
||
|
if( has_description )
|
||
|
{
|
||
|
/*
|
||
|
Mangle long descriptions to make formating look nicer
|
||
|
*/
|
||
|
debug( 3, L"Desc, width = %d %d\n", comp_len, desc_len );
|
||
|
// if( termsize.ws_col > 80 )
|
||
|
// desc_len = mini( desc_len, termsize.ws_col/3 );
|
||
|
|
||
|
*pref_width = comp_len+ desc_len+4;;
|
||
|
|
||
|
comp_len = mini( comp_len, maxi(0,termsize.ws_col/3 - 2));
|
||
|
desc_len = mini( desc_len, maxi(0,termsize.ws_col/5 - 4));
|
||
|
|
||
|
*min_width = comp_len+ desc_len+4;
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
debug( 3, L"No desc, width = %d\n", comp_len );
|
||
|
|
||
|
*pref_width=*min_width= comp_len;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Try to print the list of completions l with the prefix prefix using
|
||
|
cols as the number of columns. Return 1 if the completion list was
|
||
|
printed, 0 if the terminal is to narrow for the specified number of
|
||
|
columns. Always succeeds if cols is 1.
|
||
|
|
||
|
If all the elements do not fit on the screen at once, make the list
|
||
|
scrollable using the up, down and space keys to move. The list will
|
||
|
exit when any other key is pressed.
|
||
|
|
||
|
\param cols the number of columns to try to fit onto the screen
|
||
|
\param prefix the character string to prefix each completion with
|
||
|
\param is_quoted whether the completions should be quoted
|
||
|
\param l the list of completions
|
||
|
|
||
|
\return zero if the specified number of columns do not fit, no-zero otherwise
|
||
|
*/
|
||
|
|
||
|
static int completion_try_print( int cols,
|
||
|
wchar_t *prefix,
|
||
|
int is_quoted,
|
||
|
array_list_t *l )
|
||
|
{
|
||
|
/*
|
||
|
The calculated preferred width of each column
|
||
|
*/
|
||
|
int pref_width[32];
|
||
|
/*
|
||
|
The calculated minimum width of each column
|
||
|
*/
|
||
|
int min_width[32];
|
||
|
/*
|
||
|
If the list can be printed with this width, width will contain the width of each column
|
||
|
*/
|
||
|
int *width=pref_width;
|
||
|
/*
|
||
|
Set to one if the list should be printed at this width
|
||
|
*/
|
||
|
int print=0;
|
||
|
|
||
|
int i, j;
|
||
|
|
||
|
int rows = (al_get_count( l )-1)/cols+1;
|
||
|
|
||
|
int pref_tot_width=0;
|
||
|
int min_tot_width = 0;
|
||
|
int prefix_width = my_wcswidth( prefix );
|
||
|
|
||
|
int res=0;
|
||
|
/*
|
||
|
Skip completions on tiny terminals
|
||
|
*/
|
||
|
|
||
|
if( termsize.ws_col < 16 )
|
||
|
return 1;
|
||
|
|
||
|
memset( pref_width, 0, sizeof(pref_width) );
|
||
|
memset( min_width, 0, sizeof(min_width) );
|
||
|
|
||
|
/* Calculated how wide the list would be */
|
||
|
for( j = 0; j < cols; j++ )
|
||
|
{
|
||
|
for( i = 0; i<rows; i++ )
|
||
|
{
|
||
|
int pref,min;
|
||
|
wchar_t *el;
|
||
|
if( al_get_count( l ) <= j*rows + i )
|
||
|
continue;
|
||
|
|
||
|
el = (wchar_t *)al_get( l, j*rows + i );
|
||
|
printed_length( el, is_quoted, &pref, &min );
|
||
|
|
||
|
pref += prefix_width;
|
||
|
min += prefix_width;
|
||
|
if( j != cols-1 )
|
||
|
{
|
||
|
pref += 2;
|
||
|
min += 2;
|
||
|
}
|
||
|
min_width[j] = maxi( min_width[j],
|
||
|
min );
|
||
|
pref_width[j] = maxi( pref_width[j],
|
||
|
pref );
|
||
|
}
|
||
|
min_tot_width += min_width[j];
|
||
|
pref_tot_width += pref_width[j];
|
||
|
}
|
||
|
/*
|
||
|
Force fit if one column
|
||
|
*/
|
||
|
if( cols == 1)
|
||
|
{
|
||
|
if( pref_tot_width > termsize.ws_col )
|
||
|
{
|
||
|
pref_width[0] = termsize.ws_col;
|
||
|
}
|
||
|
width = pref_width;
|
||
|
print=1;
|
||
|
}
|
||
|
else if( pref_tot_width <= termsize.ws_col )
|
||
|
{
|
||
|
/* Terminal is wide enough. Print the list! */
|
||
|
width = pref_width;
|
||
|
print=1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int next_rows = (al_get_count( l )-1)/(cols-1)+1;
|
||
|
/* fwprintf( stderr,
|
||
|
L"cols %d, min_tot %d, term %d, rows=%d, nextrows %d, termrows %d, diff %d\n",
|
||
|
cols,
|
||
|
min_tot_width, termsize.ws_col,
|
||
|
rows, next_rows, termsize.ws_row,
|
||
|
pref_tot_width-termsize.ws_col );
|
||
|
*/
|
||
|
if( min_tot_width < termsize.ws_col &&
|
||
|
( ( (rows < termsize.ws_row) && (next_rows >= termsize.ws_row ) ) ||
|
||
|
( pref_tot_width-termsize.ws_col< 4 && cols < 3 ) ) )
|
||
|
{
|
||
|
/*
|
||
|
Terminal almost wide enough, or squeezing makes the whole list fit on-screen
|
||
|
*/
|
||
|
int tot_width = min_tot_width;
|
||
|
width = min_width;
|
||
|
|
||
|
while( tot_width < termsize.ws_col )
|
||
|
{
|
||
|
for( i=0; (i<cols) && ( tot_width < termsize.ws_col ); i++ )
|
||
|
{
|
||
|
if( width[i] < pref_width[i] )
|
||
|
{
|
||
|
width[i]++;
|
||
|
tot_width++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
print=1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// return cols==1;
|
||
|
|
||
|
if( print )
|
||
|
{
|
||
|
res=1;
|
||
|
if( rows < termsize.ws_row )
|
||
|
{
|
||
|
/* List fits on screen. Print it and leave */
|
||
|
if( is_ca_mode )
|
||
|
{
|
||
|
is_ca_mode = 0;
|
||
|
writembs(exit_ca_mode);
|
||
|
}
|
||
|
|
||
|
completion_print( cols, width, 0, rows, prefix, is_quoted, l);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int npos, pos = 0;
|
||
|
int do_loop = 1;
|
||
|
|
||
|
is_ca_mode=1;
|
||
|
writembs(enter_ca_mode);
|
||
|
|
||
|
completion_print( cols,
|
||
|
width,
|
||
|
0,
|
||
|
termsize.ws_row-1,
|
||
|
prefix,
|
||
|
is_quoted,
|
||
|
l);
|
||
|
/*
|
||
|
List does not fit on screen. Print one screenfull and
|
||
|
leave a scrollable interface
|
||
|
*/
|
||
|
while(do_loop)
|
||
|
{
|
||
|
wchar_t msg[10];
|
||
|
int percent = 100*pos/(rows-termsize.ws_row+1);
|
||
|
set_color( FISH_COLOR_BLACK,
|
||
|
get_color(HIGHLIGHT_PAGER_PROGRESS) );
|
||
|
swprintf( msg, 12,
|
||
|
L" %ls(%d%%) \r",
|
||
|
percent==100?L"":(percent >=10?L" ": L" "),
|
||
|
percent );
|
||
|
writestr(msg);
|
||
|
set_color( FISH_COLOR_NORMAL, FISH_COLOR_NORMAL );
|
||
|
int c = readch();
|
||
|
|
||
|
switch( c )
|
||
|
{
|
||
|
case LINE_UP:
|
||
|
{
|
||
|
if( pos > 0 )
|
||
|
{
|
||
|
pos--;
|
||
|
writembs(tparm( cursor_address, 0, 0));
|
||
|
writembs(scroll_reverse);
|
||
|
completion_print( cols,
|
||
|
width,
|
||
|
pos,
|
||
|
pos+1,
|
||
|
prefix,
|
||
|
is_quoted,
|
||
|
l );
|
||
|
writembs( tparm( cursor_address,
|
||
|
termsize.ws_row-1, 0) );
|
||
|
writembs(clr_eol );
|
||
|
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case LINE_DOWN:
|
||
|
{
|
||
|
if( pos <= (rows - termsize.ws_row ) )
|
||
|
{
|
||
|
pos++;
|
||
|
completion_print( cols,
|
||
|
width,
|
||
|
pos+termsize.ws_row-2,
|
||
|
pos+termsize.ws_row-1,
|
||
|
prefix,
|
||
|
is_quoted,
|
||
|
l );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case PAGE_DOWN:
|
||
|
{
|
||
|
|
||
|
npos = mini( rows - termsize.ws_row+1,
|
||
|
pos + termsize.ws_row-1 );
|
||
|
if( npos != pos )
|
||
|
{
|
||
|
pos = npos;
|
||
|
completion_print( cols,
|
||
|
width,
|
||
|
pos,
|
||
|
pos+termsize.ws_row-1,
|
||
|
prefix,
|
||
|
is_quoted,
|
||
|
l );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
writembs( flash_screen );
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case PAGE_UP:
|
||
|
{
|
||
|
npos = maxi( 0,
|
||
|
pos - termsize.ws_row+1 );
|
||
|
|
||
|
if( npos != pos )
|
||
|
{
|
||
|
pos = npos;
|
||
|
completion_print( cols,
|
||
|
width,
|
||
|
pos,
|
||
|
pos+termsize.ws_row-1,
|
||
|
prefix,
|
||
|
is_quoted,
|
||
|
l );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
writembs( flash_screen );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case R_NULL:
|
||
|
{
|
||
|
do_loop=0;
|
||
|
res=2;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
sb_append_char( &out_buff, c );
|
||
|
do_loop = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
writembs(clr_eol);
|
||
|
}
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Substitute any series of tabs, newlines, etc. with a single space character in completion description
|
||
|
*/
|
||
|
static void mangle_descriptions( array_list_t *l )
|
||
|
{
|
||
|
int i, skip;
|
||
|
for( i=0; i<al_get_count( l ); i++ )
|
||
|
{
|
||
|
wchar_t *next = (wchar_t *)al_get(l, i);
|
||
|
wchar_t *in, *out;
|
||
|
skip=0;
|
||
|
|
||
|
while( *next != COMPLETE_SEP && *next )
|
||
|
next++;
|
||
|
|
||
|
if( !*next )
|
||
|
continue;
|
||
|
|
||
|
in=out=(next+1);
|
||
|
|
||
|
while( *in != 0 )
|
||
|
{
|
||
|
if( *in == L' ' || *in==L'\t' || *in<32 )
|
||
|
{
|
||
|
if( !skip )
|
||
|
*out++=L' ';
|
||
|
skip=1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*out++ = *in;
|
||
|
skip=0;
|
||
|
}
|
||
|
in++;
|
||
|
}
|
||
|
*out=0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Respond to a winch signal by checking the terminal size
|
||
|
*/
|
||
|
static void handle_winch( int sig )
|
||
|
{
|
||
|
if (ioctl(1,TIOCGWINSZ,&termsize)!=0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int interrupt_handler()
|
||
|
{
|
||
|
return R_NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void init()
|
||
|
{
|
||
|
struct sigaction act;
|
||
|
program_name = L"fish_pager";
|
||
|
fish_setlocale( LC_ALL, L"" );
|
||
|
|
||
|
|
||
|
int out = dup( 1 );
|
||
|
close(1);
|
||
|
if( open( ttyname(0), O_WRONLY ) != 1 )
|
||
|
{
|
||
|
debug( 0, L"Could not set up file descriptors for pager" );
|
||
|
exit( 1 );
|
||
|
|
||
|
}
|
||
|
out_file = fdopen( out, "w" );
|
||
|
sb_init( &out_buff );
|
||
|
|
||
|
|
||
|
|
||
|
env_universal_init( 0, 0, 0);
|
||
|
input_common_init( &interrupt_handler );
|
||
|
|
||
|
sigemptyset( & act.sa_mask );
|
||
|
act.sa_flags=0;
|
||
|
act.sa_handler=SIG_DFL;
|
||
|
act.sa_flags = 0;
|
||
|
act.sa_handler= &handle_winch;
|
||
|
if( sigaction( SIGWINCH, &act, 0 ) )
|
||
|
{
|
||
|
wperror( L"sigaction" );
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* Loop until we are in the foreground. */
|
||
|
while (tcgetpgrp( 0 ) != getpid())
|
||
|
{
|
||
|
kill (- getpid(), SIGTTIN);
|
||
|
}
|
||
|
|
||
|
/* Put ourselves in our own process group. */
|
||
|
if( getpgrp() != getpid() )
|
||
|
{
|
||
|
if (setpgid (getpid(), getpid()) < 0)
|
||
|
{
|
||
|
debug( 1,
|
||
|
L"Couldn't put the shell in its own process group");
|
||
|
wperror( L"setpgid" );
|
||
|
exit (1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Grab control of the terminal. */
|
||
|
if( tcsetpgrp (STDIN_FILENO, getpid()) )
|
||
|
{
|
||
|
debug( 1,
|
||
|
L"Couldn't grab control of terminal" );
|
||
|
wperror( L"tcsetpgrp" );
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
handle_winch( 0 ); /* Set handler for window change events */
|
||
|
|
||
|
tcgetattr(0,&pager_modes); /* get the current terminal modes */
|
||
|
memcpy( &saved_modes,
|
||
|
&pager_modes,
|
||
|
sizeof(saved_modes)); /* save a copy so we can reset the terminal later */
|
||
|
|
||
|
pager_modes.c_lflag &= ~ICANON; /* turn off canonical mode */
|
||
|
pager_modes.c_lflag &= ~ECHO; /* turn off echo mode */
|
||
|
pager_modes.c_cc[VMIN]=1;
|
||
|
pager_modes.c_cc[VTIME]=0;
|
||
|
|
||
|
if( tcsetattr(0,TCSANOW,&pager_modes)) /* set the new modes */
|
||
|
{
|
||
|
wperror(L"tcsetattr");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if( setupterm( 0, STDOUT_FILENO, 0) == ERR )
|
||
|
{
|
||
|
debug( 0, L"Could not set up terminal" );
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
// writembs( tparm( eat_char, ' ', c ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
void destroy()
|
||
|
{
|
||
|
env_universal_destroy();
|
||
|
input_common_destroy();
|
||
|
del_curterm( cur_term );
|
||
|
sb_destroy( &out_buff );
|
||
|
fclose( out_file );
|
||
|
|
||
|
}
|
||
|
|
||
|
int main( int argc, char **argv )
|
||
|
{
|
||
|
int i;
|
||
|
int is_quoted=0;
|
||
|
array_list_t comp;
|
||
|
wchar_t *prefix;
|
||
|
|
||
|
|
||
|
init();
|
||
|
|
||
|
|
||
|
prefix = str2wcs( argv[2] );
|
||
|
is_quoted = strcmp( "1", argv[1] )==0;
|
||
|
|
||
|
debug( 3, L"prefix is '%ls'", prefix );
|
||
|
|
||
|
al_init( &comp );
|
||
|
|
||
|
for( i=3; i<argc; i++ )
|
||
|
{
|
||
|
al_push( &comp, str2wcs( argv[i] ) );
|
||
|
}
|
||
|
|
||
|
mangle_descriptions( &comp );
|
||
|
|
||
|
for( i = 6; i>0; i-- )
|
||
|
{
|
||
|
switch( completion_try_print( i, prefix, is_quoted, &comp ) )
|
||
|
{
|
||
|
case 0:
|
||
|
break;
|
||
|
case 1:
|
||
|
i=0;
|
||
|
break;
|
||
|
case 2:
|
||
|
i=7;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
al_foreach( &comp, (void(*)(const void *))&free );
|
||
|
al_destroy( &comp );
|
||
|
free(prefix );
|
||
|
|
||
|
fwprintf( out_file, L"%ls", (wchar_t *)out_buff.buff );
|
||
|
if( is_ca_mode )
|
||
|
writembs(exit_ca_mode);
|
||
|
|
||
|
|
||
|
destroy();
|
||
|
}
|
||
|
|