diff options
| author | Nathan Whitehorn <nwhitehorn@FreeBSD.org> | 2011-01-13 02:21:23 +0000 | 
|---|---|---|
| committer | Nathan Whitehorn <nwhitehorn@FreeBSD.org> | 2011-01-13 02:21:23 +0000 | 
| commit | fe24989830eac66b52d803c7a65ebf438100b714 (patch) | |
| tree | 31f235d6bd424e7318015827321080547d47c5ee /tailbox.c | |
Diffstat (limited to 'tailbox.c')
| -rw-r--r-- | tailbox.c | 368 | 
1 files changed, 368 insertions, 0 deletions
| diff --git a/tailbox.c b/tailbox.c new file mode 100644 index 0000000000000..51db836f471d7 --- /dev/null +++ b/tailbox.c @@ -0,0 +1,368 @@ +/* + *  $Id: tailbox.c,v 1.56 2010/04/28 20:52:20 tom Exp $ + * + * tailbox.c -- implements the tail box + * + * Copyright 2000-2009,2010 Thomas E. Dickey + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License, version 2.1 + *  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 + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to + *	Free Software Foundation, Inc. + *	51 Franklin St., Fifth Floor + *	Boston, MA 02110, USA. + * + *  An earlier version of this program lists as authors + *	Pasquale De Marco (demarco_p@abramo.it) + */ + +#include <dialog.h> +#include <dlg_keys.h> + +typedef struct { +    DIALOG_CALLBACK obj; +    WINDOW *text; +    const char **buttons; +    int hscroll; +    int old_hscroll; +    char line[MAX_LEN + 1]; +} MY_OBJ; + +/* + * Return current line of text. + */ +static char * +get_line(MY_OBJ * obj) +{ +    FILE *fp = obj->obj.input; +    int col = -(obj->hscroll); +    int j, tmpint, ch; + +    do { +	if (((ch = getc(fp)) == EOF) && !feof(fp)) +	    dlg_exiterr("Error moving file pointer in get_line()."); +	else if (!feof(fp) && (ch != '\n')) { +	    if ((ch == TAB) && (dialog_vars.tab_correct)) { +		tmpint = dialog_state.tab_len +		    - ((col + obj->hscroll) % dialog_state.tab_len); +		for (j = 0; j < tmpint; j++) { +		    if (col >= 0 && col < MAX_LEN) +			obj->line[col] = ' '; +		    ++col; +		} +	    } else { +		if (col >= 0) +		    obj->line[col] = (char) ch; +		++col; +	    } +	    if (col >= MAX_LEN) +		break; +	} +    } while (!feof(fp) && (ch != '\n')); + +    if (col < 0) +	col = 0; +    obj->line[col] = '\0'; + +    return obj->line; +} + +/* + * Print a new line of text. + */ +static void +print_line(MY_OBJ * obj, WINDOW *win, int row, int width) +{ +    int i, y, x; +    char *line = get_line(obj); + +    (void) wmove(win, row, 0);	/* move cursor to correct line */ +    (void) waddch(win, ' '); +#ifdef NCURSES_VERSION +    (void) waddnstr(win, line, MIN((int) strlen(line), width - 2)); +#else +    line[MIN((int) strlen(line), width - 2)] = '\0'; +    waddstr(win, line); +#endif + +    getyx(win, y, x); +    /* Clear 'residue' of previous line */ +    for (i = 0; i < width - x; i++) +	(void) waddch(win, ' '); +} + +/* + * Go back 'target' lines in text file.  BUFSIZ has to be in 'size_t' range. + */ +static void +last_lines(MY_OBJ * obj, int target) +{ +    FILE *fp = obj->obj.input; +    long inx; +    int count = 0; +    char buf[BUFSIZ + 1]; +    long size_to_read; +    long offset = 0; +    long fpos = 0; + +    if (fseek(fp, 0, SEEK_END) == -1 +	|| (fpos = ftell(fp)) < 0) +	dlg_exiterr("Error moving file pointer in last_lines()."); + +    if (fpos != 0) { +	++target; +	for (;;) { +	    if (fpos >= BUFSIZ) { +		size_to_read = BUFSIZ; +	    } else { +		size_to_read = fpos; +	    } +	    fpos = fpos - size_to_read; +	    if (fseek(fp, fpos, SEEK_SET) == -1) +		dlg_exiterr("Error moving file pointer in last_lines()."); +	    (void) fread(buf, (size_t) size_to_read, 1, fp); +	    if (ferror(fp)) +		dlg_exiterr("Error reading file in last_lines()."); + +	    offset += size_to_read; +	    for (inx = size_to_read - 1; inx >= 0; --inx) { +		if (buf[inx] == '\n') { +		    if (++count > target) +			break; +		    offset = inx + 1; +		} +	    } + +	    if (count > target) { +		break; +	    } else if (fpos == 0) { +		offset = 0; +		break; +	    } +	} + +	if (fseek(fp, fpos + offset, SEEK_SET) == -1) +	    dlg_exiterr("Error moving file pointer in last_lines()."); +    } +} + +/* + * Print a new page of text. + */ +static void +print_page(MY_OBJ * obj, int height, int width) +{ +    int i; + +    for (i = 0; i < height; i++) { +	print_line(obj, obj->text, i, width); +    } +    (void) wnoutrefresh(obj->text); +} + +static void +print_last_page(MY_OBJ * obj) +{ +    int high = getmaxy(obj->obj.win) - (2 * MARGIN + (obj->obj.bg_task ? 1 : 3)); +    int wide = getmaxx(obj->text); + +    last_lines(obj, high); +    print_page(obj, high, wide); +} + +static void +repaint_text(MY_OBJ * obj) +{ +    int cur_y, cur_x; + +    getyx(obj->obj.win, cur_y, cur_x); +    obj->old_hscroll = obj->hscroll; +    print_last_page(obj); +    (void) wmove(obj->obj.win, cur_y, cur_x);	/* Restore cursor position */ +    wrefresh(obj->obj.win); +} + +static bool +handle_my_getc(DIALOG_CALLBACK * cb, int ch, int fkey, int *result) +{ +    MY_OBJ *obj = (MY_OBJ *) cb; +    bool done = FALSE; + +    if (!fkey && dlg_char_to_button(ch, obj->buttons) == 0) { +	ch = DLGK_ENTER; +	fkey = TRUE; +    } + +    if (fkey) { +	switch (ch) { +	case DLGK_ENTER: +	    *result = DLG_EXIT_OK; +	    done = TRUE; +	    break; +	case DLGK_BEGIN:	/* Beginning of line */ +	    obj->hscroll = 0; +	    break; +	case DLGK_GRID_LEFT:	/* Scroll left */ +	    if (obj->hscroll > 0) { +		obj->hscroll -= 1; +	    } +	    break; +	case DLGK_GRID_RIGHT:	/* Scroll right */ +	    if (obj->hscroll < MAX_LEN) +		obj->hscroll += 1; +	    break; +	default: +	    beep(); +	    break; +	} +	if ((obj->hscroll != obj->old_hscroll)) +	    repaint_text(obj); +    } else { +	switch (ch) { +	case ERR: +	    clearerr(cb->input); +	    ch = getc(cb->input); +	    (void) ungetc(ch, cb->input); +	    if ((ch != EOF) || (obj->hscroll != obj->old_hscroll)) { +		repaint_text(obj); +	    } +	    break; +	case ESC: +	    done = TRUE; +	    *result = DLG_EXIT_ESC; +	    break; +	default: +	    beep(); +	    break; +	} +    } + +    return !done; +} + +/* + * Display text from a file in a dialog box, like in a "tail -f". + */ +int +dialog_tailbox(const char *title, const char *file, int height, int width, int bg_task) +{ +    /* *INDENT-OFF* */ +    static DLG_KEYS_BINDING binding[] = { +	ENTERKEY_BINDINGS, +	DLG_KEYS_DATA( DLGK_BEGIN,      '0' ), +	DLG_KEYS_DATA( DLGK_BEGIN,      KEY_BEG ), +	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'H' ), +	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'h' ), +	DLG_KEYS_DATA( DLGK_GRID_LEFT,  KEY_LEFT ), +	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'L' ), +	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ), +	DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ), +	END_KEYS_BINDING +    }; +    /* *INDENT-ON* */ + +#ifdef KEY_RESIZE +    int old_height = height; +    int old_width = width; +#endif +    int fkey; +    int x, y, result, thigh; +    WINDOW *dialog, *text; +    const char **buttons = 0; +    MY_OBJ *obj; +    FILE *fd; +    int min_width = 12; + +    /* Open input file for reading */ +    if ((fd = fopen(file, "rb")) == NULL) +	dlg_exiterr("Can't open input file in dialog_tailbox()."); + +#ifdef KEY_RESIZE +  retry: +#endif +    dlg_auto_sizefile(title, file, &height, &width, 2, min_width); +    dlg_print_size(height, width); +    dlg_ctl_size(height, width); + +    x = dlg_box_x_ordinate(width); +    y = dlg_box_y_ordinate(height); +    thigh = height - ((2 * MARGIN) + (bg_task ? 0 : 2)); + +    dialog = dlg_new_window(height, width, y, x); + +    dlg_mouse_setbase(x, y); + +    /* Create window for text region, used for scrolling text */ +    text = dlg_sub_window(dialog, +			  thigh, +			  width - (2 * MARGIN), +			  y + MARGIN, +			  x + MARGIN); + +    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); +    dlg_draw_bottom_box(dialog); +    dlg_draw_title(dialog, title); + +    if (!bg_task) { +	buttons = dlg_exit_label(); +	dlg_button_layout(buttons, &min_width); +	dlg_draw_buttons(dialog, height - (2 * MARGIN), 0, buttons, FALSE, +			 FALSE, width); +    } + +    (void) wmove(dialog, thigh, (MARGIN + 1)); +    (void) wnoutrefresh(dialog); + +    obj = dlg_calloc(MY_OBJ, 1); +    assert_ptr(obj, "dialog_tailbox"); + +    obj->obj.input = fd; +    obj->obj.win = dialog; +    obj->obj.handle_getc = handle_my_getc; +    obj->obj.keep_bg = bg_task && dialog_vars.cant_kill; +    obj->obj.bg_task = bg_task; +    obj->text = text; +    obj->buttons = buttons; +    dlg_add_callback(&(obj->obj)); + +    dlg_register_window(dialog, "tailbox", binding); +    dlg_register_buttons(dialog, "tailbox", buttons); + +    /* Print last page of text */ +    dlg_attr_clear(text, thigh, getmaxx(text), dialog_attr); +    repaint_text(obj); + +    if (bg_task) { +	result = DLG_EXIT_OK; +    } else { +	int ch; +	do { +	    ch = dlg_getc(dialog, &fkey); +#ifdef KEY_RESIZE +	    if (fkey && ch == KEY_RESIZE) { +		/* reset data */ +		height = old_height; +		width = old_width; +		/* repaint */ +		dlg_clear(); +		dlg_del_window(dialog); +		refresh(); +		dlg_mouse_free_regions(); +		dlg_button_layout(buttons, &min_width); +		goto retry; +	    } +#endif +	} +	while (handle_my_getc(&(obj->obj), ch, fkey, &result)); +    } +    dlg_mouse_free_regions(); +    return result; +} | 
