From 5ffbec56f6c6487e245eb26e24b51dad80006595 Mon Sep 17 00:00:00 2001 From: David Lawrence Ramsey Date: Tue, 16 Sep 2003 01:16:49 +0000 Subject: [PATCH] port over some of DB's refactored display code, most importantly the display_string() function, and convert some parts of nano to use it git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@1552 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 14 ++ src/move.c | 8 +- src/nano.c | 2 +- src/nano.h | 1 + src/proto.h | 18 +- src/search.c | 57 +++-- src/utils.c | 22 ++ src/winio.c | 585 ++++++++++++++++++++++++++------------------------- 8 files changed, 393 insertions(+), 314 deletions(-) diff --git a/ChangeLog b/ChangeLog index 56ebe609..2b6ce6e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -38,6 +38,20 @@ CVS code - wrap_reset() calls with DISABLE_WRAPPING #ifdefs. (DLR) - Change enum "topmidbotnone" to "topmidnone", as there's no BOTTOM option anymore. (DLR) + - Split out the string-displaying routine from update_line() + into a separate function; convert the edit window, statusbar + display, and statusbar prompt to use it, so that they can all + properly display control characters and tabs; free and NULL + the backup search string in one place in the search code + instead of several; and do some other minor refactoring of + related display functions to simplify them. New functions + mark_order() and display_string(); changes to actual_x(), + edit_add(), update_line(), statusbar(), and + do_replace_highlight(). (David Benbennick) DLR: Add minor + cosmetic tweaks, add missing NANO_SMALL #ifdef around the text + for a backwards search in the refactored code, and enclose + dump_buffer() and dump_buffer_reverse() in one ENABLE_DEBUG + #ifdef instead of two. - files.c: do_browser() - Some of the Pico compatibility options in the file browser diff --git a/src/move.c b/src/move.c index cf088b52..93472f4b 100644 --- a/src/move.c +++ b/src/move.c @@ -81,7 +81,7 @@ int do_page_up(void) #endif } /* Get the equivalent x-coordinate of the new line. */ - current_x = actual_x(current, placewewant); + current_x = actual_x(current->data, placewewant); edit_refresh(); @@ -125,7 +125,7 @@ int do_page_down(void) #endif } /* Get the equivalent x-coordinate of the new line. */ - current_x = actual_x(current, placewewant); + current_x = actual_x(current->data, placewewant); edit_refresh(); @@ -145,7 +145,7 @@ int do_up(void) assert(current_y == current->lineno - edittop->lineno); current = current->prev; - current_x = actual_x(current, placewewant); + current_x = actual_x(current->data, placewewant); if (current_y > 0) { update_line(current->next, 0); /* It was necessary to change current first, so the mark @@ -175,7 +175,7 @@ int do_down(void) assert(current_y == current->lineno - edittop->lineno); current = current->next; - current_x = actual_x(current, placewewant); + current_x = actual_x(current->data, placewewant); /* Note that current_y is zero-based. This test checks for the * cursor's being not on the last row of the edit window. */ diff --git a/src/nano.c b/src/nano.c index f020735c..67fc1595 100644 --- a/src/nano.c +++ b/src/nano.c @@ -917,7 +917,7 @@ void do_mouse(void) for(; current_y > mevent.y && current->prev != NULL; current_y--) current = current->prev; - xcur = actual_x(current, get_page_start(xplustabs()) + mevent.x); + xcur = actual_x(current->data, get_page_start(xplustabs()) + mevent.x); /* Selecting where the cursor is toggles the mark. As does selecting beyond the line length with the cursor at the end of diff --git a/src/nano.h b/src/nano.h index 32e254ed..03203c12 100644 --- a/src/nano.h +++ b/src/nano.h @@ -35,6 +35,7 @@ /* Define charalloc as a macro rather than duplicating code */ #define charalloc(howmuch) (char *)nmalloc((howmuch) * sizeof(char)) #define charealloc(ptr, howmuch) (char *)nrealloc(ptr, (howmuch) * sizeof(char)) +#define charmove(dest, src, n) memmove(dest, src, (n) * sizeof(char)) #ifdef BROKEN_REGEXEC #define regexec(preg, string, nmatch, pmatch, eflags) regexec_safe(preg, string, nmatch, pmatch, eflags) #endif diff --git a/src/proto.h b/src/proto.h index 62b3c460..138a4269 100644 --- a/src/proto.h +++ b/src/proto.h @@ -426,6 +426,10 @@ void *nmalloc(size_t howmuch); void *nrealloc(void *ptr, size_t howmuch); char *mallocstrcpy(char *dest, const char *src); void new_magicline(void); +#ifndef NANO_SMALL +void mark_order(const filestruct **top, size_t *top_x, + const filestruct **bot, size_t *bot_x); +#endif #ifndef DISABLE_TABCOMP int check_wildcard_match(const char *text, const char *pattern); #endif @@ -443,7 +447,7 @@ int do_first_line(void); int do_last_line(void); int xpt(const filestruct *fileptr, int index); size_t xplustabs(void); -size_t actual_x(const filestruct *fileptr, size_t xplus); +size_t actual_x(const char *str, size_t xplus); size_t strnlenpt(const char *buf, size_t size); size_t strlenpt(const char *buf); void blank_bottombars(void); @@ -452,7 +456,8 @@ void blank_edit(void); void blank_statusbar(void); void blank_statusbar_refresh(void); void check_statblank(void); -void nanoget_repaint(const char *buf, const char *inputbuf, int x); +char *display_string(const char *buf, size_t start_col, int len); +void nanoget_repaint(const char *buf, const char *inputbuf, size_t x); int nanogetstr(int allowtabs, const char *buf, const char *def, #ifndef NANO_SMALL historyheadtype *history_list, @@ -473,12 +478,9 @@ int get_page_start(int column); void reset_cursor(void); void add_marked_sameline(int begin, int end, filestruct *fileptr, int y, int virt_cur_x, int this_page); -void edit_add(const filestruct *fileptr, int yval, int start -#ifndef NANO_SMALL - , int virt_mark_beginx, int virt_cur_x -#endif - ); -void update_line(filestruct *fileptr, int index); +void edit_add(const filestruct *fileptr, const char *converted, + int yval, size_t start); +void update_line(const filestruct *fileptr, size_t index); void update_cursor(void); void center_cursor(void); void edit_refresh(void); diff --git a/src/search.c b/src/search.c index 43fbd65d..0d7dca9a 100644 --- a/src/search.c +++ b/src/search.c @@ -107,18 +107,22 @@ int search_init(int replacing) search_init_globals(); + /* If we don't already have a backupstring, set it. */ if (backupstring == NULL) - backupstring = mallocstrcpy(backupstring, ""); + backupstring = mallocstrcpy(NULL, ""); #ifndef NANO_SMALL search_history.current = (historytype *)&search_history.next; #endif if (last_search[0] != '\0') { + char *disp = display_string(last_search, 0, COLS / 3); + buf = charalloc(COLS / 3 + 7); /* We use COLS / 3 here because we need to see more on the line */ - sprintf(buf, " [%.*s%s]", COLS / 3, last_search, - strlen(last_search) > COLS / 3 ? "..." : ""); + sprintf(buf, " [%s%s]", disp, + strlenpt(last_search) > COLS / 3 ? "..." : ""); + free(disp); } else { buf = charalloc(1); buf[0] = '\0'; @@ -132,17 +136,23 @@ int search_init(int replacing) "%s%s%s%s%s%s", _("Search"), +#ifndef NANO_SMALL /* This string is just a modifier for the search prompt, no grammar is implied */ - ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : "", + ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : +#endif + "", /* This string is just a modifier for the search prompt, no grammar is implied */ ISSET(USE_REGEXP) ? _(" [Regexp]") : "", +#ifndef NANO_SMALL /* This string is just a modifier for the search prompt, no grammar is implied */ - ISSET(REVERSE_SEARCH) ? _(" [Backwards]") : "", + ISSET(REVERSE_SEARCH) ? _(" [Backwards]") : +#endif + "", replacing ? _(" (to replace)") : "", buf); @@ -150,12 +160,13 @@ int search_init(int replacing) /* 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] == '\0')) { statusbar(_("Search Cancelled")); reset_cursor(); - free(backupstring); - backupstring = NULL; #ifndef NANO_SMALL search_history.current = search_history.next; #endif @@ -169,29 +180,23 @@ int search_init(int replacing) if (regexp_init(last_search) == 0) { statusbar(regex_error, last_search); reset_cursor(); - free(backupstring); - backupstring = NULL; return -3; } #endif break; case 0: /* They entered something new */ + last_replace[0] = '\0'; #ifdef HAVE_REGEX_H if (ISSET(USE_REGEXP)) if (regexp_init(answer) == 0) { statusbar(regex_error, answer); reset_cursor(); - free(backupstring); - backupstring = NULL; #ifndef NANO_SMALL search_history.current = search_history.next; #endif return -3; } #endif - free(backupstring); - backupstring = NULL; - last_replace[0] = '\0'; break; #ifndef NANO_SMALL case TOGGLE_CASE_KEY: @@ -213,8 +218,6 @@ int search_init(int replacing) backupstring = mallocstrcpy(backupstring, answer); return -2; /* Call the opposite search function */ case NANO_FROMSEARCHTOGOTO_KEY: - free(backupstring); - backupstring = NULL; #ifndef NANO_SMALL search_history.current = search_history.next; #endif @@ -226,8 +229,6 @@ int search_init(int replacing) return -3; default: do_early_abort(); - free(backupstring); - backupstring = NULL; return -3; } } @@ -631,6 +632,8 @@ int do_replace_loop(const char *prevanswer, const filestruct *begin, last_replace = mallocstrcpy(last_replace, answer); while (1) { + size_t match_len; + /* Sweet optimization by Rocco here */ fileptr = findnextstr(fileptr || replaceall || search_last_line, FALSE, begin, *beginx, prevanswer); @@ -651,13 +654,27 @@ int do_replace_loop(const char *prevanswer, const filestruct *begin, if (numreplaced == -1) numreplaced = 0; +#ifdef HAVE_REGEX_H + if (ISSET(USE_REGEXP)) + match_len = regmatches[0].rm_eo - regmatches[0].rm_so; + else +#endif + match_len = strlen(prevanswer); + if (!replaceall) { + char *exp_word; + size_t xpt = xplustabs(); + + exp_word = display_string(current->data, xpt, + strnlenpt(current->data, match_len + current_x) - xpt); + curs_set(0); - do_replace_highlight(TRUE, prevanswer); + do_replace_highlight(TRUE, exp_word); *i = do_yesno(1, 1, _("Replace this instance?")); - do_replace_highlight(FALSE, prevanswer); + do_replace_highlight(FALSE, exp_word); + free(exp_word); curs_set(1); } diff --git a/src/utils.c b/src/utils.c index f18f86a3..1628418d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -302,6 +302,28 @@ void new_magicline(void) totsize++; } +#ifndef NANO_SMALL +/* Set top_x and bot_x to the top and bottom x-coordinates of the mark, + * respectively, based on the locations of top and bot. */ +void mark_order(const filestruct **top, size_t *top_x, + const filestruct **bot, size_t *bot_x) +{ + assert(top != NULL && top_x != NULL && bot != NULL && bot_x != NULL); + if ((current->lineno == mark_beginbuf->lineno && current_x > mark_beginx) + || current->lineno > mark_beginbuf->lineno) { + *top = mark_beginbuf; + *top_x = mark_beginx; + *bot = current; + *bot_x = current_x; + } else { + *bot = mark_beginbuf; + *bot_x = mark_beginx; + *top = current; + *top_x = current_x; + } +} +#endif + #ifndef DISABLE_TABCOMP /* * Routine to see if a text string is matched by a wildcard pattern. diff --git a/src/winio.c b/src/winio.c index 7b5f8de9..74f368ba 100644 --- a/src/winio.c +++ b/src/winio.c @@ -343,36 +343,32 @@ size_t xplustabs(void) return strnlenpt(current->data, current_x); } -/* Return what current_x should be, given xplustabs() for the line. */ -size_t actual_x(const filestruct *fileptr, size_t xplus) +/* actual_x() gives the index in str of the character displayed at + * column xplus. That is, actual_x() is the largest value such that + * strnlenpt(str, actual_x(str, xplus)) <= xplus. */ +size_t actual_x(const char *str, size_t xplus) { size_t i = 0; - /* the position in fileptr->data, returned */ + /* the position in str, returned */ size_t length = 0; - /* the screen display width to data[i] */ - char *c; - /* fileptr->data + i */ + /* the screen display width to str[i] */ - assert(fileptr != NULL && fileptr->data != NULL); + assert(str != NULL); - for (c = fileptr->data; length < xplus && *c != '\0'; i++, c++) { - if (*c == '\t') + for (; length < xplus && *str != '\0'; i++, str++) { + if (*str == '\t') length += tabsize - length % tabsize; - else if (is_cntrl_char((int)*c)) + else if (is_cntrl_char((int)*str)) length += 2; else length++; } - assert(length == strnlenpt(fileptr->data, i)); - assert(i <= strlen(fileptr->data)); + assert(length == strnlenpt(str - i, i)); + assert(i <= strlen(str - i)); if (length > xplus) i--; -#ifdef DEBUG - fprintf(stderr, "actual_x for xplus=%d returns %d\n", xplus, i); -#endif - return i; } @@ -444,26 +440,97 @@ void check_statblank(void) } } +/* Convert buf into a string that can be displayed on screen. The + * caller wants to display buf starting with column start_col, and + * extending for at most len columns. start_col is zero-based. len is + * one-based, so len == 0 means you get "" returned. The returned + * string is dynamically allocated, and should be freed. */ +char *display_string(const char *buf, size_t start_col, int len) +{ + size_t start_index; + /* Index in buf of first character shown in return value. */ + size_t column; + /* Screen column start_index corresponds to. */ + size_t end_index; + /* Index in buf of last character shown in return value. */ + size_t alloc_len; + /* The length of memory allocated for converted. */ + char *converted; + /* The string we return. */ + size_t index; + /* Current position in converted. */ + + if (len == 0) + return mallocstrcpy(NULL, ""); + + start_index = actual_x(buf, start_col); + column = strnlenpt(buf, start_index); + assert(column <= start_col); + end_index = actual_x(buf, start_col + len - 1); + alloc_len = strnlenpt(buf, end_index + 1) - column; + if (len > alloc_len + column - start_col) + len = alloc_len + column - start_col; + converted = charalloc(alloc_len + 1); + buf += start_index; + index = 0; + + for (; index < alloc_len; buf++) { + if (*buf == '\t') + do { + converted[index++] = ' '; + } while ((column + index) % tabsize); + else if (is_cntrl_char(*buf)) { + converted[index++] = '^'; + if (*buf == '\n') + /* Treat newlines embedded in a line as encoded nulls; + * the line in question should be run through unsunder() + * before reaching here. */ + converted[index++] = '@'; + else if (*buf == NANO_CONTROL_8) + converted[index++] = '?'; + else + converted[index++] = *buf + 64; + } else + converted[index++] = *buf; + } + assert(len <= alloc_len + column - start_col); + charmove(converted, converted + start_col - column, len); + null_at(&converted, len); + + return charealloc(converted, len + 1); +} + /* Repaint the statusbar when getting a character in nanogetstr(). buf - * should be no longer than COLS - 4. + * should be no longer than max(0, COLS - 4). * * Note that we must turn on A_REVERSE here, since do_help() turns it * off! */ -void nanoget_repaint(const char *buf, const char *inputbuf, int x) +void nanoget_repaint(const char *buf, const char *inputbuf, size_t x) { - int len = strlen(buf) + 2; - int wid = COLS - len; + size_t x_real = strnlenpt(inputbuf, x); + int wid = COLS - strlen(buf) - 2; - assert(wid >= 2); assert(0 <= x && x <= strlen(inputbuf)); wattron(bottomwin, A_REVERSE); blank_statusbar(); + mvwaddstr(bottomwin, 0, 0, buf); waddch(bottomwin, ':'); - waddch(bottomwin, x < wid ? ' ' : '$'); - waddnstr(bottomwin, &inputbuf[wid * (x / wid)], wid); - wmove(bottomwin, 0, (x % wid) + len); + + if (COLS > 1) + waddch(bottomwin, x_real < wid ? ' ' : '$'); + if (COLS > 2) { + size_t page_start = x_real - x_real % wid; + char *expanded = display_string(inputbuf, page_start, wid); + + assert(wid > 0); + assert(strlen(expanded) <= wid); + waddstr(bottomwin, expanded); + free(expanded); + wmove(bottomwin, 0, COLS - wid + x_real - page_start); + } else + wmove(bottomwin, 0, COLS - 1); wattroff(bottomwin, A_REVERSE); } @@ -933,23 +1000,34 @@ void reset_cursor(void) wmove(edit, current_y, x - get_page_start(x)); } -/* edit_add() takes care of the job of actually painting a line into - * the edit window. Called only from update_line(). Expects a - * converted-to-not-have-tabs line. */ -void edit_add(const filestruct *fileptr, int yval, int start -#ifndef NANO_SMALL - , int virt_mark_beginx, int virt_cur_x -#endif - ) +/* edit_add() takes care of the job of actually painting a line into the + * edit window. fileptr is the line to be painted, at row yval 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_add(const filestruct *fileptr, const char *converted, + int yval, size_t start) { -#ifdef DEBUG - fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno, - current->lineno); +#if defined(ENABLE_COLOR) || !defined(NANO_SMALL) + 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. */ #endif + assert(fileptr != NULL && converted != NULL); + assert(strlen(converted) <= COLS); + /* Just paint the string in any case (we'll add color or reverse on - just the text that needs it */ - mvwaddnstr(edit, yval, 0, &fileptr->data[start], COLS); + * just the text that needs it). */ + mvwaddstr(edit, yval, 0, converted); #ifdef ENABLE_COLOR if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) { @@ -959,16 +1037,16 @@ void edit_add(const filestruct *fileptr, int yval, int start int x_start; /* Starting column for mvwaddnstr. Zero-based. */ int paintlen; - /* number of chars to paint on this line. There are COLS + /* Number of chars to paint on this line. There are COLS * characters on a whole line. */ - regmatch_t startmatch; /* match position for start_regexp*/ - regmatch_t endmatch; /* match position for end_regexp*/ + regmatch_t startmatch; /* match position for start_regexp */ + regmatch_t endmatch; /* match position for end_regexp */ if (tmpcolor->bright) wattron(edit, A_BOLD); wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); - /* Two notes about regexec. Return value 0 means there is a - * match. Also, rm_eo is the first non-matching character + /* Two notes about regexec(). Return value 0 means there is + * a match. Also, rm_eo is the first non-matching character * after the match. */ /* First case, tmpcolor is a single-line expression. */ @@ -976,16 +1054,16 @@ void edit_add(const filestruct *fileptr, int yval, int start 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 C-strings - correctly. */ - while (k < start + COLS) { - /* Note the fifth parameter to regexec. It says not to - * match the beginning-of-line character unless - * k == 0. If regexec returns nonzero, there are no - * more matches in the line. */ + * last match. Even though two matches may overlap, we + * want to ignore them, so that we can highlight + * 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 0. 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)) + &startmatch, k == 0 ? 0 : REG_NOTBOL) == REG_NOMATCH) break; /* Translate the match to the beginning of the line. */ startmatch.rm_so += k; @@ -993,20 +1071,23 @@ void edit_add(const filestruct *fileptr, int yval, int start if (startmatch.rm_so == startmatch.rm_eo) { startmatch.rm_eo++; statusbar(_("Refusing 0 length regex match")); - } else if (startmatch.rm_so < start + COLS && - startmatch.rm_eo > start) { - x_start = startmatch.rm_so - start; - if (x_start < 0) + } else if (startmatch.rm_so < endpos && + startmatch.rm_eo > startpos) { + if (startmatch.rm_so <= startpos) x_start = 0; - paintlen = startmatch.rm_eo - start - x_start; + else + x_start = strnlenpt(fileptr->data, startmatch.rm_so) + - start; + paintlen = strnlenpt(fileptr->data, startmatch.rm_eo) + - start - x_start; if (paintlen > COLS - x_start) paintlen = COLS - x_start; assert(0 <= x_start && 0 < paintlen && x_start + paintlen <= COLS); mvwaddnstr(edit, yval, x_start, - fileptr->data + start + x_start, paintlen); - } + converted + x_start, paintlen); + } k = startmatch.rm_eo; } } else { @@ -1022,7 +1103,7 @@ void edit_add(const filestruct *fileptr, int yval, int start * before fileptr, then paint the beginning of this line. */ const filestruct *start_line = fileptr->prev; - /* the first line before fileptr matching start*/ + /* the first line before fileptr matching start */ regoff_t start_col; /* where it starts in that line */ const filestruct *end_line; @@ -1032,11 +1113,11 @@ void edit_add(const filestruct *fileptr, int yval, int start while (start_line != NULL && regexec(&tmpcolor->start, start_line->data, 1, - &startmatch, 0)) { + &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, 1, - &endmatch, 0)) + if (regexec(tmpcolor->end, start_line->data, 0, NULL, 0) + == 0) goto step_two; start_line = start_line->prev; } @@ -1051,43 +1132,44 @@ void edit_add(const filestruct *fileptr, int yval, int start while (1) { start_col += startmatch.rm_so; startmatch.rm_eo -= startmatch.rm_so; - if (regexec(tmpcolor->end, - start_line->data + start_col + startmatch.rm_eo, - 1, &endmatch, - start_col + startmatch.rm_eo == 0 ? 0 : REG_NOTBOL)) - /* No end found after this start */ + 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_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. */ + /* 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. */ + * 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)) end_line = end_line->next; /* No end found, or it is too early. */ - if (end_line == NULL || end_line->lineno < fileptr->lineno || - (end_line == fileptr && endmatch.rm_eo <= start)) + if (end_line == NULL || + (end_line == fileptr && endmatch.rm_eo <= startpos)) goto step_two; /* Now paint the start of fileptr. */ - paintlen = end_line != fileptr - ? COLS : endmatch.rm_eo - start; + paintlen = end_line != fileptr ? COLS : + strnlenpt(fileptr->data, endmatch.rm_eo) - start; if (paintlen > COLS) paintlen = COLS; assert(0 < paintlen && paintlen <= COLS); - mvwaddnstr(edit, yval, 0, fileptr->data + start, paintlen); + mvwaddnstr(edit, yval, 0, converted, paintlen); /* We have already painted the whole line. */ if (paintlen == COLS) @@ -1095,10 +1177,11 @@ void edit_add(const filestruct *fileptr, int yval, int start step_two: /* Second step, we look for starts on this line. */ start_col = 0; - while (start_col < start + COLS) { + while (start_col < endpos) { if (regexec(&tmpcolor->start, fileptr->data + start_col, 1, - &startmatch, start_col == 0 ? 0 : REG_NOTBOL) - || start_col + startmatch.rm_so >= start + COLS) + &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 @@ -1106,52 +1189,54 @@ void edit_add(const filestruct *fileptr, int yval, int start startmatch.rm_so += start_col; startmatch.rm_eo += start_col; - x_start = startmatch.rm_so - start; - if (x_start < 0) { + if (startmatch.rm_so <= startpos) x_start = 0; - startmatch.rm_so = start; - } - if (!regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo, - 1, &endmatch, - startmatch.rm_eo == 0 ? 0 : REG_NOTBOL)) { + else + x_start = strnlenpt(fileptr->data, startmatch.rm_so) + - 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. */ + * 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 > start && + * 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 = endmatch.rm_eo - start - x_start; + paintlen = strnlenpt(fileptr->data, endmatch.rm_eo) + - start - x_start; if (x_start + paintlen > COLS) paintlen = COLS - x_start; assert(0 <= x_start && 0 < paintlen && x_start + paintlen <= COLS); mvwaddnstr(edit, yval, x_start, - fileptr->data + start + x_start, paintlen); + converted + x_start, paintlen); } } else if (!searched_later_lines) { searched_later_lines = 1; /* 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, 1, &endmatch, 0)) + 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, yval, x_start, - fileptr->data + start + x_start, - COLS - x_start); + converted + x_start, + COLS - x_start); /* We painted to the end of the line, so * don't bother checking any more starts. */ break; } } start_col = startmatch.rm_so + 1; - } /* while start_col < start + COLS */ + } /* while start_col < endpos */ } /* if (tmp_color->end != NULL) */ skip_step_two: @@ -1169,43 +1254,39 @@ void edit_add(const filestruct *fileptr, int yval, int start || fileptr->lineno >= current->lineno)) { /* fileptr is at least partially selected. */ + const filestruct *top; + /* Either current or mark_beginbuf, whichever is first. */ + size_t top_x; + /* current_x or mark_beginx, corresponding to top. */ + const filestruct *bot; + size_t bot_x; int x_start; /* Starting column for mvwaddnstr. Zero-based. */ int paintlen; - /* number of chars to paint on this line. There are COLS + /* Number of chars to paint on this line. There are COLS * characters on a whole line. */ - if (mark_beginbuf == fileptr && current == fileptr) { - x_start = virt_mark_beginx < virt_cur_x ? virt_mark_beginx - : virt_cur_x; - paintlen = abs(virt_mark_beginx - virt_cur_x); - } else { - if (mark_beginbuf->lineno < fileptr->lineno || - current->lineno < fileptr->lineno) - x_start = 0; - else - x_start = mark_beginbuf == fileptr ? virt_mark_beginx - : virt_cur_x; + mark_order(&top, &top_x, &bot, &bot_x); + + if (top->lineno < fileptr->lineno || top_x < startpos) + top_x = startpos; + if (bot->lineno > fileptr->lineno || bot_x > endpos) + bot_x = endpos; - if (mark_beginbuf->lineno > fileptr->lineno || - current->lineno > fileptr->lineno) - paintlen = start + COLS; + /* the selected bit of fileptr is on this page */ + if (top_x < endpos && bot_x > startpos) { + assert(startpos <= top_x); + x_start = strnlenpt(fileptr->data + startpos, top_x - startpos); + + if (bot_x >= endpos) + paintlen = -1; /* Paint everything. */ else - paintlen = mark_beginbuf == fileptr ? virt_mark_beginx - : virt_cur_x; - } - x_start -= start; - if (x_start < 0) { - paintlen += x_start; - x_start = 0; - } - if (x_start + paintlen > COLS) - paintlen = COLS - x_start; - if (paintlen > 0) { + paintlen = strnlenpt(fileptr->data + top_x, bot_x - top_x); + + assert(x_start >= 0 && x_start <= strlen(converted)); + wattron(edit, A_REVERSE); - assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS); - mvwaddnstr(edit, yval, x_start, - fileptr->data + start + x_start, paintlen); + mvwaddnstr(edit, yval, x_start, converted + x_start, paintlen); wattroff(edit, A_REVERSE); } } @@ -1213,27 +1294,21 @@ void edit_add(const filestruct *fileptr, int yval, int start } /* Just update one line in the edit buffer. Basically a wrapper for - * edit_add(). If fileptr != current, then index is considered 0. + * edit_add(). + * + * If fileptr != current, then index is considered 0. * The line will be displayed starting with fileptr->data[index]. * Likely args are current_x or 0. */ -void update_line(filestruct *fileptr, int index) +void update_line(const filestruct *fileptr, size_t index) { int line; /* line in the edit window for CURSES calls */ -#ifndef NANO_SMALL - int virt_cur_x; - int virt_mark_beginx; -#endif - char *original; - /* The original string fileptr->data. */ char *converted; /* fileptr->data converted to have tabs and control characters * expanded. */ - size_t pos; size_t page_start; - if (fileptr == NULL) - return; + assert(fileptr != NULL); line = fileptr->lineno - edittop->lineno; @@ -1246,54 +1321,22 @@ void update_line(filestruct *fileptr, int index) /* First, blank out the line (at a minimum) */ mvwaddstr(edit, line, 0, hblank); - original = fileptr->data; - converted = charalloc(strlenpt(original) + 1); + /* Next, convert variables that index the line to their equivalent + * positions in the expanded line. */ + index = fileptr == current ? strnlenpt(fileptr->data, index) : 0; + page_start = get_page_start(index); - /* Next, convert all the tabs to spaces, so everything else is easy. - * Note the internal speller sends us index == -1. */ - index = fileptr == current && index > 0 ? strnlenpt(original, index) : 0; -#ifndef NANO_SMALL - virt_cur_x = fileptr == current ? strnlenpt(original, current_x) : current_x; - virt_mark_beginx = fileptr == mark_beginbuf ? strnlenpt(original, mark_beginx) : mark_beginx; -#endif - - pos = 0; - for (; *original != '\0'; original++) { - if (*original == '\t') - do { - converted[pos++] = ' '; - } while (pos % tabsize); - else if (is_cntrl_char(*original)) { - converted[pos++] = '^'; - if (*original == 127) - converted[pos++] = '?'; - else if (*original == '\n') - /* Treat newlines (ASCII 10's) embedded in a line as encoded - * nulls (ASCII 0's); the line in question should be run - * through unsunder() before reaching here */ - converted[pos++] = '@'; - else - converted[pos++] = *original + 64; - } else - converted[pos++] = *original; - } - converted[pos] = '\0'; + /* Expand the line, replacing Tab by spaces, and control characters + * by their display form. */ + converted = display_string(fileptr->data, page_start, COLS); /* Now, paint the line */ - original = fileptr->data; - fileptr->data = converted; - page_start = get_page_start(index); - edit_add(fileptr, line, page_start -#ifndef NANO_SMALL - , virt_mark_beginx, virt_cur_x -#endif - ); + edit_add(fileptr, converted, line, page_start); free(converted); - fileptr->data = original; if (page_start > 0) mvwaddch(edit, line, 0, '$'); - if (pos > page_start + COLS) + if (strlenpt(fileptr->data) > page_start + COLS) mvwaddch(edit, line, COLS - 1, '$'); } @@ -1328,8 +1371,8 @@ void center_cursor(void) /* Refresh the screen without changing the position of lines. */ void edit_refresh(void) { - /* Neither of these conditions should occur, but they do. edittop is - * NULL when you open an existing file on the command line, and + /* Neither of these conditions should occur, but they do. edittop + * is NULL when you open an existing file on the command line, and * ENABLE_COLOR is defined. Yuck. */ if (current == NULL) return; @@ -1346,7 +1389,8 @@ void edit_refresh(void) else { int nlines = 0; - /* Don't make the cursor jump around the screen whilst updating */ + /* Don't make the cursor jump around the screen whilst + * updating. */ leaveok(edit, TRUE); editbot = edittop; @@ -1362,7 +1406,7 @@ void edit_refresh(void) nlines++; } /* What the hell are we expecting to update the screen if this - isn't here? Luck? */ + * isn't here? Luck? */ wrefresh(edit); leaveok(edit, FALSE); } @@ -1377,10 +1421,8 @@ void edit_refresh_clearok(void) clearok(edit, FALSE); } -/* - * Nice generic routine to update the edit buffer, given a pointer to the - * file struct =) - */ +/* Nice generic routine to update the edit buffer, given a pointer to the + * file struct =) */ void edit_update(filestruct *fileptr, topmidnone location) { if (fileptr == NULL) @@ -1396,14 +1438,12 @@ void edit_update(filestruct *fileptr, topmidnone location) edit_refresh(); } -/* - * Ask a question on the statusbar. Answer will be stored in answer +/* Ask a question on the statusbar. Answer will be stored in answer * global. Returns -1 on aborted enter, -2 on a blank string, and 0 * otherwise, the valid shortcut key caught. Def is any editable text we * want to put up by default. * - * New arg tabs tells whether or not to allow tab completion. - */ + * New arg tabs tells whether or not to allow tab completion. */ int statusq(int tabs, const shortcut *s, const char *def, #ifndef NANO_SMALL historyheadtype *which_history, @@ -1469,7 +1509,7 @@ int statusq(int tabs, const shortcut *s, const char *def, #ifndef DISABLE_TABCOMP /* if we've done tab completion, there might be a list of filename matches on the edit window at this point; make sure - they're cleared off */ + they're cleared off. */ if (list) edit_refresh(); #endif @@ -1477,11 +1517,9 @@ int statusq(int tabs, const shortcut *s, const char *def, return ret; } -/* - * Ask a simple yes/no question on the statusbar. Returns 1 for Y, 0 +/* Ask a simple yes/no question on the statusbar. Returns 1 for Y, 0 * for N, 2 for All (if all is nonzero when passed in) and -1 for abort - * (^C). - */ + * (^C). */ int do_yesno(int all, int leavecursor, const char *msg, ...) { va_list ap; @@ -1491,18 +1529,19 @@ int do_yesno(int all, int leavecursor, const char *msg, ...) const char *nostr; /* Same for no */ const char *allstr; /* And all, surprise! */ - /* Yes, no and all are strings of any length. Each string consists of - all characters accepted as a valid character for that value. - The first value will be the one displayed in the shortcuts. */ + /* Yes, no and all are strings of any length. Each string consists + * of all characters accepted as a valid character for that value. + * The first value will be the one displayed in the shortcuts. */ yesstr = _("Yy"); nostr = _("Nn"); allstr = _("Aa"); - /* Remove gettext call for keybindings until we clear the thing up */ + /* Remove gettext call for keybindings until we clear the thing + * up. */ if (!ISSET(NO_HELP)) { - char shortstr[3]; /* Temp string for Y, N, A */ + char shortstr[3]; /* Temp string for Y, N, A. */ - /* Write the bottom of the screen */ + /* Write the bottom of the screen. */ blank_bottombars(); sprintf(shortstr, " %c", yesstr[0]); @@ -1555,20 +1594,21 @@ int do_yesno(int all, int leavecursor, const char *msg, ...) mevent.y >= editwinrows + 3) { int x = mevent.x /= 16; /* Did we click in the first column of shortcuts, or the - second? */ + * second? */ int y = mevent.y - editwinrows - 3; /* Did we click in the first row of shortcuts? */ assert(0 <= x && x <= 1 && 0 <= y && y <= 1); /* x = 0 means they clicked Yes or No. - y = 0 means Yes or All. */ + * y = 0 means Yes or All. */ ok = -2 * x * y + x - y + 1; if (ok == 2 && !all) ok = -2; } #endif - /* Look for the kbinput in the yes, no and (optionally) all str */ + /* Look for the kbinput in the yes, no and (optionally) all + * str. */ else if (strchr(yesstr, kbinput) != NULL) ok = 1; else if (strchr(nostr, kbinput) != NULL) @@ -1608,52 +1648,52 @@ void display_main_list(void) void statusbar(const char *msg, ...) { va_list ap; - char *foo; - int start_x = 0; - size_t foo_len; 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(). */ + /* Curses mode is turned off. If we use wmove() now, it will muck + * up the terminal settings. So we just use vfprintf(). */ if (curses_ended) { vfprintf(stderr, msg, ap); va_end(ap); return; } - assert(COLS >= 4); - foo = charalloc(COLS - 3); - - vsnprintf(foo, COLS - 3, msg, ap); - va_end(ap); - - foo[COLS - 4] = '\0'; - foo_len = strlen(foo); - start_x = (COLS - foo_len - 4) / 2; - - /* Blank out line */ + /* Blank out the line. */ blank_statusbar(); - wmove(bottomwin, 0, start_x); - - wattron(bottomwin, A_REVERSE); - - waddstr(bottomwin, "[ "); - waddstr(bottomwin, foo); - free(foo); - waddstr(bottomwin, " ]"); - - wattroff(bottomwin, A_REVERSE); - - wrefresh(bottomwin); + if (COLS >= 4) { + char *bar; + char *foo; + int start_x = 0; + size_t foo_len; + bar = charalloc(COLS - 3); + vsnprintf(bar, COLS - 3, msg, ap); + va_end(ap); + foo = display_string(bar, 0, COLS - 4); + free(bar); + foo_len = strlen(foo); + start_x = (COLS - foo_len - 4) / 2; + + wmove(bottomwin, 0, start_x); + wattron(bottomwin, A_REVERSE); + + waddstr(bottomwin, "[ "); + waddstr(bottomwin, foo); + free(foo); + waddstr(bottomwin, " ]"); + wattroff(bottomwin, A_REVERSE); + wnoutrefresh(bottomwin); + wrefresh(edit); + /* Leave the cursor at its position in the edit window, not + * in the statusbar. */ + } SET(DISABLE_CURPOS); statblank = 26; } -/* - * If constant is false, the user typed ^C so we unconditionally display +/* If constant is false, the user typed ^C so we unconditionally display * the cursor position. Otherwise, we display it only if the character * position changed, and DISABLE_CURPOS is not set. * @@ -1685,9 +1725,9 @@ int do_cursorpos(int constant) return 0; } - /* if constant is false, display the position on the statusbar - unconditionally; otherwise, only display the position when the - character values have changed */ + /* If constant is false, display the position on the statusbar + * unconditionally; otherwise, only display the position when the + * character values have changed. */ if (!constant || old_i != i || old_totsize != totsize) { unsigned long xpt = xplustabs() + 1; unsigned long cur_len = strlenpt(current->data) + 1; @@ -1728,12 +1768,12 @@ int line_len(const char *ptr) /* Don't wrap at the first of two spaces following a period. */ if (*ptr == ' ' && *(ptr + 1) == ' ') j++; - /* Don't print half a word if we've run out of space */ + /* Don't print half a word if we've run out of space. */ while (*ptr != ' ' && j > 0) { ptr--; j--; } - /* Word longer than COLS - 5 chars just gets broken */ + /* Word longer than COLS - 5 chars just gets broken. */ if (j == 0) j = COLS - 5; } @@ -1741,8 +1781,8 @@ int line_len(const char *ptr) return j; } -/* Our shortcut-list-compliant help function, which is - * better than nothing, and dynamic! */ +/* Our shortcut-list-compliant help function, which is better than + * nothing, and dynamic! */ int do_help(void) { #ifndef DISABLE_HELP @@ -1755,7 +1795,7 @@ int do_help(void) wattroff(bottomwin, A_REVERSE); blank_statusbar(); - /* set help_text as the string to display */ + /* Set help_text as the string to display. */ help_init(); assert(help_text != NULL); @@ -1765,8 +1805,8 @@ int do_help(void) if (ISSET(NO_HELP)) { - /* Well, if we're going to do this, we should at least - do it the right way */ + /* Well, if we're going to do this, we should at least do it the + * right way. */ no_help_flag = 1; UNSET(NO_HELP); window_init(); @@ -1801,7 +1841,8 @@ int do_help(void) break; } - /* Calculate where in the text we should be, based on the page */ + /* Calculate where in the text we should be, based on the + * page. */ for (i = 1; i < page * (editwinrows - 1); i++) { ptr += line_len(ptr); if (*ptr == '\n') @@ -1837,7 +1878,7 @@ int do_help(void) edit_refresh(); /* The help_init() at the beginning allocated help_text, which has - now been written to screen. */ + * now been written to the screen. */ free(help_text); help_text = NULL; @@ -1848,49 +1889,31 @@ int do_help(void) return 1; } -/* Highlight the current word being replaced or spell checked. */ +/* Highlight the current word being replaced or spell checked. We + * expect word to have tabs and control characters expanded. */ void do_replace_highlight(int highlight_flag, const char *word) { - char *highlight_word = NULL; - int x, y, word_len; - - highlight_word = - mallocstrcpy(highlight_word, ¤t->data[current_x]); + int y = xplustabs(); + size_t word_len = strlen(word); -#ifdef HAVE_REGEX_H - if (ISSET(USE_REGEXP)) - /* if we're using regexps, the highlight is the length of the - search result, not the length of the regexp string */ - word_len = regmatches[0].rm_eo - regmatches[0].rm_so; - else -#endif - word_len = strlen(word); - - highlight_word[word_len] = '\0'; - - /* adjust output when word extends beyond screen */ - - x = xplustabs(); - y = get_page_start(x) + COLS; - - if ((COLS - (y - x) + word_len) > COLS) { - highlight_word[y - x - 1] = '$'; - highlight_word[y - x] = '\0'; - } - - /* OK display the output */ + y = get_page_start(y) + COLS - y; + /* Now y is the number of characters we can display on this + * line. */ reset_cursor(); if (highlight_flag) wattron(edit, A_REVERSE); - waddstr(edit, highlight_word); + waddnstr(edit, word, y - 1); + + if (word_len > y) + waddch(edit, '$'); + else if (word_len == y) + waddch(edit, word[word_len - 1]); if (highlight_flag) wattroff(edit, A_REVERSE); - - free(highlight_word); } /* Fix editbot, based on the assumption that edittop is correct. */ @@ -1904,8 +1927,9 @@ void fix_editbot(void) } #ifdef DEBUG -/* Dump the current file structure to stderr */ -void dump_buffer(const filestruct *inptr) { +/* Dump the passed-in file structure to stderr. */ +void dump_buffer(const filestruct *inptr) +{ if (inptr == fileage) fprintf(stderr, "Dumping file buffer to stderr...\n"); else if (inptr == cutbuffer) @@ -1918,9 +1942,8 @@ void dump_buffer(const filestruct *inptr) { inptr = inptr->next; } } -#endif /* DEBUG */ -#ifdef DEBUG +/* Dump the file structure to stderr in reverse. */ void dump_buffer_reverse(void) { const filestruct *fileptr = filebot; -- 2.39.5