Пример #1
0
Файл: move.c Проект: ris21/yoda
/* Move up one page. */
void do_page_up(void)
{
    int i, skipped = 0;

    /* If there's less than a page of text left on the screen, put the
     * cursor at the beginning of the first line of the file, and then
     * update the edit window. */
    if (openfile->current->lineno == 1 || (
#ifndef NANO_TINY
	!ISSET(SOFTWRAP) &&
#endif
	openfile->current->lineno <= editwinrows - 2)) {
	do_first_line();
	return;
    }

    /* If we're not in smooth scrolling mode, put the cursor at the
     * beginning of the top line of the edit window, as Pico does. */
#ifndef NANO_TINY
    if (!ISSET(SMOOTH_SCROLL)) {
#endif
	openfile->current = openfile->edittop;
	openfile->placewewant = openfile->current_y = 0;
#ifndef NANO_TINY
    }
#endif

    for (i = editwinrows - 2; i - skipped > 0 && openfile->current !=
	openfile->fileage; i--) {
	openfile->current = openfile->current->prev;
#ifndef NANO_TINY
	if (ISSET(SOFTWRAP) && openfile->current) {
	    skipped += strlenpt(openfile->current->data) / COLS;
#ifdef DEBUG
	    fprintf(stderr, "do_page_up: i = %d, skipped = %d based on line %ld len %lu\n",
			i, skipped, (long)openfile->current->lineno, (unsigned long)strlenpt(openfile->current->data));
#endif
	}
#endif
    }

    openfile->current_x = actual_x(openfile->current->data,
	openfile->placewewant);

#ifdef DEBUG
    fprintf(stderr, "do_page_up: openfile->current->lineno = %lu, skipped = %d\n",
	(unsigned long)openfile->current->lineno, skipped);
#endif

    /* Scroll the edit window up a page. */
    edit_update(NONE);
}
Пример #2
0
/* Move right one character. */
void do_right(void)
{
    size_t was_column = xplustabs();

    assert(openfile->current_x <= strlen(openfile->current->data));

    if (openfile->current->data[openfile->current_x] != '\0')
	openfile->current_x = move_mbright(openfile->current->data,
						openfile->current_x);
    else if (openfile->current != openfile->filebot) {
	openfile->current_x = 0;
#ifndef NANO_TINY
	if (ISSET(SOFTWRAP))
	    openfile->current_y -= strlenpt(openfile->current->data) / editwincols;
#endif
    }

    openfile->placewewant = xplustabs();

    if (need_horizontal_scroll(was_column, openfile->placewewant))
	update_line(openfile->current, openfile->current_x);

    if (openfile->current_x == 0)
	do_down_void();
    else
	ensure_line_is_visible();
}
Пример #3
0
/* Handle a mouse click on the statusbar prompt or the shortcut list. */
int do_statusbar_mouse(void)
{
    int mouse_x, mouse_y;
    int retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);

    /* We can click on the statusbar window text to move the cursor. */
    if (retval == 0 && wmouse_trafo(bottomwin, &mouse_y, &mouse_x,
	FALSE)) {
	size_t start_col;

	assert(prompt != NULL);

	start_col = strlenpt(prompt) + 2;

	/* Move to where the click occurred. */
	if (mouse_x >= start_col && mouse_y == 0) {
	    size_t pww_save = statusbar_pww;

	    statusbar_x = actual_x(answer,
			get_statusbar_page_start(start_col, start_col +
			statusbar_xplustabs()) + mouse_x - start_col);
	    statusbar_pww = statusbar_xplustabs();

	    if (need_statusbar_update(pww_save))
		update_statusbar_line(answer, statusbar_x);
	}
    }

    return retval;
}
Пример #4
0
/* Move edittop to put it in range of current, keeping current in the
 * same place.  location determines how we move it: if it's CENTER, we
 * center current, and if it's NONE, we put current current_y lines
 * below edittop. */
void edit_update(UpdateType location)
{
	filestruct *foo = openfile->current;
	int goal;

	/* If location is CENTER, we move edittop up (editwinrows / 2)
	 * lines.  This puts current at the center of the screen.  If
	 * location is NONE, we move edittop up current_y lines if current_y
	 * is in range of the screen, 0 lines if current_y is less than 0,
	 * or (editwinrows - 1) lines if current_y is greater than
	 * (editwinrows - 1).  This puts current at the same place on the
	 * screen as before, or at the top or bottom of the screen if
	 * edittop is beyond either. */
	if (location == CENTER) {
		goal = editwinrows / 2;
	} else {
		goal = openfile->current_y;

		/* Limit goal to (editwinrows - 1) lines maximum. */
		if (goal > editwinrows - 1) {
			goal = editwinrows - 1;
		}
	}

	for (; goal > 0 && foo->prev != NULL; goal--) {
		foo = foo->prev;
		if (ISSET(SOFTWRAP) && foo) {
			goal -= strlenpt(foo->data) / COLS;
		}
	}
	openfile->edittop = foo;
	DEBUG_LOG("edit_udpate(), setting edittop to lineno " << openfile->edittop->lineno);
	compute_maxrows();
	edit_refresh_needed = true;
}
Пример #5
0
/* Repaint the statusbar when getting a character in
 * get_prompt_string().  The statusbar text line will be displayed
 * starting with curranswer[index]. */
void update_statusbar_line(const char *curranswer, size_t index)
{
    size_t start_col, page_start;
    char *expanded;

    assert(prompt != NULL && index <= strlen(curranswer));

    start_col = strlenpt(prompt) + 2;
    index = strnlenpt(curranswer, index);
    page_start = get_statusbar_page_start(start_col, start_col + index);

    if (interface_color_pair[TITLE_BAR].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[TITLE_BAR].pairnum);

    blank_statusbar();

    mvwaddnstr(bottomwin, 0, 0, prompt, actual_x(prompt, COLS - 2));
    waddch(bottomwin, ':');
    waddch(bottomwin, (page_start == 0) ? ' ' : '$');

    expanded = display_string(curranswer, page_start, COLS - start_col -
	1, FALSE);
    waddstr(bottomwin, expanded);
    free(expanded);

    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[TITLE_BAR].pairnum);
    statusbar_pww = statusbar_xplustabs();
    reset_statusbar_cursor();
    wnoutrefresh(bottomwin);
}
Пример #6
0
/* Reset current_y, based on the position of current, and put the cursor
 * in the edit window at (current_y, current_x). */
void reset_cursor(void)
{
	size_t xpt;
	/* If we haven't opened any files yet, put the cursor in the top
	 * left corner of the edit window and get out. */
	if (openfiles.size() == 0) {
		wmove(edit, 0, 0);
		return;
	}

	xpt = xplustabs();

	if (ISSET(SOFTWRAP)) {
		filestruct *tmp;
		openfile->current_y = 0;

		for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next) {
			openfile->current_y += 1 + strlenpt(tmp->data) / COLS;
		}

		openfile->current_y += xplustabs() / COLS;
		if (openfile->current_y < editwinrows) {
			wmove(edit, openfile->current_y, xpt % COLS);
		}
	} else {
		openfile->current_y = openfile->current->lineno - openfile->edittop->lineno;

		if (openfile->current_y < editwinrows) {
			wmove(edit, openfile->current_y, xpt - get_page_start(xpt));
		}
	}
}
Пример #7
0
/* Return TRUE if we need an update after moving the cursor, and FALSE
 * otherwise.  We need an update if pww_save and statusbar_pww are on
 * different pages. */
bool need_statusbar_update(size_t pww_save)
{
    size_t start_col = strlenpt(prompt) + 2;

    return get_statusbar_page_start(start_col, start_col + pww_save) !=
	get_statusbar_page_start(start_col, start_col + statusbar_pww);
}
Пример #8
0
/* Put the cursor in the statusbar prompt at statusbar_x. */
void reset_statusbar_cursor(void)
{
    size_t start_col = strlenpt(prompt) + 2;
    size_t xpt = statusbar_xplustabs();

    wmove(bottomwin, 0, start_col + xpt -
	get_statusbar_page_start(start_col, start_col + xpt));
}
Пример #9
0
/* Make sure that the current line, when it is partially scrolled off the
 * screen in softwrap mode, is scrolled fully into view. */
