Exemple #1
0
/* Calculate the displayable length of the help-text line starting at ptr. */
size_t help_line_len(const char *ptr)
{
    size_t wrapping_point = (COLS > 24) ? COLS - 1 : 24;
	/* The target width for wrapping long lines. */
    ssize_t wrap_location;
	/* Actual position where the line can be wrapped. */
    size_t length = 0;
	/* Full length of the line, until the first newline. */

    /* Avoid overwide paragraphs in the introductory text. */
    if (ptr < end_of_intro && COLS > 74)
	wrapping_point = 74;

    wrap_location = break_line(ptr, wrapping_point, TRUE);

    /* Get the length of the entire line up to a null or a newline. */
    while (*(ptr + length) != '\0' && *(ptr + length) != '\n')
	length = move_mbright(ptr, length);

    /* If the entire line will just fit the screen, don't wrap it. */
    if (strnlenpt(ptr, length) <= wrapping_point + 1)
	return length;
    else if (wrap_location > 0)
	return wrap_location;
    else
	return 0;
}
Exemple #2
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);
}
Exemple #3
0
/* Return the placewewant associated with current_x, i.e. the zero-based
 * column position of the cursor.  The value will be no smaller than
 * current_x. */
size_t xplustabs(void)
{
    if (openfile->current)
	return strnlenpt(openfile->current->data, openfile->current_x);
    else
	return 0;
}
Exemple #4
0
/* Return the placewewant associated with statusbar_x, i.e. the
 * zero-based column position of the cursor.  The value will be no
 * smaller than statusbar_x. */
size_t statusbar_xplustabs(void)
{
    return strnlenpt(answer, statusbar_x);
}
Exemple #5
0
/* Step through each replace word and prompt user before replacing.
 * Parameters real_current and real_current_x are needed in order to
 * allow the cursor position to be updated when a word before the cursor
 * is replaced by a shorter word.
 *
 * needle is the string to seek.  We replace it with answer.  Return -1
 * if needle isn't found, else the number of replacements performed.  If
 * canceled isn't NULL, set it to TRUE if we canceled. */
