/* Delete one character. */ void do_statusbar_delete(void) { statusbar_pww = statusbar_xplustabs(); if (answer[statusbar_x] != '\0') { int char_buf_len = parse_mbchar(answer + statusbar_x, NULL, NULL); size_t line_len = strlen(answer + statusbar_x); assert(statusbar_x < strlen(answer)); charmove(answer + statusbar_x, answer + statusbar_x + char_buf_len, strlen(answer) - statusbar_x - char_buf_len + 1); null_at(&answer, statusbar_x + line_len - char_buf_len); update_statusbar_line(answer, statusbar_x); } }
/* Get a string of input at the statusbar prompt. This should only be * called from do_prompt(). */ functionptrtype get_prompt_string(int *actual, bool allow_tabs, #ifndef DISABLE_TABCOMP bool allow_files, bool *list, #endif const char *curranswer, #ifndef DISABLE_HISTORIES linestruct **history_list, #endif void (*refresh_func)(void)) { int kbinput = ERR; bool ran_func, finished; size_t curranswer_len; functionptrtype func; #ifndef DISABLE_TABCOMP bool tabbed = FALSE; /* Whether we've pressed Tab. */ #endif #ifndef DISABLE_HISTORIES char *history = NULL; /* The current history string. */ char *magichistory = NULL; /* The temporary string typed at the bottom of the history, if * any. */ #ifndef DISABLE_TABCOMP int last_kbinput = ERR; /* The key we pressed before the current key. */ size_t complete_len = 0; /* The length of the original string that we're trying to * tab complete, if any. */ #endif #endif /* !DISABLE_HISTORIES */ answer = mallocstrcpy(answer, curranswer); curranswer_len = strlen(answer); /* If reset_statusbar_x is TRUE, restore statusbar_x and * statusbar_pww to what they were before this prompt. Then, if * statusbar_x is uninitialized or past the end of curranswer, put * statusbar_x at the end of the string and update statusbar_pww * based on it. We do these things so that the cursor position * stays at the right place if a prompt-changing toggle is pressed, * or if this prompt was started from another prompt and we cancel * out of it. */ if (reset_statusbar_x) { statusbar_x = old_statusbar_x; statusbar_pww = old_pww; } if (statusbar_x == (size_t)-1 || statusbar_x > curranswer_len) { statusbar_x = curranswer_len; statusbar_pww = statusbar_xplustabs(); } #ifdef DEBUG fprintf(stderr, "get_prompt_string: answer = \"%s\", statusbar_x = %lu\n", answer, (unsigned long) statusbar_x); #endif update_statusbar_line(answer, statusbar_x); /* Refresh the edit window and the statusbar before getting * input. */ wnoutrefresh(edit); wnoutrefresh(bottomwin); /* If we're using restricted mode, we aren't allowed to change the * name of the current file once it has one, because that would * allow writing to files not specified on the command line. In * this case, disable all keys that would change the text if the * filename isn't blank and we're at the "Write File" prompt. */ while (TRUE) { kbinput = do_statusbar_input(&ran_func, &finished, refresh_func); assert(statusbar_x <= strlen(answer)); #ifndef NANO_TINY if (kbinput == KEY_WINCH) { refresh_func(); update_statusbar_line(answer, statusbar_x); continue; } #endif func = func_from_key(&kbinput); if (func == do_cancel || func == do_enter_void) break; #ifndef DISABLE_TABCOMP if (func != do_tab) tabbed = FALSE; if (func == do_tab) { #ifndef DISABLE_HISTORIES if (history_list != NULL) { if (last_kbinput != sc_seq_or(do_tab, NANO_CONTROL_I)) complete_len = strlen(answer); if (complete_len > 0) { answer = mallocstrcpy(answer, get_history_completion(history_list, answer, complete_len)); statusbar_x = strlen(answer); } } else #endif if (allow_tabs) answer = input_tab(answer, allow_files, &statusbar_x, &tabbed, refresh_func, list); update_statusbar_line(answer, statusbar_x); } else #endif /* !DISABLE_TABCOMP */ #ifndef DISABLE_HISTORIES if (func == get_history_older_void) { if (history_list != NULL) { /* If we're scrolling up at the bottom of the history list * and answer isn't blank, save answer in magichistory. */ if ((*history_list)->next == NULL && answer[0] != '\0') magichistory = mallocstrcpy(magichistory, answer); /* Get the older search from the history list and save it in * answer. If there is no older search, don't do anything. */ if ((history = get_history_older(history_list)) != NULL) { answer = mallocstrcpy(answer, history); statusbar_x = strlen(answer); } update_statusbar_line(answer, statusbar_x); /* This key has a shortcut-list entry when it's used to * move to an older search, which means that finished has * been set to TRUE. Set it back to FALSE here, so that * we aren't kicked out of the statusbar prompt. */ finished = FALSE; } } else if (func == get_history_newer_void) { if (history_list != NULL) { /* Get the newer search from the history list and save it in * answer. If there is no newer search, don't do anything. */ if ((history = get_history_newer(history_list)) != NULL) { answer = mallocstrcpy(answer, history); statusbar_x = strlen(answer); } /* If, after scrolling down, we're at the bottom of the * history list, answer is blank, and magichistory is set, * save magichistory in answer. */ if ((*history_list)->next == NULL && *answer == '\0' && magichistory != NULL) { answer = mallocstrcpy(answer, magichistory); statusbar_x = strlen(answer); } update_statusbar_line(answer, statusbar_x); /* This key has a shortcut-list entry when it's used to * move to a newer search, which means that finished has * been set to TRUE. Set it back to FALSE here, so that * we aren't kicked out of the statusbar prompt. */ finished = FALSE; } } else #endif /* !DISABLE_HISTORIES */ if (func == do_help_void) { update_statusbar_line(answer, statusbar_x); /* This key has a shortcut-list entry when it's used to go to * the help browser or display a message indicating that help * is disabled, which means that finished has been set to TRUE. * Set it back to FALSE here, so that we aren't kicked out of * the statusbar prompt. */ finished = FALSE; } /* If we have a shortcut with an associated function, break out if * we're finished after running or trying to run the function. */ if (finished) break; #if !defined(DISABLE_HISTORIES) && !defined(DISABLE_TABCOMP) last_kbinput = kbinput; #endif reset_statusbar_cursor(); wnoutrefresh(bottomwin); } #ifndef DISABLE_HISTORIES /* Set the current position in the history list to the bottom, * and free magichistory if we need to. */ if (history_list != NULL) { history_reset(*history_list); free(magichistory); } #endif /* We've finished putting in an answer or run a normal shortcut's * associated function, so reset statusbar_x and statusbar_pww. If * we've finished putting in an answer, reset the statusbar cursor * position too. */ if (func) { if (func == do_cancel || func == do_enter_void || ran_func) { statusbar_x = old_statusbar_x; statusbar_pww = old_pww; if (!ran_func) reset_statusbar_x = TRUE; /* Otherwise, we're still putting in an answer or a shortcut with * an associated function, so leave the statusbar cursor position * alone. */ } else reset_statusbar_x = FALSE; } *actual = kbinput; return func; }
/* Move to the previous word in the prompt text. If allow_punct is * TRUE, treat punctuation as part of a word. Return TRUE if we started * on a word, and FALSE otherwise. */ bool do_statusbar_prev_word(bool allow_punct) { size_t pww_save = statusbar_pww; char *char_mb; int char_mb_len; bool begin_line = FALSE, started_on_word = FALSE; assert(answer != NULL); char_mb = charalloc(mb_cur_max()); /* Move backward until we find the character before the first letter * of the current word. */ while (!begin_line) { char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL); /* If we've found it, stop moving backward through the current * line. */ if (!is_word_mbchar(char_mb, allow_punct)) break; /* If we haven't found it, then we've started on a word, so set * started_on_word to TRUE. */ started_on_word = TRUE; if (statusbar_x == 0) begin_line = TRUE; else statusbar_x = move_mbleft(answer, statusbar_x); } /* Move backward until we find the last letter of the previous * word. */ if (statusbar_x == 0) begin_line = TRUE; else statusbar_x = move_mbleft(answer, statusbar_x); while (!begin_line) { char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL); /* If we've found it, stop moving backward through the current * line. */ if (is_word_mbchar(char_mb, allow_punct)) break; if (statusbar_x == 0) begin_line = TRUE; else statusbar_x = move_mbleft(answer, statusbar_x); } /* If we've found it, move backward until we find the character * before the first letter of the previous word. */ if (!begin_line) { if (statusbar_x == 0) begin_line = TRUE; else statusbar_x = move_mbleft(answer, statusbar_x); while (!begin_line) { char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL); /* If we've found it, stop moving backward through the * current line. */ if (!is_word_mbchar(char_mb, allow_punct)) break; if (statusbar_x == 0) begin_line = TRUE; else statusbar_x = move_mbleft(answer, statusbar_x); } /* If we've found it, move forward to the first letter of the * previous word. */ if (!begin_line) statusbar_x += char_mb_len; } free(char_mb); statusbar_pww = statusbar_xplustabs(); if (need_statusbar_update(pww_save)) update_statusbar_line(answer, statusbar_x); /* Return whether we started on a word. */ return started_on_word; }
/* Move to the next word in the prompt text. If allow_punct is TRUE, * treat punctuation as part of a word. Return TRUE if we started on a * word, and FALSE otherwise. */ bool do_statusbar_next_word(bool allow_punct) { size_t pww_save = statusbar_pww; char *char_mb; int char_mb_len; bool end_line = FALSE, started_on_word = FALSE; assert(answer != NULL); char_mb = charalloc(mb_cur_max()); /* Move forward until we find the character after the last letter of * the current word. */ while (!end_line) { char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL); /* If we've found it, stop moving forward through the current * line. */ if (!is_word_mbchar(char_mb, allow_punct)) break; /* If we haven't found it, then we've started on a word, so set * started_on_word to TRUE. */ started_on_word = TRUE; if (answer[statusbar_x] == '\0') end_line = TRUE; else statusbar_x += char_mb_len; } /* Move forward until we find the first letter of the next word. */ if (answer[statusbar_x] == '\0') end_line = TRUE; else statusbar_x += char_mb_len; while (!end_line) { char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL); /* If we've found it, stop moving forward through the current * line. */ if (is_word_mbchar(char_mb, allow_punct)) break; if (answer[statusbar_x] == '\0') end_line = TRUE; else statusbar_x += char_mb_len; } free(char_mb); statusbar_pww = statusbar_xplustabs(); if (need_statusbar_update(pww_save)) update_statusbar_line(answer, statusbar_x); /* Return whether we started on a word. */ return started_on_word; }
/* The user typed output_len multibyte characters. Add them to the * statusbar prompt, setting got_enter to TRUE if we get a newline, and * filtering out all ASCII control characters if allow_cntrls is * TRUE. */ void do_statusbar_output(char *output, size_t output_len, bool *got_enter, bool allow_cntrls) { size_t answer_len, i = 0; char *char_buf = charalloc(mb_cur_max()); int char_buf_len; assert(answer != NULL); answer_len = strlen(answer); *got_enter = FALSE; while (i < output_len) { /* If allow_cntrls is TRUE, convert nulls and newlines * properly. */ if (allow_cntrls) { /* Null to newline, if needed. */ if (output[i] == '\0') output[i] = '\n'; /* Newline to Enter, if needed. */ else if (output[i] == '\n') { /* Set got_enter to TRUE to indicate that we got the * Enter key, put back the rest of the characters in * output so that they can be parsed and output again, * and get out. */ *got_enter = TRUE; unparse_kbinput(output + i, output_len - i); return; } } /* Interpret the next multibyte character. */ char_buf_len = parse_mbchar(output + i, char_buf, NULL); i += char_buf_len; /* If allow_cntrls is FALSE, filter out an ASCII control * character. */ if (!allow_cntrls && is_ascii_cntrl_char(*(output + i - char_buf_len))) continue; /* More dangerousness fun. =) */ answer = charealloc(answer, answer_len + (char_buf_len * 2)); assert(statusbar_x <= answer_len); charmove(answer + statusbar_x + char_buf_len, answer + statusbar_x, answer_len - statusbar_x + char_buf_len); strncpy(answer + statusbar_x, char_buf, char_buf_len); answer_len += char_buf_len; statusbar_x += char_buf_len; } free(char_buf); statusbar_pww = statusbar_xplustabs(); update_statusbar_line(answer, statusbar_x); }
/* Search for a match to the bracket at the current cursor position, if * there is one. */ void do_statusbar_find_bracket(void) { size_t statusbar_x_save, pww_save; const char *ch; /* The location in matchbrackets of the bracket at the current * cursor position. */ int ch_len; /* The length of ch in bytes. */ const char *wanted_ch; /* The location in matchbrackets of the bracket complementing * the bracket at the current cursor position. */ int wanted_ch_len; /* The length of wanted_ch in bytes. */ char *bracket_set; /* The pair of characters in ch and wanted_ch. */ size_t i; /* Generic loop variable. */ size_t matchhalf; /* The number of single-byte characters in one half of * matchbrackets. */ size_t mbmatchhalf; /* The number of multibyte characters in one half of * matchbrackets. */ size_t count = 1; /* The initial bracket count. */ bool reverse; /* The direction we search. */ char *found_ch; /* The character we find. */ assert(mbstrlen(matchbrackets) % 2 == 0); ch = answer + statusbar_x; if (ch == '\0' || (ch = mbstrchr(matchbrackets, ch)) == NULL) return; /* Save where we are. */ statusbar_x_save = statusbar_x; pww_save = statusbar_pww; /* If we're on an opening bracket, which must be in the first half * of matchbrackets, we want to search forwards for a closing * bracket. If we're on a closing bracket, which must be in the * second half of matchbrackets, we want to search backwards for an * opening bracket. */ matchhalf = 0; mbmatchhalf = mbstrlen(matchbrackets) / 2; for (i = 0; i < mbmatchhalf; i++) matchhalf += parse_mbchar(matchbrackets + matchhalf, NULL, NULL); reverse = ((ch - matchbrackets) >= matchhalf); /* If we're on an opening bracket, set wanted_ch to the character * that's matchhalf characters after ch. If we're on a closing * bracket, set wanted_ch to the character that's matchhalf * characters before ch. */ wanted_ch = ch; while (mbmatchhalf > 0) { if (reverse) wanted_ch = matchbrackets + move_mbleft(matchbrackets, wanted_ch - matchbrackets); else wanted_ch += move_mbright(wanted_ch, 0); mbmatchhalf--; } ch_len = parse_mbchar(ch, NULL, NULL); wanted_ch_len = parse_mbchar(wanted_ch, NULL, NULL); /* Fill bracket_set in with the values of ch and wanted_ch. */ bracket_set = charalloc((mb_cur_max() * 2) + 1); strncpy(bracket_set, ch, ch_len); strncpy(bracket_set + ch_len, wanted_ch, wanted_ch_len); null_at(&bracket_set, ch_len + wanted_ch_len); found_ch = charalloc(mb_cur_max() + 1); while (TRUE) { if (find_statusbar_bracket_match(reverse, bracket_set)) { /* If we found an identical bracket, increment count. If we * found a complementary bracket, decrement it. */ parse_mbchar(answer + statusbar_x, found_ch, NULL); count += (strncmp(found_ch, ch, ch_len) == 0) ? 1 : -1; /* If count is zero, we've found a matching bracket. Update * the statusbar prompt and get out. */ if (count == 0) { if (need_statusbar_horizontal_update(pww_save)) update_statusbar_line(answer, statusbar_x); break; } } else { /* We didn't find either an opening or closing bracket. * Restore where we were, and get out. */ statusbar_x = statusbar_x_save; statusbar_pww = pww_save; break; } } /* Clean up. */ free(bracket_set); free(found_ch); }