void ensure_line_is_visible(void)
{
#ifndef NANO_TINY
    if (ISSET(SOFTWRAP) && strlenpt(openfile->current->data) / editwincols +
				openfile->current_y >= editwinrows) {
	adjust_viewport(ISSET(SMOOTH_SCROLL) ? FLOWING : CENTERING);
	refresh_needed = TRUE;
    }
#endif
}
Пример #10
0
/* Move up one page. */
void do_page_up(void)
{
    int i, mustmove, skipped = 0;

    /* If the cursor is less than a page away from the top of the file,
     * put it at the beginning of the first line. */
    if (openfile->current->lineno == 1 || (!ISSET(SOFTWRAP) &&
		openfile->current->lineno <= editwinrows - 2)) {
	do_first_line();
	return;
    }

    /* If we're not in smooth scrolling mode, put the cursor at the
     * beginning of the top line of the edit window, as Pico does. */
    if (!ISSET(SMOOTH_SCROLL)) {
	openfile->current = openfile->edittop;
	openfile->placewewant = openfile->current_y = 0;
    }

    mustmove = (editwinrows < 3) ? 1 : editwinrows - 2;

    for (i = mustmove; i - skipped > 0 && openfile->current != openfile->fileage; i--) {
	openfile->current = openfile->current->prev;
#ifndef NANO_TINY
	if (ISSET(SOFTWRAP) && openfile->current) {
	    skipped += strlenpt(openfile->current->data) / editwincols;
#ifdef DEBUG
	    fprintf(stderr, "paging up: i = %d, skipped = %d based on line %ld len %lu\n",
			i, skipped, (long)openfile->current->lineno, (unsigned long)strlenpt(openfile->current->data));
#endif
	}
#endif
    }

    openfile->current_x = actual_x(openfile->current->data,
					openfile->placewewant);

    /* Scroll the edit window up a page. */
    adjust_viewport(STATIONARY);
    refresh_needed = TRUE;
}
Пример #11
0
/* Display a message on the statusbar, and set disable_cursorpos to
 * true, so that the message won't be immediately overwritten if
 * constant cursor position display is on. */
void statusbar(const char *msg, ...)
{
	va_list ap;
	char *bar, *foo;
	size_t start_x;
	bool old_whitespace;

	va_start(ap, msg);

	/* Curses mode is turned off.  If we use wmove() now, it will muck
	 * up the terminal settings.  So we just use vfprintf(). */
	if (isendwin()) {
		vfprintf(stderr, msg, ap);
		va_end(ap);
		return;
	}

	blank_statusbar();

	old_whitespace = ISSET(WHITESPACE_DISPLAY);
	UNSET(WHITESPACE_DISPLAY);
	bar = charalloc(mb_cur_max() * (COLS - 3));
	vsnprintf(bar, mb_cur_max() * (COLS - 3), msg, ap);
	va_end(ap);
	foo = display_string(bar, 0, COLS - 4, false);
	free(bar);
	if (old_whitespace) {
		SET(WHITESPACE_DISPLAY);
	}
	start_x = (COLS - strlenpt(foo) - 4) / 2;

	wmove(bottomwin, 0, start_x);
	set_color(bottomwin, interface_colors[STATUS_BAR]);
	waddstr(bottomwin, "[ ");
	waddstr(bottomwin, foo);
	free(foo);
	waddstr(bottomwin, " ]");
	clear_color(bottomwin, interface_colors[STATUS_BAR]);
	wnoutrefresh(bottomwin);
	reset_cursor();
	wnoutrefresh(edit);
	/* Leave the cursor at its position in the edit window, not in
	 * the statusbar. */

	disable_cursorpos = true;

	/* If we're doing quick statusbar blanking, and constant cursor
	 * position display is off, blank the statusbar after only one
	 * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
	 * Pico does. */
	statusblank = ISSET(QUICK_BLANK) && !ISSET(CONST_UPDATE) ? 1 : 26;
}
Пример #12
0
/* If constant is true, we display the current cursor position only if
 * disable_cursorpos is false.  Otherwise, we display it
 * unconditionally and set disable_cursorpos to false.  If constant is
 * true and disable_cursorpos is true, we also set disable_cursorpos to
 * false, so that we leave the current statusbar alone this time, and
 * display the current cursor position next time. */
void do_cursorpos(bool constant)
{
	filestruct *f;
	char c;
	size_t i, cur_xpt = xplustabs() + 1;
	size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
	int linepct, colpct, charpct;

	assert(openfile->fileage != NULL && openfile->current != NULL);

	f = openfile->current->next;
	c = openfile->current->data[openfile->current_x];

	openfile->current->next = NULL;
	openfile->current->data[openfile->current_x] = '\0';

	i = get_totsize(openfile->fileage, openfile->current);

	openfile->current->data[openfile->current_x] = c;
	openfile->current->next = f;

	if (constant && disable_cursorpos) {
		disable_cursorpos = false;
		return;
	}

	/* Display the current cursor position on the statusbar, and set
	 * disable_cursorpos to false. */
	linepct = 100 * openfile->current->lineno / openfile->filebot->lineno;
	colpct = 100 * cur_xpt / cur_lenpt;
	charpct = (openfile->totsize == 0) ? 0 : 100 * i / openfile->totsize;

	statusbar(
	    _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
	    (long)openfile->current->lineno,
	    (long)openfile->filebot->lineno, linepct,
	    (unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
	    (unsigned long)i, (unsigned long)openfile->totsize, charpct);

	disable_cursorpos = false;
}
Пример #13
0
/* When edittop changes, try and figure out how many lines
 * we really have to work with (i.e. set maxrows)
 */
void compute_maxrows(void)
{
	int n;
	filestruct *foo = openfile->edittop;

	if (!ISSET(SOFTWRAP)) {
		maxrows = editwinrows;
		return;
	}

	maxrows = 0;
	for (n = 0; n < editwinrows && foo; n++) {
		maxrows ++;
		n += strlenpt(foo->data) / COLS;
		foo = foo->next;
	}

	if (n < editwinrows) {
		maxrows += editwinrows - n;
	}

	DEBUG_LOG("compute_maxrows(): maxrows = " << maxrows);
}
Пример #14
0
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
void do_replace_highlight(bool highlight, const char *word)
{
	size_t y = xplustabs(), word_len = strlenpt(word);

	y = get_page_start(y) + COLS - y;
	/* Now y is the number of columns that we can display on this
	 * line. */

	assert(y > 0);

	if (word_len > y) {
		y--;
	}

	reset_cursor();
	wnoutrefresh(edit);

	if (highlight) {
		wattron(edit, highlight_attribute);
	}

	/* This is so we can show zero-length matches. */
	if (word_len == 0) {
		waddch(edit, ' ');
	} else {
		waddnstr(edit, word, actual_x(word, y));
	}

	if (word_len > y) {
		waddch(edit, '$');
	}

	if (highlight) {
		wattroff(edit, highlight_attribute);
	}
}
Пример #15
0
/* Just update one line in the edit buffer.  This is basically a wrapper
 * for edit_draw().  The line will be displayed starting with
 * fileptr->data[index].  Likely arguments are current_x or zero.
 * Returns: Number of additiona lines consumed (needed for SOFTWRAP)
 */
int update_line(filestruct *fileptr, size_t index)
{
	int line = 0;
	int extralinesused = 0;
	/* The line in the edit window that we want to update. */
	char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
	size_t page_start;
	filestruct *tmp;

	assert(fileptr != NULL);

	if (ISSET(SOFTWRAP)) {
		for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next) {
			line += 1 + (strlenpt(tmp->data) / COLS);
		}
	} else {
		line = fileptr->lineno - openfile->edittop->lineno;
	}

	if (line < 0 || line >= editwinrows) {
		return 1;
	}

	/* First, blank out the line. */
	blank_line(edit, line, 0, COLS);

	/* Next, convert variables that index the line to their equivalent
	 * positions in the expanded line. */
	if (ISSET(SOFTWRAP)) {
		index = 0;
	} else {
		index = strnlenpt(fileptr->data, index);
	}
	page_start = get_page_start(index);

	/* Expand the line, replacing tabs with spaces, and control
	 * characters with their displayed forms. */
	converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP));

#ifdef DEBUG
	if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2) {
		fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
	}
#endif


	/* Paint the line. */
	edit_draw(fileptr, converted, line, page_start);
	free(converted);

	if (!ISSET(SOFTWRAP)) {
		if (page_start > 0) {
			mvwaddch(edit, line, 0, '$');
		}
		if (strlenpt(fileptr->data) > page_start + COLS) {
			mvwaddch(edit, line, COLS - 1, '$');
		}
	} else {
		int full_length = strlenpt(fileptr->data);
		for (index += COLS; index <= full_length && line < editwinrows; index += COLS) {
			line++;
			DEBUG_LOG("update_line(): Softwrap code, moving to " << line << " index " << index);
			blank_line(edit, line, 0, COLS);

			/* Expand the line, replacing tabs with spaces, and control
			 * characters with their displayed forms. */
			converted = display_string(fileptr->data, index, COLS, !ISSET(SOFTWRAP));
			if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2) {
				DEBUG_LOG("update_line(): converted(2) line == " << converted);
			}

			/* Paint the line. */
			edit_draw(fileptr, converted, line, index);
			free(converted);
			extralinesused++;
		}
	}
	return extralinesused;
}
Пример #16
0
/* Scroll the edit window in the given direction and the given number
 * of lines, and draw new lines on the blank lines left after the
 * scrolling.  direction is the direction to scroll, either UPWARD or
 * DOWNWARD, and nlines is the number of lines to scroll.  We change
 * edittop, and assume that current and current_x are up to date.  We
 * also assume that scrollok(edit) is false. */