ssize_t do_replace_loop(
#ifndef DISABLE_SPELLER
	bool whole_word_only,
#endif
	bool *canceled, const linestruct *real_current, size_t
	*real_current_x, const char *needle)
{
    ssize_t numreplaced = -1;
    size_t match_len;
    bool replaceall = FALSE;
#ifndef NANO_TINY
    bool old_mark_set = openfile->mark_set;
    linestruct *top, *bot;
    size_t top_x, bot_x;
    bool right_side_up = FALSE;
	/* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
	 * FALSE if (current, current_x) is. */

    if (old_mark_set) {
	/* If the mark is on, frame the region, and turn the mark off. */
	mark_order((const linestruct **)&top, &top_x,
	    (const linestruct **)&bot, &bot_x, &right_side_up);
	openfile->mark_set = FALSE;

	/* Start either at the top or the bottom of the marked region. */
	if (!ISSET(BACKWARDS_SEARCH)) {
	    openfile->current = top;
	    openfile->current_x = (top_x == 0 ? 0 : top_x - 1);
	} else {
	    openfile->current = bot;
	    openfile->current_x = bot_x;
	}
    }
#endif /* !NANO_TINY */

    if (canceled != NULL)
	*canceled = FALSE;

    findnextstr_wrap_reset();
    while (findnextstr(
#ifndef DISABLE_SPELLER
	whole_word_only,
#endif
	real_current, *real_current_x, needle, &match_len)) {
	int i = 0;

#ifndef NANO_TINY
	if (old_mark_set) {
	    /* When we've found an occurrence outside of the marked region,
	     * stop the fanfare. */
	    if (openfile->current->lineno > bot->lineno ||
		openfile->current->lineno < top->lineno ||
		(openfile->current == bot && openfile->current_x > bot_x) ||
		(openfile->current == top && openfile->current_x < top_x))
		break;
	}
#endif

	/* Indicate that we found the search string. */
	if (numreplaced == -1)
	    numreplaced = 0;

	if (!replaceall) {
	    size_t xpt = xplustabs();
	    char *exp_word = display_string(openfile->current->data,
		xpt, strnlenpt(openfile->current->data,
		openfile->current_x + match_len) - xpt, FALSE);

	    edit_refresh();

	    curs_set(0);

	    do_replace_highlight(TRUE, exp_word);

	    /* TRANSLATORS: This is a prompt. */
	    i = do_yesno_prompt(TRUE, _("Replace this instance?"));

	    do_replace_highlight(FALSE, exp_word);

	    free(exp_word);

	    curs_set(1);

	    if (i == -1) {	/* We canceled the replace. */
		if (canceled != NULL)
		    *canceled = TRUE;
		break;
	    }
	}

	if (i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    char *copy;
	    size_t length_change;

#ifndef NANO_TINY
	    add_undo(REPLACE);
#endif
	    if (i == 2)
		replaceall = TRUE;

	    copy = replace_line(needle);

	    length_change = strlen(copy) - strlen(openfile->current->data);

#ifndef NANO_TINY
	    /* If the mark was on and (mark_begin, mark_begin_x) was the
	     * top of it, don't change mark_begin_x. */
	    if (!old_mark_set || !right_side_up) {
		/* Keep mark_begin_x in sync with the text changes. */
		if (openfile->current == openfile->mark_begin &&
			openfile->mark_begin_x > openfile->current_x) {
		    if (openfile->mark_begin_x < openfile->current_x +
			match_len)
			openfile->mark_begin_x = openfile->current_x;
		    else
			openfile->mark_begin_x += length_change;
		    bot_x = openfile->mark_begin_x;
		}
	    }

	    /* If the mark was on and (current, current_x) was the top
	     * of it, don't change real_current_x. */
	    if (!old_mark_set || right_side_up) {
#endif
		/* Keep real_current_x in sync with the text changes. */
		if (openfile->current == real_current &&
			openfile->current_x <= *real_current_x) {
		    if (*real_current_x < openfile->current_x + match_len)
			*real_current_x = openfile->current_x + match_len;
		    *real_current_x += length_change;
#ifndef NANO_TINY
		    bot_x = *real_current_x;
		}
#endif
	    }

	    /* Set the cursor at the last character of the replacement
	     * text, so searching will resume after the replacement
	     * text.  Note that current_x might be set to (size_t)-1
	     * here. */
#ifndef NANO_TINY
	    if (!ISSET(BACKWARDS_SEARCH))
#endif
		openfile->current_x += match_len + length_change - 1;

	    /* Clean up. */
	    openfile->totsize += mbstrlen(copy) -
		mbstrlen(openfile->current->data);
	    free(openfile->current->data);
	    openfile->current->data = copy;

#ifndef DISABLE_COLOR
	    /* Reset the precalculated multiline-regex hints only when
	     * the first replacement has been made. */
	    if (numreplaced == 0)
		reset_multis(openfile->current, TRUE);
#endif

	    if (!replaceall) {
#ifndef DISABLE_COLOR
		/* If color syntaxes are available and turned on, we
		 * need to call edit_refresh(). */
		if (openfile->colorstrings != NULL &&
			!ISSET(NO_COLOR_SYNTAX))
		    edit_refresh();
		else
#endif
		    update_line(openfile->current, openfile->current_x);
	    }

	    set_modified();
	    numreplaced++;
	}
    }

    if (numreplaced == -1)
	not_found_msg(needle);

#ifndef NANO_TINY
    if (old_mark_set)
	openfile->mark_set = TRUE;
#endif

    /* If the NO_NEWLINES flag isn't set, and text has been added to the
     * magicline, make a new magicline. */
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
	new_magicline();

    return numreplaced;
}
Exemple #6
0
/* A strlen() with tabs and multicolumn characters factored in, similar
 * to xplustabs().  How many columns wide is s? */
size_t strlenpt(const char *s)
{
    return strnlenpt(s, (size_t)-1);
}
Exemple #7
0
/* Return the placewewant associated with current_x, i.e. the zero-based
 * column position of the cursor.  The value will be no smaller than
 * current_x. */
size_t xplustabs(void)
{
    return strnlenpt(openfile->current->data, openfile->current_x);
}
Exemple #8
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]);
		}
	}
}
Exemple #9
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);
}
Exemple #10
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;
}
Exemple #11
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;
}