/* 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; }
/* Return the index in buf of the beginning of the multibyte character * before the one at pos. */ size_t move_mbleft(const char *buf, size_t pos) { size_t before, char_len = 0; assert(buf != NULL && pos <= strlen(buf)); /* There is no library function to move backward one multibyte * character. So we just start groping for one at the farthest * possible point. */ if (mb_cur_max() > pos) before = 0; else before = pos - mb_cur_max(); while (before < pos) { char_len = parse_mbchar(buf + before, NULL, NULL); before += char_len; } return before - char_len; }
/* Is the word starting at position pos in buf a whole word? */ bool is_whole_word(size_t pos, const char *buf, const char *word) { char *p = charalloc(mb_cur_max()), *r = charalloc(mb_cur_max()); size_t word_end = pos + strlen(word); bool retval; assert(buf != NULL && pos <= strlen(buf) && word != NULL); parse_mbchar(buf + move_mbleft(buf, pos), p, NULL); parse_mbchar(buf + word_end, r, NULL); /* If we're at the beginning of the line or the character before the * word isn't a non-punctuation "word" character, and if we're at * the end of the line or the character after the word isn't a * non-punctuation "word" character, we have a whole word. */ retval = (pos == 0 || !is_word_mbchar(p, FALSE)) && (word_end == strlen(buf) || !is_word_mbchar(r, FALSE)); free(p); free(r); return retval; }
/* Return TRUE when the given multibyte character c is a word-forming * character (that is: alphanumeric, or specified in wordchars, or * punctuation when allow_punct is TRUE), and FALSE otherwise. */ bool is_word_mbchar(const char *c, bool allow_punct) { if (*c == '\0') return FALSE; if (is_alnum_mbchar(c)) return TRUE; if (word_chars != NULL && *word_chars != '\0') { char symbol[mb_cur_max() + 1]; int symlen = parse_mbchar(c, symbol, NULL); symbol[symlen] = '\0'; return (strstr(word_chars, symbol) != NULL); } return (allow_punct && is_punct_mbchar(c)); }
/* Ask a question on the statusbar. The prompt will be stored in the * static prompt, which should be NULL initially, and the answer will be * stored in the answer global. Returns -1 on aborted enter, -2 on a * blank string, and 0 otherwise, the valid shortcut key caught. * curranswer is any editable text that we want to put up by default, * and refresh_func is the function we want to call to refresh the edit * window. * * The allow_tabs parameter indicates whether we should allow tabs to be * interpreted. The allow_files parameter indicates whether we should * allow all files (as opposed to just directories) to be tab * completed. */ int do_prompt(bool allow_tabs, #ifndef DISABLE_TABCOMP bool allow_files, #endif int menu, const char *curranswer, #ifndef DISABLE_HISTORIES linestruct **history_list, #endif void (*refresh_func)(void), const char *msg, ...) { va_list ap; int retval; functionptrtype func; #ifndef DISABLE_TABCOMP bool list = FALSE; #endif /* prompt has been freed and set to NULL unless the user resized * while at the statusbar prompt. */ free(prompt); prompt = charalloc(((COLS - 4) * mb_cur_max()) + 1); bottombars(menu); va_start(ap, msg); vsnprintf(prompt, (COLS - 4) * mb_cur_max(), msg, ap); va_end(ap); null_at(&prompt, actual_x(prompt, COLS - 4)); func = get_prompt_string(&retval, allow_tabs, #ifndef DISABLE_TABCOMP allow_files, &list, #endif curranswer, #ifndef DISABLE_HISTORIES history_list, #endif refresh_func); free(prompt); prompt = NULL; /* We're done with the prompt, so save the statusbar cursor * position. */ old_statusbar_x = statusbar_x; old_pww = statusbar_pww; /* If we left the prompt via Cancel or Enter, set the return value * properly. */ if (func == do_cancel) retval = -1; else if (func == do_enter_void) retval = (*answer == '\0') ? -2 : 0; blank_statusbar(); wnoutrefresh(bottomwin); #ifdef DEBUG fprintf(stderr, "answer = \"%s\"\n", answer); #endif #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 that they're * cleared off. */ if (list) refresh_func(); #endif return retval; }
/* 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); }
/* Move to the previous word in the file. If allow_punct is TRUE, treat * punctuation as part of a word. If allow_update is TRUE, update the * screen afterwards. Return TRUE if we started on a word, and FALSE * otherwise. */ bool do_prev_word(bool allow_punct, bool allow_update) { size_t pww_save = openfile->placewewant; filestruct *current_save = openfile->current; char *char_mb; int char_mb_len; bool begin_line = FALSE, started_on_word = FALSE; assert(openfile->current != NULL && openfile->current->data != 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(openfile->current->data + openfile->current_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 (openfile->current_x == 0) begin_line = TRUE; else openfile->current_x = move_mbleft(openfile->current->data, openfile->current_x); } /* Move backward until we find the last letter of the previous * word. */ if (openfile->current_x == 0) begin_line = TRUE; else openfile->current_x = move_mbleft(openfile->current->data, openfile->current_x); for (; openfile->current != NULL; openfile->current = openfile->current->prev) { while (!begin_line) { char_mb_len = parse_mbchar(openfile->current->data + openfile->current_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 (openfile->current_x == 0) begin_line = TRUE; else openfile->current_x = move_mbleft(openfile->current->data, openfile->current_x); } /* If we've found it, stop moving backward to the ends of * previous lines. */ if (!begin_line) break; if (openfile->current != openfile->fileage) { begin_line = FALSE; openfile->current_x = strlen(openfile->current->prev->data); } } /* If we haven't found it, move to the beginning of the file. */ if (openfile->current == NULL) openfile->current = openfile->fileage; /* If we've found it, move backward until we find the character * before the first letter of the previous word. */ else if (!begin_line) { if (openfile->current_x == 0) begin_line = TRUE; else openfile->current_x = move_mbleft(openfile->current->data, openfile->current_x); while (!begin_line) { char_mb_len = parse_mbchar(openfile->current->data + openfile->current_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 (openfile->current_x == 0) begin_line = TRUE; else openfile->current_x = move_mbleft(openfile->current->data, openfile->current_x); } /* If we've found it, move forward to the first letter of the * previous word. */ if (!begin_line) openfile->current_x += char_mb_len; } free(char_mb); openfile->placewewant = xplustabs(); /* If allow_update is TRUE, update the screen. */ if (allow_update) edit_redraw(current_save, pww_save); /* Return whether we started on a word. */ return started_on_word; }
/* Move to the next word in the file. If allow_punct is TRUE, treat * punctuation as part of a word. If allow_update is TRUE, update the * screen afterwards. Return TRUE if we started on a word, and FALSE * otherwise. */ bool do_next_word(bool allow_punct, bool allow_update) { size_t pww_save = openfile->placewewant; filestruct *current_save = openfile->current; char *char_mb; int char_mb_len; bool end_line = FALSE, started_on_word = FALSE; assert(openfile->current != NULL && openfile->current->data != 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(openfile->current->data + openfile->current_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 (openfile->current->data[openfile->current_x] == '\0') end_line = TRUE; else openfile->current_x += char_mb_len; } /* Move forward until we find the first letter of the next word. */ if (openfile->current->data[openfile->current_x] == '\0') end_line = TRUE; else openfile->current_x += char_mb_len; for (; openfile->current != NULL; openfile->current = openfile->current->next) { while (!end_line) { char_mb_len = parse_mbchar(openfile->current->data + openfile->current_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 (openfile->current->data[openfile->current_x] == '\0') end_line = TRUE; else openfile->current_x += char_mb_len; } /* If we've found it, stop moving forward to the beginnings of * subsequent lines. */ if (!end_line) break; if (openfile->current != openfile->filebot) { end_line = FALSE; openfile->current_x = 0; } } free(char_mb); /* If we haven't found it, move to the end of the file. */ if (openfile->current == NULL) openfile->current = openfile->filebot; openfile->placewewant = xplustabs(); /* If allow_update is TRUE, update the screen. */ if (allow_update) edit_redraw(current_save, pww_save); /* Return whether we started on a word. */ return started_on_word; }
/* Search for a match to the bracket at the current cursor position, if * there is one. */ void do_find_bracket(void) { linestruct *current_save; size_t current_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 = openfile->current->data + openfile->current_x; if (ch == '\0' || (ch = mbstrchr(matchbrackets, ch)) == NULL) { statusbar(_("Not a bracket")); return; } /* Save where we are. */ current_save = openfile->current; current_x_save = openfile->current_x; pww_save = openfile->placewewant; /* 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_bracket_match(reverse, bracket_set)) { /* If we found an identical bracket, increment count. If we * found a complementary bracket, decrement it. */ parse_mbchar(openfile->current->data + openfile->current_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 screen and get out. */ if (count == 0) { edit_redraw(current_save, pww_save); break; } } else { /* We didn't find either an opening or closing bracket. * Indicate this, restore where we were, and get out. */ statusbar(_("No matching bracket")); openfile->current = current_save; openfile->current_x = current_x_save; openfile->placewewant = pww_save; break; } } /* Clean up. */ free(bracket_set); free(found_ch); }
/* Allocate space for the help text for the current menu, and concatenate * the different pieces of text into it. */ void help_init(void) { size_t allocsize = 0; /* Space needed for help_text. */ const char *htx[3]; /* Untranslated help introduction. We break it up into three chunks * in case the full string is too long for the compiler to handle. */ char *ptr; const subnfunc *f; const sc *s; #ifndef NANO_TINY bool old_whitespace = ISSET(WHITESPACE_DISPLAY); UNSET(WHITESPACE_DISPLAY); #endif /* First, set up the initial help text for the current function. */ if (currmenu == MWHEREIS || currmenu == MREPLACE || currmenu == MREPLACEWITH) { htx[0] = N_("Search Command Help Text\n\n " "Enter the words or characters you would like to " "search for, and then press Enter. If there is a " "match for the text you entered, the screen will be " "updated to the location of the nearest match for the " "search string.\n\n The previous search string will be " "shown in brackets after the search prompt. Hitting " "Enter without entering any text will perform the " "previous search. "); htx[1] = N_("If you have selected text with the mark and then " "search to replace, only matches in the selected text " "will be replaced.\n\n The following function keys are " "available in Search mode:\n\n"); htx[2] = NULL; } else if (currmenu == MGOTOLINE) { htx[0] = N_("Go To Line Help Text\n\n " "Enter the line number that you wish to go to and hit " "Enter. If there are fewer lines of text than the " "number you entered, you will be brought to the last " "line of the file.\n\n The following function keys are " "available in Go To Line mode:\n\n"); htx[1] = NULL; htx[2] = NULL; } else if (currmenu == MINSERTFILE) { htx[0] = N_("Insert File Help Text\n\n " "Type in the name of a file to be inserted into the " "current file buffer at the current cursor " "location.\n\n If you have compiled nano with multiple " "file buffer support, and enable multiple file buffers " "with the -F or --multibuffer command line flags, the " "Meta-F toggle, or a nanorc file, inserting a file " "will cause it to be loaded into a separate buffer " "(use Meta-< and > to switch between file buffers). "); htx[1] = N_("If you need another blank buffer, do not enter " "any filename, or type in a nonexistent filename at " "the prompt and press Enter.\n\n The following " "function keys are available in Insert File mode:\n\n"); htx[2] = NULL; } else if (currmenu == MWRITEFILE) { htx[0] = N_("Write File Help Text\n\n " "Type the name that you wish to save the current file " "as and press Enter to save the file.\n\n If you have " "selected text with the mark, you will be prompted to " "save only the selected portion to a separate file. To " "reduce the chance of overwriting the current file with " "just a portion of it, the current filename is not the " "default in this mode.\n\n The following function keys " "are available in Write File mode:\n\n"); htx[1] = NULL; htx[2] = NULL; } #ifndef DISABLE_BROWSER else if (currmenu == MBROWSER) { htx[0] = N_("File Browser Help Text\n\n " "The file browser is used to visually browse the " "directory structure to select a file for reading " "or writing. You may use the arrow keys or Page Up/" "Down to browse through the files, and S or Enter to " "choose the selected file or enter the selected " "directory. To move up one level, select the " "directory called \"..\" at the top of the file " "list.\n\n The following function keys are available " "in the file browser:\n\n"); htx[1] = NULL; htx[2] = NULL; } else if (currmenu == MWHEREISFILE) { htx[0] = N_("Browser Search Command Help Text\n\n " "Enter the words or characters you would like to " "search for, and then press Enter. If there is a " "match for the text you entered, the screen will be " "updated to the location of the nearest match for the " "search string.\n\n The previous search string will be " "shown in brackets after the search prompt. Hitting " "Enter without entering any text will perform the " "previous search.\n\n"); htx[1] = N_(" The following function keys are available in " "Browser Search mode:\n\n"); htx[2] = NULL; } else if (currmenu == MGOTODIR) { htx[0] = N_("Browser Go To Directory Help Text\n\n " "Enter the name of the directory you would like to " "browse to.\n\n If tab completion has not been " "disabled, you can use the Tab key to (attempt to) " "automatically complete the directory name.\n\n The " "following function keys are available in Browser Go " "To Directory mode:\n\n"); htx[1] = NULL; htx[2] = NULL; } #endif /* !DISABLE_BROWSER */ #ifndef DISABLE_SPELLER else if (currmenu == MSPELL) { htx[0] = N_("Spell Check Help Text\n\n " "The spell checker checks the spelling of all text in " "the current file. When an unknown word is " "encountered, it is highlighted and a replacement can " "be edited. It will then prompt to replace every " "instance of the given misspelled word in the current " "file, or, if you have selected text with the mark, in " "the selected text.\n\n The following function keys " "are available in Spell Check mode:\n\n"); htx[1] = NULL; htx[2] = NULL; } #endif /* !DISABLE_SPELLER */ #ifndef NANO_TINY else if (currmenu == MEXTCMD) { htx[0] = N_("Execute Command Help Text\n\n " "This mode allows you to insert the output of a " "command run by the shell into the current buffer (or " "a new buffer in multiple file buffer mode). If you " "need another blank buffer, do not enter any " "command.\n\n The following function keys are " "available in Execute Command mode:\n\n"); htx[1] = NULL; htx[2] = NULL; } #endif /* !NANO_TINY */ else { /* Default to the main help list. */ htx[0] = N_("Main nano help text\n\n " "The nano editor is designed to emulate the " "functionality and ease-of-use of the UW Pico text " "editor. There are four main sections of the editor. " "The top line shows the program version, the current " "filename being edited, and whether or not the file " "has been modified. Next is the main editor window " "showing the file being edited. The status line is " "the third line from the bottom and shows important " "messages. "); htx[1] = N_("The bottom two lines show the most commonly used " "shortcuts in the editor.\n\n Shortcuts are written as " "follows: Control-key sequences are notated with a '^' " "and can be entered either by using the Ctrl key or " "pressing the Esc key twice. Meta-key sequences are " "notated with 'M-' and can be entered using either the " "Alt, Cmd, or Esc key, depending on your keyboard setup. "); htx[2] = N_("Also, pressing Esc twice and then typing a " "three-digit decimal number from 000 to 255 will enter " "the character with the corresponding value. The " "following keystrokes are available in the main editor " "window. Alternative keys are shown in " "parentheses:\n\n"); } htx[0] = _(htx[0]); if (htx[1] != NULL) htx[1] = _(htx[1]); if (htx[2] != NULL) htx[2] = _(htx[2]); allocsize += strlen(htx[0]); if (htx[1] != NULL) allocsize += strlen(htx[1]); if (htx[2] != NULL) allocsize += strlen(htx[2]); /* Calculate the length of the shortcut help text. Each entry has * one or two keys, which fill 16 columns, plus translated text, * plus one or two \n's. */ for (f = allfuncs; f != NULL; f = f->next) if (f->menus & currmenu) allocsize += (16 * mb_cur_max()) + strlen(f->help) + 2; #ifndef NANO_TINY /* If we're on the main list, we also count the toggle help text. * Each entry has "M-%c\t\t", five chars which fill 16 columns, * plus a space, plus translated text, plus one or two '\n's. */ if (currmenu == MMAIN) { size_t endis_len = strlen(_("enable/disable")); for (s = sclist; s != NULL; s = s->next) if (s->scfunc == do_toggle_void) allocsize += strlen(_(flagtostr(s->toggle))) + endis_len + 8; } #endif /* Allocate space for the help text. */ help_text = charalloc(allocsize + 1); /* Now add the text we want. */ strcpy(help_text, htx[0]); if (htx[1] != NULL) strcat(help_text, htx[1]); if (htx[2] != NULL) strcat(help_text, htx[2]); ptr = help_text + strlen(help_text); /* Remember this end-of-introduction, start-of-shortcuts. */ end_of_intro = ptr; /* Now add our shortcut info. */ for (f = allfuncs; f != NULL; f = f->next) { int scsfound = 0; if ((f->menus & currmenu) == 0) continue; /* Let's simply show the first two shortcuts from the list. */ for (s = sclist; s != NULL; s = s->next) { if ((s->menus & currmenu) == 0) continue; if (s->scfunc == f->scfunc) { scsfound++; /* Make the first column narrower (6) than the second (10), * but allow it to spill into the second, for "M-Space". */ if (scsfound == 1) { sprintf(ptr, "%s ", s->keystr); /* Unicode arrows take three bytes instead of one. */ if (s->keystr[1] == '\xE2') ptr += 8; else ptr += 6; } else { ptr += sprintf(ptr, "(%s)\t", s->keystr); break; } } } if (scsfound == 0) ptr += sprintf(ptr, "\t\t"); else if (scsfound == 1) ptr += 10; /* The shortcut's help text. */ ptr += sprintf(ptr, "%s\n", _(f->help)); if (f->blank_after) ptr += sprintf(ptr, "\n"); } #ifndef NANO_TINY /* And the toggles... */ if (currmenu == MMAIN) { int maximum = 0, counter = 0; /* First see how many toggles there are. */ for (s = sclist; s != NULL; s = s->next) maximum = (s->toggle && s->ordinal > maximum) ? s->ordinal : maximum; /* Now show them in the original order. */ while (counter < maximum) { counter++; for (s = sclist; s != NULL; s = s->next) if (s->toggle && s->ordinal == counter) { ptr += sprintf(ptr, "%s\t\t%s %s\n", (s->menus == MMAIN ? s->keystr : ""), _(flagtostr(s->toggle)), _("enable/disable")); if (s->toggle == NO_COLOR_SYNTAX || s->toggle == TABS_TO_SPACES) ptr += sprintf(ptr, "\n"); break; } } } if (old_whitespace) SET(WHITESPACE_DISPLAY); #endif /* !NANO_TINY */ /* If all went well, we didn't overwrite the allocated space. */ assert(strlen(help_text) <= allocsize + 1); }
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; }