void edit_scroll(ScrollDir direction, ssize_t nlines)
{
	filestruct *foo;
	ssize_t i;
	bool do_redraw = need_screen_update(0);

	/* Don't bother scrolling less than one line. */
	if (nlines < 1) {
		return;
	}

	/* Part 1: nlines is the number of lines we're going to scroll the
	 * text of the edit window. */

	/* Move the top line of the edit window up or down (depending on the
	 * value of direction) nlines lines, or as many lines as we can if
	 * there are fewer than nlines lines available. */
	for (i = nlines; i > 0; i--) {
		if (direction == UPWARD) {
			if (openfile->edittop == openfile->fileage) {
				break;
			}
			openfile->edittop = openfile->edittop->prev;
		} else {
			if (openfile->edittop == openfile->filebot) {
				break;
			}
			openfile->edittop = openfile->edittop->next;
		}
		/* Don't over-scroll on long lines */
		if (ISSET(SOFTWRAP) && (direction == UPWARD)) {
			ssize_t len = strlenpt(openfile->edittop->data) / COLS;
			i -= len;
			if (len > 0) {
				do_redraw = true;
			}
		}
	}

	/* Limit nlines to the number of lines we could scroll. */
	nlines -= i;

	/* Don't bother scrolling zero lines or more than the number of
	 * lines in the edit window minus one; in both cases, get out, and
	 * call edit_refresh() beforehand if we need to. */
	if (nlines == 0 || do_redraw || nlines >= editwinrows) {
		if (do_redraw || nlines >= editwinrows) {
			edit_refresh_needed = true;
		}
		return;
	}

	/* Scroll the text of the edit window up or down nlines lines,
	 * depending on the value of direction. */
	scrollok(edit, true);
	wscrl(edit, (direction == UPWARD) ? -nlines : nlines);
	scrollok(edit, false);

	/* Part 2: nlines is the number of lines in the scrolled region of
	 * the edit window that we need to draw. */

	/* If the top or bottom line of the file is now visible in the edit
	 * window, we need to draw the entire edit window. */
	if ((direction == UPWARD && openfile->edittop == openfile->fileage)
		|| (direction == DOWNWARD && openfile->edittop->lineno + editwinrows - 1 >= openfile->filebot->lineno)) {
		nlines = editwinrows;
	}

	/* If the scrolled region contains only one line, and the line
	 * before it is visible in the edit window, we need to draw it too.
	 * If the scrolled region contains more than one line, and the lines
	 * before and after the scrolled region are visible in the edit
	 * window, we need to draw them too. */
	nlines += (nlines == 1) ? 1 : 2;

	if (nlines > editwinrows) {
		nlines = editwinrows;
	}

	/* If we scrolled up, we're on the line before the scrolled region. */
	foo = openfile->edittop;

	/* If we scrolled down, move down to the line before the scrolled region. */
	if (direction == DOWNWARD) {
		for (i = editwinrows - nlines; i > 0 && foo != NULL; i--) {
			foo = foo->next;
		}
	}

	/* Draw new lines on any blank lines before or inside the scrolled
	 * region.  If we scrolled down and we're on the top line, or if we
	 * scrolled up and we're on the bottom line, the line won't be
	 * blank, so we don't need to draw it unless the mark is on or we're
	 * not on the first page. */
	for (i = nlines; i > 0 && foo != NULL; i--) {
		if ((i == nlines && direction == DOWNWARD) || (i == 1 && direction == UPWARD)) {
			if (do_redraw)
				update_line(foo, (foo == openfile->current) ? openfile->current_x : 0);
		} else {
			update_line(foo, (foo == openfile->current) ? openfile->current_x : 0);
		}
		foo = foo->next;
	}

	compute_maxrows();
}
Пример #17
0
Файл: move.c Проект: sria91/nano
/* If scroll_only is FALSE, move down one line.  If scroll_only is TRUE,
 * scroll down one line without scrolling the cursor. */
void do_down(
#ifndef NANO_TINY
    bool scroll_only
#else
    void
#endif
)
{
#ifndef NANO_TINY
    int amount = 0, enough;
    filestruct *topline;
#endif

    /* If we're at the bottom of the file, get out. */
    if (openfile->current == openfile->filebot || !openfile->current->next)
        return;

    assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno);

    /* Move the current line of the edit window down. */
    openfile->current = openfile->current->next;
    openfile->current_x = actual_x(openfile->current->data,
                                   openfile->placewewant);

#ifndef NANO_TINY
    if (ISSET(SOFTWRAP)) {
        /* Compute the amount to scroll. */
        amount = (strlenpt(openfile->current->data) / COLS + openfile->current_y + 2
                  + strlenpt(openfile->current->prev->data) / COLS - editwinrows);
        topline = openfile->edittop;
        /* Reduce the amount when there are overlong lines at the top. */
        for (enough = 1; enough < amount; enough++) {
            if (amount <= strlenpt(topline->data) / COLS) {
                amount = enough;
                break;
            }
            amount -= strlenpt(topline->data) / COLS;
            topline = topline->next;
        }
    }
#endif

    /* If scroll_only is FALSE and if we're on the last line of the
     * edit window, scroll the edit window down one line if we're in
     * smooth scrolling mode, or down half a page if we're not.  If
     * scroll_only is TRUE, scroll the edit window down one line
     * unconditionally. */
    if (openfile->current_y == editwinrows - 1
#ifndef NANO_TINY
            || amount > 0 || scroll_only
#endif
       ) {
#ifndef NANO_TINY
        if (amount < 1 || scroll_only)
            amount = 1;
#endif
        edit_scroll(DOWNWARD,
#ifndef NANO_TINY
                    (ISSET(SMOOTH_SCROLL) || scroll_only) ? amount :
#endif
                    editwinrows / 2 + 1);
        edit_refresh_needed = TRUE;
    }
    /* If we're above the last line of the edit window, update the line
     * we were on before and the line we're on now.  The former needs to
     * be redrawn if we're not on the first page, and the latter needs
     * to be drawn unconditionally. */
    if (openfile->current_y < editwinrows - 1
#ifndef NANO_TINY
            || ISSET(SOFTWRAP)
#endif
       ) {
        if (need_screen_update(0))
            update_line(openfile->current->prev, 0);
        update_line(openfile->current, openfile->current_x);
    }
}
Пример #18
0
char *display_string(const char *buf, size_t start_col, size_t len, bool dollars)
{
	size_t start_index;
	/* Index in buf of the first character shown. */
	size_t column;
	/* Screen column that start_index corresponds to. */
	size_t alloc_len;
	/* The length of memory allocated for converted. */
	char *converted;
	/* The string we return. */
	size_t index;
	/* Current position in converted. */
	char *buf_mb;
	int buf_mb_len;

	/* If dollars is true, make room for the "$" at the end of the
	 * line. */
	if (dollars && len > 0 && strlenpt(buf) > start_col + len) {
		len--;
	}

	if (len == 0) {
		return mallocstrcpy(NULL, "");
	}

	buf_mb = charalloc(mb_cur_max());

	start_index = actual_x(buf, start_col);
	column = strnlenpt(buf, start_index);

	assert(column <= start_col);

	/* Make sure there's enough room for the initial character, whether
	 * it's a multibyte control character, a non-control multibyte
	 * character, a tab character, or a null terminator.  Rationale:
	 *
	 * multibyte control character followed by a null terminator:
	 *     1 byte ('^') + mb_cur_max() bytes + 1 byte ('\0')
	 * multibyte non-control character followed by a null terminator:
	 *     mb_cur_max() bytes + 1 byte ('\0')
	 * tab character followed by a null terminator:
	 *     mb_cur_max() bytes + (tabsize - 1) bytes + 1 byte ('\0')
	 *
	 * Since tabsize has a minimum value of 1, it can substitute for 1
	 * byte above. */
	alloc_len = (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
	converted = charalloc(alloc_len);

	index = 0;

	if (buf[start_index] != '\0' && buf[start_index] != '\t' && (column < start_col || (dollars && column > 0))) {
		/* We don't display all of buf[start_index] since it starts to
		 * the left of the screen. */
		buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);

		if (is_cntrl_mbchar(buf_mb)) {
			if (column < start_col) {
				char *ctrl_buf_mb = charalloc(mb_cur_max());
				int ctrl_buf_mb_len, i;

				ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb, &ctrl_buf_mb_len);

				for (i = 0; i < ctrl_buf_mb_len; i++) {
					converted[index++] = ctrl_buf_mb[i];
				}

				start_col += mbwidth(ctrl_buf_mb);

				free(ctrl_buf_mb);

				start_index += buf_mb_len;
			}
		} else if (using_utf8() && mbwidth(buf_mb) == 2) {
			if (column >= start_col) {
				converted[index++] = ' ';
				start_col++;
			}

			converted[index++] = ' ';
			start_col++;

			start_index += buf_mb_len;
		}
	}

	while (buf[start_index] != '\0') {
		buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);

		/* Make sure there's enough room for the next character, whether
		 * it's a multibyte control character, a non-control multibyte
		 * character, a tab character, or a null terminator. */
		if (index + mb_cur_max() + tabsize + 1 >= alloc_len - 1) {
			alloc_len += (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
			converted = charealloc(converted, alloc_len);
		}

		/* If buf contains a tab character, interpret it. */
		if (*buf_mb == '\t') {
			if (ISSET(WHITESPACE_DISPLAY)) {
				int i;

				for (i = 0; i < whitespace_len[0]; i++) {
					converted[index++] = whitespace[i];
				}
			} else {
				converted[index++] = ' ';
			}
			start_col++;
			while (start_col % tabsize != 0) {
				converted[index++] = ' ';
				start_col++;
			}
		} else if (is_cntrl_mbchar(buf_mb)) {
			/* If buf contains a control character, interpret it. */
			char *ctrl_buf_mb = charalloc(mb_cur_max());
			int ctrl_buf_mb_len, i;

			converted[index++] = '^';
			start_col++;

			ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb, &ctrl_buf_mb_len);

			for (i = 0; i < ctrl_buf_mb_len; i++) {
				converted[index++] = ctrl_buf_mb[i];
			}

			start_col += mbwidth(ctrl_buf_mb);

			free(ctrl_buf_mb);
			/* If buf contains a space character, interpret it. */
		} else if (*buf_mb == ' ') {
			if (ISSET(WHITESPACE_DISPLAY)) {
				int i;

				for (i = whitespace_len[0]; i < whitespace_len[0] + whitespace_len[1]; i++) {
					converted[index++] = whitespace[i];
				}
			} else {
				converted[index++] = ' ';
			}
			start_col++;
		} else {
			/* If buf contains a non-control character, interpret it.  If buf
			 * contains an invalid multibyte sequence, display it as such. */
			char *nctrl_buf_mb = charalloc(mb_cur_max());
			int nctrl_buf_mb_len, i;

			/* Make sure an invalid sequence-starter byte is properly
			 * terminated, so that it doesn't pick up lingering bytes
			 * of any previous content. */
			null_at(&buf_mb, buf_mb_len);

			nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb, &nctrl_buf_mb_len);

			for (i = 0; i < nctrl_buf_mb_len; i++) {
				converted[index++] = nctrl_buf_mb[i];
			}

			start_col += mbwidth(nctrl_buf_mb);

			free(nctrl_buf_mb);
		}

		start_index += buf_mb_len;
	}

	free(buf_mb);

	assert(alloc_len >= index + 1);

	/* Null-terminate converted. */
	converted[index] = '\0';

	/* Make sure converted takes up no more than len columns. */
	index = actual_x(converted, len);
	null_at(&converted, index);

	return converted;
}
Пример #19
0
/* Set up the system variables for a search or replace.  If use_answer
 * is TRUE, only set backupstring to answer.  Return -2 to run the
 * opposite program (search -> replace, replace -> search), return -1 if
 * the search should be canceled (due to Cancel, a blank search string,
 * Go to Line, or a failed regcomp()), return 0 on success, and return 1
 * on rerun calling program.
 *
 * replacing is TRUE if we call from do_replace(), and FALSE if called
 * from do_search(). */
int search_init(bool replacing, bool use_answer)
{
    int i = 0;
    char *buf;
    static char *backupstring = NULL;
	/* The search string we'll be using. */

    /* If backupstring doesn't exist, initialize it to "". */
    if (backupstring == NULL)
	backupstring = mallocstrcpy(NULL, "");

    /* If use_answer is TRUE, set backupstring to answer and get out. */
    if (use_answer) {
	backupstring = mallocstrcpy(backupstring, answer);
	return 0;
    }

    /* We display the search prompt below.  If the user types a partial
     * search string and then Replace or a toggle, we will return to
     * do_search() or do_replace() and be called again.  In that case,
     * we should put the same search string back up. */

    focusing = TRUE;

    if (last_search[0] != '\0') {
	char *disp = display_string(last_search, 0, COLS / 3, FALSE);

	buf = charalloc(strlen(disp) + 7);
	/* We use (COLS / 3) here because we need to see more on the line. */
	sprintf(buf, " [%s%s]", disp,
		(strlenpt(last_search) > COLS / 3) ? "..." : "");
	free(disp);
    } else
	buf = mallocstrcpy(NULL, "");

    /* This is now one simple call.  It just does a lot. */
    i = do_prompt(FALSE,
#ifndef DISABLE_TABCOMP
	TRUE,
#endif
	replacing ? MREPLACE : MWHEREIS, backupstring,
#ifndef DISABLE_HISTORIES
	&search_history,
#endif
	/* TRANSLATORS: This is the main search prompt. */
	edit_refresh, "%s%s%s%s%s%s", _("Search"),
#ifndef NANO_TINY
	/* TRANSLATORS: The next three strings are modifiers of the search prompt. */
	ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") :
#endif
	"",
#ifdef HAVE_REGEX_H
	ISSET(USE_REGEXP) ? _(" [Regexp]") :
#endif
	"",
#ifndef NANO_TINY
	ISSET(BACKWARDS_SEARCH) ? _(" [Backwards]") :
#endif
	"", replacing ?
#ifndef NANO_TINY
	/* TRANSLATORS: The next two strings are modifiers of the search prompt. */
	openfile->mark_set ? _(" (to replace) in selection") :
#endif
	_(" (to replace)") : "", buf);

    fflush(stderr);

    /* Release buf now that we don't need it anymore. */
    free(buf);

    free(backupstring);
    backupstring = NULL;

    /* Cancel any search, or just return with no previous search. */
    if (i == -1 || (i < 0 && *last_search == '\0') || (!replacing &&
	i == 0 && *answer == '\0')) {
	statusbar(_("Cancelled"));
	return -1;
    } else {
	functionptrtype func = func_from_key(&i);

	if (i == -2 || i == 0 ) {
#ifdef HAVE_REGEX_H
		/* Use last_search if answer is an empty string, or
		 * answer if it isn't. */
		if (ISSET(USE_REGEXP) && !regexp_init((i == -2) ?
			last_search : answer))
		    return -1;
#endif
		;
#ifndef NANO_TINY
	} else if (func == case_sens_void) {
		TOGGLE(CASE_SENSITIVE);
		backupstring = mallocstrcpy(backupstring, answer);
		return 1;
	} else if (func == backwards_void) {
		TOGGLE(BACKWARDS_SEARCH);
		backupstring = mallocstrcpy(backupstring, answer);
		return 1;
#endif
#ifdef HAVE_REGEX_H
	} else if (func == regexp_void) {
		TOGGLE(USE_REGEXP);
		backupstring = mallocstrcpy(backupstring, answer);
		return 1;
#endif
	} else if (func == do_replace || func == flip_replace_void) {
		backupstring = mallocstrcpy(backupstring, answer);
		return -2;	/* Call the opposite search function. */
	} else if (func == do_gotolinecolumn_void) {
		do_gotolinecolumn(openfile->current->lineno,
			openfile->placewewant + 1, TRUE, TRUE);
				/* Put answer up on the statusbar and
				 * fall through. */
		return 3;
	} else {
		return -1;
	}
    }

    return 0;
}
Пример #20
0
/* Parse the rcfile, once it has been opened successfully at rcstream,
 * and close it afterwards.  If syntax_only is TRUE, only allow the file
 * to contain color syntax commands: syntax, color, and icolor. */
void parse_rcfile(FILE *rcstream
#ifdef ENABLE_COLOR
	, bool syntax_only
#endif
	)
{
    char *buf = NULL;
    ssize_t len;
    size_t n;

    while ((len = getline(&buf, &n, rcstream)) > 0) {
	char *ptr, *keyword, *option;
	int set = 0;
	size_t i;

	/* Ignore the newline. */
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';

	lineno++;
	ptr = buf;
	while (isblank(*ptr))
	    ptr++;

	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
	    continue;

	/* Otherwise, skip to the next space. */
	keyword = ptr;
	ptr = parse_next_word(ptr);

	/* Try to parse the keyword. */
	if (strcasecmp(keyword, "set") == 0) {
#ifdef ENABLE_COLOR
	    if (syntax_only)
		rcfile_error(
			N_("Command \"%s\" not allowed in included file"),
			keyword);
	    else
#endif
		set = 1;
	} else if (strcasecmp(keyword, "unset") == 0) {
#ifdef ENABLE_COLOR
	    if (syntax_only)
		rcfile_error(
			N_("Command \"%s\" not allowed in included file"),
			keyword);
	    else
#endif
		set = -1;
	}
#ifdef ENABLE_COLOR
	else if (strcasecmp(keyword, "include") == 0) {
	    if (syntax_only)
		rcfile_error(
			N_("Command \"%s\" not allowed in included file"),
			keyword);
	    else
		parse_include(ptr);
	} else if (strcasecmp(keyword, "syntax") == 0) {
	    if (endsyntax != NULL && endcolor == NULL)
		rcfile_error(N_("Syntax \"%s\" has no color commands"),
			endsyntax->desc);
	    parse_syntax(ptr);
	} else if (strcasecmp(keyword, "header") == 0)
	    parse_headers(ptr);
	else if (strcasecmp(keyword, "color") == 0)
	    parse_colors(ptr, FALSE);
	else if (strcasecmp(keyword, "icolor") == 0)
	    parse_colors(ptr, TRUE);
	else if (strcasecmp(keyword, "bind") == 0)
	    parse_keybinding(ptr);
#endif /* ENABLE_COLOR */
	else
	    rcfile_error(N_("Command \"%s\" not understood"), keyword);

	if (set == 0)
	    continue;

	if (*ptr == '\0') {
	    rcfile_error(N_("Missing flag"));
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

	for (i = 0; rcopts[i].name != NULL; i++) {
	    if (strcasecmp(option, rcopts[i].name) == 0) {
#ifdef DEBUG
		fprintf(stderr, "parse_rcfile(): name = \"%s\"\n", rcopts[i].name);
#endif
		if (set == 1) {
		    if (rcopts[i].flag != 0)
			/* This option has a flag, so it doesn't take an
			 * argument. */
			SET(rcopts[i].flag);
		    else {
			/* This option doesn't have a flag, so it takes
			 * an argument. */
			if (*ptr == '\0') {
			    rcfile_error(
				N_("Option \"%s\" requires an argument"),
				rcopts[i].name);
			    break;
			}
			option = ptr;
			if (*option == '"')
			    option++;
			ptr = parse_argument(ptr);

			option = mallocstrcpy(NULL, option);
#ifdef DEBUG
			fprintf(stderr, "option = \"%s\"\n", option);
#endif

			/* Make sure option is a valid multibyte
			 * string. */
			if (!is_valid_mbstring(option)) {
			    rcfile_error(
				N_("Option is not a valid multibyte string"));
			    break;
			}

#ifndef DISABLE_OPERATINGDIR
			if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
			    operating_dir = option;
			else
#endif
#ifndef DISABLE_WRAPJUSTIFY
			if (strcasecmp(rcopts[i].name, "fill") == 0) {
			    if (!parse_num(option, &wrap_at)) {
				rcfile_error(
					N_("Requested fill size \"%s\" is invalid"),
					option);
				wrap_at = -CHARS_FROM_EOL;
			    } else
				free(option);
			} else
#endif
#ifndef NANO_TINY
			if (strcasecmp(rcopts[i].name,
				"matchbrackets") == 0) {
			    matchbrackets = option;
			    if (has_blank_mbchars(matchbrackets)) {
				rcfile_error(
					N_("Non-blank characters required"));
				free(matchbrackets);
				matchbrackets = NULL;
			    }
			} else if (strcasecmp(rcopts[i].name,
				"whitespace") == 0) {
			    whitespace = option;
			    if (mbstrlen(whitespace) != 2 ||
				strlenpt(whitespace) != 2) {
				rcfile_error(
					N_("Two single-column characters required"));
				free(whitespace);
				whitespace = NULL;
			    } else {
				whitespace_len[0] =
					parse_mbchar(whitespace, NULL,
					NULL);
				whitespace_len[1] =
					parse_mbchar(whitespace +
					whitespace_len[0], NULL, NULL);
			    }
			} else
#endif
#ifndef DISABLE_JUSTIFY
			if (strcasecmp(rcopts[i].name, "punct") == 0) {
			    punct = option;
			    if (has_blank_mbchars(punct)) {
				rcfile_error(
					N_("Non-blank characters required"));
				free(punct);
				punct = NULL;
			    }
			} else if (strcasecmp(rcopts[i].name,
				"brackets") == 0) {
			    brackets = option;
			    if (has_blank_mbchars(brackets)) {
				rcfile_error(
					N_("Non-blank characters required"));
				free(brackets);
				brackets = NULL;
			    }
			} else if (strcasecmp(rcopts[i].name,
				"quotestr") == 0)
			    quotestr = option;
			else
#endif
#ifndef NANO_TINY
			if (strcasecmp(rcopts[i].name,
				"backupdir") == 0)
			    backup_dir = option;
			else
#endif
#ifndef DISABLE_SPELLER
			if (strcasecmp(rcopts[i].name, "speller") == 0)
			    alt_speller = option;
			else
#endif
			if (strcasecmp(rcopts[i].name,
				"tabsize") == 0) {
			    if (!parse_num(option, &tabsize) ||
				tabsize <= 0) {
				rcfile_error(
					N_("Requested tab size \"%s\" is invalid"),
					option);
				tabsize = -1;
			    } else
				free(option);
			} else
			    assert(FALSE);
		    }
#ifdef DEBUG
		    fprintf(stderr, "flag = %ld\n", rcopts[i].flag);
#endif
		} else if (rcopts[i].flag != 0)
		    UNSET(rcopts[i].flag);
		else
		    rcfile_error(N_("Cannot unset flag \"%s\""),
			rcopts[i].name);
		break;
	    }
	}
	if (rcopts[i].name == NULL)
	    rcfile_error(N_("Unknown flag \"%s\""), option);
    }

#ifdef ENABLE_COLOR
    if (endsyntax != NULL && endcolor == NULL)
	rcfile_error(N_("Syntax \"%s\" has no color commands"),
		endsyntax->desc);
#endif

    free(buf);
    fclose(rcstream);
    lineno = 0;

    check_vitals_mapped();
    return;
}
Пример #21
0
/* Set width to the number of files that we can display per line, if
 * necessary, and display the list of files. */
void browser_refresh(void)
{
    size_t i;
    int line = 0, col = 0;
	/* The current line and column while the list is getting displayed. */
    char *foo;
	/* The additional information that we'll display about a file. */

    /* Perhaps window dimensions have changed; reinitialize the browser. */
    browser_init(path_save, opendir(path_save));
    qsort(filelist, filelist_len, sizeof(char *), diralphasort);

    /* Make sure the selected file is within range. */
    if (selected >= filelist_len)
	selected = filelist_len - 1;

    titlebar(path_save);
    blank_edit();

    wmove(edit, 0, 0);

    i = width * editwinrows * ((selected / width) / editwinrows);

    for (; i < filelist_len && line < editwinrows; i++) {
	struct stat st;
	const char *filetail = tail(filelist[i]);
		/* The filename we display, minus the path. */
	size_t filetaillen = strlenpt(filetail);
		/* The length of the filename in columns. */
	size_t foolen;
		/* The length of the file information in columns. */
	int foomaxlen = 7;
		/* The maximum length of the file information in
		 * columns: seven for "--", "(dir)", or the file size,
		 * and 12 for "(parent dir)". */
	bool dots = (COLS >= 15 && filetaillen >= longest - foomaxlen);
		/* Do we put an ellipsis before the filename?  Don't set
		 * this to TRUE if we have fewer than 15 columns (i.e.
		 * one column for padding, plus seven columns for a
		 * filename other than ".."). */
	char *disp = display_string(filetail, dots ? filetaillen -
		longest + foomaxlen + 4 : 0, longest, FALSE);
		/* If we put an ellipsis before the filename, reserve
		 * one column for padding, plus seven columns for "--",
		 * "(dir)", or the file size, plus three columns for the
		 * ellipsis. */

	/* Start highlighting the currently selected file or directory. */
	if (i == selected)
	    wattron(edit, hilite_attribute);

	blank_line(edit, line, col, longest);

	/* If dots is TRUE, we will display something like "...ename". */
	if (dots)
	    mvwaddstr(edit, line, col, "...");
	mvwaddstr(edit, line, dots ? col + 3 : col, disp);

	free(disp);

	col += longest;

	/* Show information about the file.  We don't want to report
	 * file sizes for links, so we use lstat(). */
	if (lstat(filelist[i], &st) == -1 || S_ISLNK(st.st_mode)) {
	    /* If the file doesn't exist (i.e. it's been deleted while
	     * the file browser is open), or it's a symlink that doesn't
	     * point to a directory, display "--". */
	    if (stat(filelist[i], &st) == -1 || !S_ISDIR(st.st_mode))
		foo = mallocstrcpy(NULL, "--");
	    /* If the file is a symlink that points to a directory,
	     * display it as a directory. */
	    else
		/* TRANSLATORS: Try to keep this at most 7 characters. */
		foo = mallocstrcpy(NULL, _("(dir)"));
	} else if (S_ISDIR(st.st_mode)) {
	    /* If the file is a directory, display it as such. */
	    if (strcmp(filetail, "..") == 0) {
		/* TRANSLATORS: Try to keep this at most 12 characters. */
		foo = mallocstrcpy(NULL, _("(parent dir)"));
		foomaxlen = 12;
	    } else
		foo = mallocstrcpy(NULL, _("(dir)"));
	} else {
	    unsigned long result = st.st_size;
	    char modifier;

	    foo = charalloc(foomaxlen + 1);

	    if (st.st_size < (1 << 10))
		modifier = ' ';  /* bytes */
	    else if (st.st_size < (1 << 20)) {
		result >>= 10;
		modifier = 'K';  /* kilobytes */
	    } else if (st.st_size < (1 << 30)) {
		result >>= 20;
		modifier = 'M';  /* megabytes */
	    } else {
Пример #22
0
/* Set filelist to the list of files contained in the directory path,
 * set filelist_len to the number of files in that list, set longest to
 * the width in columns of the longest filename in that list (between 15
 * and COLS), and set width to the number of files that we can display
 * per line.  longest needs to be at least 15 columns in order to
 * display ".. (parent dir)", as Pico does.  Assume path exists and is a
 * directory. */
void browser_init(const char *path, DIR *dir)
{
    const struct dirent *nextdir;
    size_t i = 0, path_len = strlen(path);
    int col = 0;
	/* The maximum number of columns that the filenames will take
	 * up. */
    int line = 0;
	/* The maximum number of lines that the filenames will take
	 * up. */
    int filesperline = 0;
	/* The number of files that we can display per line. */

    assert(path != NULL && path[strlen(path) - 1] == '/' && dir != NULL);

    /* Set longest to zero, just before we initialize it. */
    longest = 0;

    while ((nextdir = readdir(dir)) != NULL) {
	size_t d_len;

	/* Don't show the "." entry. */
	if (strcmp(nextdir->d_name, ".") == 0)
	    continue;

	d_len = strlenpt(nextdir->d_name);
	if (d_len > longest)
	    longest = (d_len > COLS) ? COLS : d_len;

	i++;
    }

    rewinddir(dir);

    /* Put 10 columns' worth of blank space between columns of filenames
     * in the list whenever possible, as Pico does. */
    longest += 10;

    if (filelist != NULL)
	free_chararray(filelist, filelist_len);

    filelist_len = i;

    filelist = (char **)nmalloc(filelist_len * sizeof(char *));

    i = 0;

    while ((nextdir = readdir(dir)) != NULL && i < filelist_len) {
	/* Don't show the "." entry. */
	if (strcmp(nextdir->d_name, ".") == 0)
	    continue;

	filelist[i] = charalloc(path_len + strlen(nextdir->d_name) + 1);
	sprintf(filelist[i], "%s%s", path, nextdir->d_name);

	i++;
    }

    /* Maybe the number of files in the directory changed between the
     * first time we scanned and the second.  i is the actual length of
     * filelist, so record it. */
    filelist_len = i;

    closedir(dir);

    /* Make sure longest is between 15 and COLS. */
    if (longest < 15)
	longest = 15;
    if (longest > COLS)
	longest = COLS;

    /* Set width to zero, just before we initialize it. */
    width = 0;

    for (i = 0; i < filelist_len && line < editwinrows; i++) {
	/* Calculate the number of columns one filename will take up. */
	col += longest;
	filesperline++;

	/* Add some space between the columns. */
	col += 2;

	/* If the next entry isn't going to fit on the current line,
	 * move to the next line. */
	if (col > COLS - longest) {
	    line++;
	    col = 0;

	    /* If width isn't initialized yet, and we've taken up more
	     * than one line, it means that width is equal to
	     * filesperline. */
	    if (width == 0)
		width = filesperline;
	}
    }

    /* If width isn't initialized yet, and we've taken up only one line,
     * it means that width is equal to longest. */
    if (width == 0)
	width = longest;
}
Пример #23
0
/* Set filelist to the list of files contained in the directory path,
 * set longest to the width in columns of the longest filename in that
 * list (between 15 and COLS), and set width to the number of files that
 * we can display per line.  longest needs to be at least 15 columns in
 * order to display ".. (parent dir)", as Pico does.  Assume path exists
 * and is a directory. */
void browser_init(const std::string& path, DIR *dir)
{
	const struct dirent *nextdir;
	int col = 0;
	/* The maximum number of columns that the filenames will take
	 * up. */
	int line = 0;
	/* The maximum number of lines that the filenames will take
	 * up. */
	int filesperline = 0;
	/* The number of files that we can display per line. */

	assert(path.length() > 0 && path.back() == '/' && dir != NULL);

	/* Set longest to zero, just before we initialize it. */
	longest = 0;

	while ((nextdir = readdir(dir)) != NULL) {
		size_t d_len;

		/* Don't show the "." entry. */
		if (strcmp(nextdir->d_name, ".") == 0) {
			continue;
		}

		d_len = strlenpt(nextdir->d_name);
		if (d_len > longest) {
			longest = (d_len > COLS) ? COLS : d_len;
		}
	}

	rewinddir(dir);

	/* Put 10 columns' worth of blank space between columns of filenames
	 * in the list whenever possible, as Pico does. */
	longest += 10;

	filelist.clear();

	while ((nextdir = readdir(dir)) != NULL) {
		/* Don't show the "." entry. */
		if (strcmp(nextdir->d_name, ".") == 0) {
			continue;
		}

		filelist.push_back(std::string(path) + std::string(nextdir->d_name));
	}

	closedir(dir);

	/* Make sure longest is between 15 and COLS. */
	if (longest < 15) {
		longest = 15;
	}
	if (longest > COLS) {
		longest = COLS;
	}

	/* Set width to zero, just before we initialize it. */
	width = 0;

	for (size_t i = 0; i < filelist.size() && line < editwinrows; i++) {
		/* Calculate the number of columns one filename will take up. */
		col += longest;
		filesperline++;

		/* Add some space between the columns. */
		col += 2;

		/* If the next entry isn't going to fit on the current line,
		 * move to the next line. */
		if (col > COLS - longest) {
			line++;
			col = 0;

			/* If width isn't initialized yet, and we've taken up more
			 * than one line, it means that width is equal to
			 * filesperline. */
			if (width == 0) {
				width = filesperline;
			}
		}
	}

	/* If width isn't initialized yet, and we've taken up only one line,
	 * it means that width is equal to longest. */
	if (width == 0) {
		width = longest;
	}
}
Пример #24
0
void titlebar(const char *path)
{
	int space = COLS;
	/* The space we have available for display. */
	size_t verlen = strlenpt(PACKAGE_STRING) + 1;
	/* The length of the version message in columns, plus one for
	 * padding. */
	const char *prefix;
	/* "DIR:", "File:", or "New Buffer".  Goes before filename. */
	size_t prefixlen;
	/* The length of the prefix in columns, plus one for padding. */
	const char *state;
	/* "Modified", "View", or "".  Shows the state of this
	 * buffer. */
	ssize_t statelen = 0;
	/* The length of the state in columns, or the length of
	 * "Modified" if the state is blank and we're not in the file
	 * browser. */
	char *exppath = NULL;
	/* The filename, expanded for display. */
	bool newfie = false;
	/* Do we say "New Buffer"? */
	bool dots = false;
	/* Do we put an ellipsis before the path? */

	set_color(topwin, interface_colors[TITLE_BAR]);

	blank_titlebar();

	/* space has to be at least 4: two spaces before the version message,
	 * at least one character of the version message, and one space
	 * after the version message. */
	if (space < 4) {
		space = 0;
	} else {
		/* Limit verlen to 1/3 the length of the screen in columns,
		 * minus three columns for spaces. */
		if (verlen > (COLS / 3) - 3) {
			verlen = (COLS / 3) - 3;
		}
	}

	if (space >= 4) {
		/* Add a space after the version message, and account for both
		 * it and the two spaces before it. */
		mvwaddnstr(topwin, 0, 2, PACKAGE_STRING, actual_x(PACKAGE_STRING, verlen));
		verlen += 3;

		/* Account for the full length of the version message. */
		space -= verlen;
	}

	/* Don't display the state if we're in the file browser. */
	if (path != NULL) {
		state = "";
	} else {
		state = openfile->modified ? _("Modified") : ISSET(VIEW_MODE) ? _("View") : "";
	}

	statelen = strlenpt((*state == '\0' && path == NULL) ? _("Modified") : state);

	/* If possible, add a space before state. */
	if (space > 0 && statelen < space) {
		statelen++;
	} else {
		goto the_end;
	}

	/* path should be a directory if we're in the file browser. */
	if (path != NULL) {
		prefix = _("DIR:");
	} else {
		if (openfile->filename[0] == '\0') {
			prefix = _("New Buffer");
			newfie = true;
		} else {
			prefix = _("File:");
		}
	}

	prefixlen = strnlenpt(prefix, space - statelen) + 1;

	/* If newfie is false, add a space after prefix. */
	if (!newfie && prefixlen + statelen < space) {
		prefixlen++;
	}

	/* If we're not in the file browser, set path to the current
	 * filename. */
	if (path == NULL) {
		path = openfile->filename.c_str();
	}

	/* Account for the full lengths of the prefix and the state. */
	if (space >= prefixlen + statelen) {
		space -= prefixlen + statelen;
	} else {
		space = 0;
	}
	/* space is now the room we have for the filename. */

	if (!newfie) {
		size_t lenpt = strlenpt(path), start_col;

		/* Don't set dots to true if we have fewer than eight columns
		 * (i.e. one column for padding, plus seven columns for a
		 * filename). */
		dots = (space >= 8 && lenpt >= space);

		if (dots) {
			start_col = lenpt - space + 3;
			space -= 3;
		} else {
			start_col = 0;
		}

		exppath = display_string(path, start_col, space, false);
	}

	/* If dots is true, we will display something like "File:
	 * ...ename". */
	if (dots) {
		mvwaddnstr(topwin, 0, verlen - 1, prefix, actual_x(prefix, prefixlen));
		if (space <= -3 || newfie) {
			goto the_end;
		}
		waddch(topwin, ' ');
		waddnstr(topwin, "...", space + 3);
		if (space <= 0) {
			goto the_end;
		}
		waddstr(topwin, exppath);
	} else {
		size_t exppathlen = newfie ? 0 : strlenpt(exppath);
		/* The length of the expanded filename. */

		/* There is room for the whole filename, so we center it. */
		mvwaddnstr(topwin, 0, verlen + ((space - exppathlen) / 3), prefix, actual_x(prefix, prefixlen));
		if (!newfie) {
			waddch(topwin, ' ');
			waddstr(topwin, exppath);
		}
	}

the_end:
	free(exppath);

	if (state[0] != '\0') {
		if (statelen >= COLS - 1) {
			mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));
		} else {
			assert(COLS - statelen - 1 >= 0);

			mvwaddnstr(topwin, 0, COLS - statelen - 1, state, actual_x(state, statelen));
		}
	}

	clear_color(topwin, interface_colors[TITLE_BAR]);

	wnoutrefresh(topwin);
	reset_cursor();
	wnoutrefresh(edit);
}
Пример #25
0
/* If scroll_only is FALSE, move down one line.  If scroll_only is TRUE,
 * scroll down one line without scrolling the cursor. */
void do_down(bool scroll_only)
{
#ifndef NANO_TINY
    int amount = 0, enough;
    filestruct *topline;
#endif
    size_t was_column = xplustabs();

    /* If we're at the bottom of the file, get out. */
    if (openfile->current == openfile->filebot)
	return;

    assert(ISSET(SOFTWRAP) || openfile->current_y ==
		openfile->current->lineno - openfile->edittop->lineno);
    assert(openfile->current->next != NULL);

#ifndef NANO_TINY
    if (ISSET(SOFTWRAP)) {
	/* Compute the number of lines to scroll. */
	amount = strlenpt(openfile->current->data) / editwincols -
			xplustabs() / editwincols +
			strlenpt(openfile->current->next->data) / editwincols +
			openfile->current_y - editwinrows + 2;
	topline = openfile->edittop;
	/* Reduce the amount when there are overlong lines at the top. */
	for (enough = 1; enough < amount; enough++) {
	    amount -= strlenpt(topline->data) / editwincols;
	    if (amount > 0)
		topline = topline->next;
	    if (amount < enough) {
		amount = enough;
		break;
	    }
	}
    }
#endif

    /* Move to the next line in the file. */
    openfile->current = openfile->current->next;
    openfile->current_x = actual_x(openfile->current->data,
					openfile->placewewant);

    /* If scroll_only is FALSE and if we're on the last line of the
     * edit window, scroll the edit window down one line if we're in
     * smooth scrolling mode, or down half a page if we're not.  If
     * scroll_only is TRUE, scroll the edit window down one line
     * unconditionally. */
#ifndef NANO_TINY
    if (openfile->current_y == editwinrows - 1 || amount > 0 || scroll_only) {
	if (amount < 1 || scroll_only)
	    amount = 1;

	edit_scroll(DOWNWARD, (ISSET(SMOOTH_SCROLL) || scroll_only) ?
				amount : editwinrows / 2 + 1);

	if (ISSET(SOFTWRAP)) {
	    refresh_needed = TRUE;
	    return;
	}
    }
#else
    if (openfile->current_y == editwinrows - 1)
	edit_scroll(DOWNWARD, editwinrows / 2 + 1);
#endif

    /* If the lines weren't already redrawn, see if they need to be. */
    if (openfile->current_y < editwinrows - 1) {
	/* Redraw the prior line if it was horizontally scrolled. */
	if (need_horizontal_scroll(was_column, 0))
	    update_line(openfile->current->prev, 0);
	/* Redraw the current line if it needs to be horizontally scrolled. */
	if (need_horizontal_scroll(0, xplustabs()))
	    update_line(openfile->current, openfile->current_x);
    }
}
Пример #26
0
/* edit_draw() takes care of the job of actually painting a line into
 * the edit window.  fileptr is the line to be painted, at row line of
 * the window.  converted is the actual string to be written to the
 * window, with tabs and control characters replaced by strings of
 * regular characters.  start is the column number of the first
 * character of this page.  That is, the first character of converted
 * corresponds to character number actual_x(fileptr->data, start) of the
 * line. */
void edit_draw(filestruct *fileptr, const char *converted, int line, size_t start)
{
	size_t startpos = actual_x(fileptr->data, start);
	/* The position in fileptr->data of the leftmost character
	 * that displays at least partially on the window. */
	size_t endpos = actual_x(fileptr->data, start + COLS - 1) + 1;
	/* The position in fileptr->data of the first character that is
	 * completely off the window to the right.
	 *
	 * Note that endpos might be beyond the null terminator of the
	 * string. */

	assert(openfile != openfiles.end() && fileptr != NULL && converted != NULL);
	assert(strlenpt(converted) <= COLS);

	/* Just paint the string in any case (we'll add color or reverse on
	 * just the text that needs it). */
	mvwaddstr(edit, line, 0, converted);
	/* Tell ncurses to really redraw the line without trying to optimize
	 * for what it thinks is already there, because it gets it wrong in
	 * the case of a wide character in column zero. */
#ifndef USE_SLANG
	wredrawln(edit, line, 1);
#endif

	/* If color syntaxes are available and turned on, we need to display
	 * them. */
	if (!openfile->colorstrings.empty() && !ISSET(NO_COLOR_SYNTAX)) {
		/* Set up multi-line color data for this line if it's not yet calculated  */
		if (fileptr->multidata.empty() && openfile->syntax && openfile->syntax->nmultis > 0) {
			fileptr->multidata.resize(openfile->syntax->nmultis, -1); // assume that '-1' applies until we know otherwise
		}
		for (auto tmpcolor : openfile->colorstrings) {
			int x_start;
			/* Starting column for mvwaddnstr.  Zero-based. */
			int paintlen = 0;
			/* Number of chars to paint on this line.  There are
			 * COLS characters on a whole line. */
			size_t index;
			/* Index in converted where we paint. */
			regmatch_t startmatch;
			/* Match position for start_regex. */
			regmatch_t endmatch;
			/* Match position for end_regex. */

			if (tmpcolor->bright) {
				wattron(edit, A_BOLD);
			}
			if (tmpcolor->underline) {
				wattron(edit, A_UNDERLINE);
			}
			wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
			/* Two notes about regexec().  A return value of zero means
			 * that there is a match.  Also, rm_eo is the first
			 * non-matching character after the match. */

			/* First case,tmpcolor is a single-line expression. */
			if (tmpcolor->end == NULL) {
				size_t k = 0;

				/* We increment k by rm_eo, to move past the end of the
				 * last match.  Even though two matches may overlap, we
				 * want to ignore them, so that we can highlight e.g. C
				 * strings correctly. */
				while (k < endpos) {
					/* Note the fifth parameter to regexec().  It says
					 * not to match the beginning-of-line character
					 * unless k is zero.  If regexec() returns
					 * REG_NOMATCH, there are no more matches in the
					 * line. */
					if (regexec(tmpcolor->start, &fileptr->data[k], 1, &startmatch, (k == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH) {
						break;
					}


					/* Translate the match to the beginning of the
					 * line. */
					startmatch.rm_so += k;
					startmatch.rm_eo += k;

					/* Skip over a zero-length regex match. */
					if (startmatch.rm_so == startmatch.rm_eo) {
						startmatch.rm_eo++;
					} else if (startmatch.rm_so < endpos && startmatch.rm_eo > startpos) {
						x_start = (startmatch.rm_so <= startpos) ? 0 : strnlenpt(fileptr->data, startmatch.rm_so) - start;

						index = actual_x(converted, x_start);

						paintlen = actual_x(converted + index, strnlenpt(fileptr->data, startmatch.rm_eo) - start - x_start);

						assert(0 <= x_start && 0 <= paintlen);

						mvwaddnstr(edit, line, x_start, converted + index, paintlen);
					}
					k = startmatch.rm_eo;
				}
			} else if (!fileptr->multidata.empty() && fileptr->multidata[tmpcolor->id] != CNONE) {
				/* This is a multi-line regex.  There are two steps.
				 * First, we have to see if the beginning of the line is
				 * colored by a start on an earlier line, and an end on
				 * this line or later.
				 *
				 * We find the first line before fileptr matching the
				 * start.  If every match on that line is followed by an
				 * end, then go to step two.  Otherwise, find the next
				 * line after start_line matching the end.  If that line
				 * is not before fileptr, then paint the beginning of
				 * this line. */
				const filestruct *start_line = fileptr->prev;
				/* The first line before fileptr matching start. */
				regoff_t start_col;
				/* Where it starts in that line. */
				const filestruct *end_line;
				short md = fileptr->multidata[tmpcolor->id];

				if (md == -1) {
					fileptr->multidata[tmpcolor->id] = CNONE;    /* until we find out otherwise */
				} else if (md == CNONE) {
					unset_formatting(tmpcolor);
					continue;
				} else if (md == CWHOLELINE) {
					mvwaddnstr(edit, line, 0, converted, -1);
					unset_formatting(tmpcolor);
					continue;
				} else if (md == CBEGINBEFORE) {
					regexec(tmpcolor->end, fileptr->data, 1, &endmatch, 0);
					paintlen = actual_x(converted, strnlenpt(fileptr->data, endmatch.rm_eo) - start);
					mvwaddnstr(edit, line, 0, converted, paintlen);
					unset_formatting(tmpcolor);
					continue;
				}

				while (start_line != NULL && regexec(tmpcolor->start, start_line->data, 1, &startmatch, 0) == REG_NOMATCH) {
					/* If there is an end on this line, there is no need
					 * to look for starts on earlier lines. */
					if (regexec(tmpcolor->end, start_line->data, 0, NULL, 0) == 0) {
						goto step_two;
					}
					start_line = start_line->prev;
				}

				/* If the found start has been qualified as an end earlier, believe it and skip to the next step. */
				if (start_line != NULL && !start_line->multidata.empty() && start_line->multidata[tmpcolor->id] == CBEGINBEFORE) {
					goto step_two;
				}

				/* Skip over a zero-length regex match. */
				if (startmatch.rm_so == startmatch.rm_eo) {
					startmatch.rm_eo++;
				} else {
					/* No start found, so skip to the next step. */
					if (start_line == NULL) {
						goto step_two;
					}
					/* Now start_line is the first line before fileptr
					 * containing a start match.  Is there a start on
					 * this line not followed by an end on this line? */
					start_col = 0;
					while (true) {
						start_col += startmatch.rm_so;
						startmatch.rm_eo -= startmatch.rm_so;
						if (regexec(tmpcolor->end, start_line->data + start_col + startmatch.rm_eo, 0, NULL, (start_col + startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH) {
							/* No end found after this start. */
							break;
						}
						start_col++;
						if (regexec(tmpcolor->start, start_line->data + start_col, 1, &startmatch, REG_NOTBOL) == REG_NOMATCH) {
							/* No later start on this line. */
							goto step_two;
						}
					}
					/* Indeed, there is a start not followed on this
					 * line by an end. */

					/* We have already checked that there is no end
					 * before fileptr and after the start.  Is there an
					 * end after the start at all?  We don't paint
					 * unterminated starts. */
					end_line = fileptr;
					while (end_line != NULL && regexec(tmpcolor->end, end_line->data, 1, &endmatch, 0) == REG_NOMATCH) {
						end_line = end_line->next;
					}

					/* No end found, or it is too early. */
					if (end_line == NULL || (end_line == fileptr && endmatch.rm_eo <= startpos)) {
						goto step_two;
					}

					/* Now paint the start of fileptr.  If the start of
					 * fileptr is on a different line from the end,
					 * paintlen is -1, meaning that everything on the
					 * line gets painted.  Otherwise, paintlen is the
					 * expanded location of the end of the match minus
					 * the expanded location of the beginning of the
					 * page. */
					if (end_line != fileptr) {
						paintlen = -1;
						fileptr->multidata[tmpcolor->id] = CWHOLELINE;
					} else {
						paintlen = actual_x(converted, strnlenpt(fileptr->data, endmatch.rm_eo) - start);
						fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
					}
					mvwaddnstr(edit, line, 0, converted, paintlen);
					/* If the whole line has been painted, don't bother looking for any more starts. */
					if (paintlen < 0) {
						continue;
					}
step_two:
					/* Second step, we look for starts on this line. */
					start_col = 0;

					while (start_col < endpos) {
						if (regexec(tmpcolor->start, fileptr->data + start_col, 1, &startmatch, (start_col == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH || start_col + startmatch.rm_so >= endpos) {
							/* No more starts on this line. */
							break;
						}
						/* Translate the match to be relative to the
						 * beginning of the line. */
						startmatch.rm_so += start_col;
						startmatch.rm_eo += start_col;

						x_start = (startmatch.rm_so <= startpos) ? 0 : strnlenpt(fileptr->data, startmatch.rm_so) - start;

						index = actual_x(converted, x_start);

						if (regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo, 1, &endmatch, (startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) == 0) {
							/* Translate the end match to be relative to the beginning of the line. */
							endmatch.rm_so += startmatch.rm_eo;
							endmatch.rm_eo += startmatch.rm_eo;
							/* There is an end on this line.  But does
							 * it appear on this page, and is the match
							 * more than zero characters long? */
							if (endmatch.rm_eo > startpos && endmatch.rm_eo > startmatch.rm_so) {
								paintlen = actual_x(converted + index, strnlenpt(fileptr->data, endmatch.rm_eo) - start - x_start);

								assert(0 <= x_start && x_start < COLS);

								mvwaddnstr(edit, line, x_start, converted + index, paintlen);
								if (paintlen > 0) {
									fileptr->multidata[tmpcolor->id] = CSTARTENDHERE;
								}

							}
						} else {
							/* There is no end on this line.  But we
							 * haven't yet looked for one on later
							 * lines. */
							end_line = fileptr->next;

							while (end_line != NULL && regexec(tmpcolor->end, end_line->data, 0, NULL, 0) == REG_NOMATCH) {
								end_line = end_line->next;
							}

							if (end_line != NULL) {
								assert(0 <= x_start && x_start < COLS);

								mvwaddnstr(edit, line, x_start, converted + index, -1);
								/* We painted to the end of the line, so
								 * don't bother checking any more
								 * starts. */
								fileptr->multidata[tmpcolor->id] = CENDAFTER;
								break;
							}
						}
						start_col = startmatch.rm_so + 1;
					}
				}
			}
			unset_formatting(tmpcolor);
		}
	}

	/* If the mark is on, we need to display it. */
	if (openfile->mark_set && (fileptr->lineno <=
	                           openfile->mark_begin->lineno || fileptr->lineno <=
	                           openfile->current->lineno) && (fileptr->lineno >=
	                                   openfile->mark_begin->lineno || fileptr->lineno >=
	                                   openfile->current->lineno)) {
		/* fileptr is at least partially selected. */
		const filestruct *top;
		/* Either current or mark_begin, whichever is first. */
		size_t top_x;
		/* current_x or mark_begin_x, corresponding to top. */
		const filestruct *bot;
		size_t bot_x;
		int x_start;
		/* Starting column for mvwaddnstr().  Zero-based. */
		int paintlen;
		/* Number of characters to paint on this line.  There are
		 * COLS characters on a whole line. */
		size_t index;
		/* Index in converted where we paint. */

		mark_order(&top, &top_x, &bot, &bot_x, NULL);

		if (top->lineno < fileptr->lineno || top_x < startpos) {
			top_x = startpos;
		}
		if (bot->lineno > fileptr->lineno || bot_x > endpos) {
			bot_x = endpos;
		}

		/* The selected bit of fileptr is on this page. */
		if (top_x < endpos && bot_x > startpos) {
			assert(startpos <= top_x);

			/* x_start is the expanded location of the beginning of the
			 * mark minus the beginning of the page. */
			x_start = strnlenpt(fileptr->data, top_x) - start;

			/* If the end of the mark is off the page, paintlen is -1,
			 * meaning that everything on the line gets painted.
			 * Otherwise, paintlen is the expanded location of the end
			 * of the mark minus the expanded location of the beginning
			 * of the mark. */
			if (bot_x >= endpos) {
				paintlen = -1;
			} else
				paintlen = strnlenpt(fileptr->data, bot_x) - (x_start + start);

			/* If x_start is before the beginning of the page, shift
			 * paintlen x_start characters to compensate, and put
			 * x_start at the beginning of the page. */
			if (x_start < 0) {
				paintlen += x_start;
				x_start = 0;
			}

			assert(x_start >= 0 && x_start <= strlen(converted));

			index = actual_x(converted, x_start);

			if (paintlen > 0) {
				paintlen = actual_x(converted + index, paintlen);
			}

			set_color(edit, interface_colors[FUNCTION_TAG]);
			mvwaddnstr(edit, line, x_start, converted + index, paintlen);
			clear_color(edit, interface_colors[FUNCTION_TAG]);
		}
	}
}