diff --git a/configure.ac b/configure.ac index 891d6783..5bbdb1b3 100644 --- a/configure.ac +++ b/configure.ac @@ -167,6 +167,30 @@ if test "x$enable_extra" != xno; then AC_DEFINE(ENABLE_EXTRA, 1, [Define this to have an Easter egg.]) fi +AC_ARG_ENABLE(formatter, +AS_HELP_STRING([--disable-formatter], [Disable the formatting tool])) +if test "x$enable_tiny" = xyes; then + if test "x$enable_formatter" = xyes; then + if test "x$enable_color" != xyes; then + AC_MSG_ERROR([ + *** --enable-formatter needs both --enable-color and --enable-nanorc to work]) + fi + else + enable_formatter=no + fi +fi +if test "x$enable_color" = xno; then + if test "x$enable_formatter" = xyes; then + AC_MSG_ERROR([ + *** --enable-formatter cannot work with --disable-color nor --disable-nanorc]) + else + enable_formatter=no + fi +fi +if test "x$enable_formatter" != xno; then + AC_DEFINE(ENABLE_FORMATTER, 1, [Define this to have access to a formatter.]) +fi + AC_ARG_ENABLE(help, AS_HELP_STRING([--disable-help], [Disable the built-in help texts])) if test "x$enable_tiny" = xyes; then @@ -220,7 +244,7 @@ if test "x$enable_libmagic" = xyes; then if test "x$enable_tiny" = xyes; then if test "x$enable_color" != xyes; then AC_MSG_ERROR([ - *** --enable-libmagic needs --enable-color and --enable-nanorc to work]) + *** --enable-libmagic needs both --enable-color and --enable-nanorc to work]) fi fi if test "x$enable_color" = xno; then @@ -229,6 +253,30 @@ if test "x$enable_libmagic" = xyes; then fi fi +AC_ARG_ENABLE(linter, +AS_HELP_STRING([--disable-linter], [Disable the linting tool])) +if test "x$enable_tiny" = xyes; then + if test "x$enable_linter" = xyes; then + if test "x$enable_color" != xyes; then + AC_MSG_ERROR([ + *** --enable-linter needs both --enable-color and --enable-nanorc to work]) + fi + else + enable_linter=no + fi +fi +if test "x$enable_color" = xno; then + if test "x$enable_linter" = xyes; then + AC_MSG_ERROR([ + *** --enable-linter cannot work with --disable-color nor --disable-nanorc]) + else + enable_linter=no + fi +fi +if test "x$enable_linter" != xno; then + AC_DEFINE(ENABLE_LINTER, 1, [Define this to have access to a linter.]) +fi + AC_ARG_ENABLE(linenumbers, AS_HELP_STRING([--disable-linenumbers], [Disable line numbering])) if test "x$enable_tiny" = xyes; then @@ -286,14 +334,14 @@ if test "x$enable_operatingdir" != xno; then fi AC_ARG_ENABLE(speller, -AS_HELP_STRING([--disable-speller], [Disable the spell-checker functions])) +AS_HELP_STRING([--disable-speller], [Disable the spell-checking tool])) if test "x$enable_tiny" = xyes; then if test "x$enable_speller" != xyes; then enable_speller=no fi fi if test "x$enable_speller" != xno; then - AC_DEFINE(ENABLE_SPELLER, 1, [Define this to have the spell-checker functions.]) + AC_DEFINE(ENABLE_SPELLER, 1, [Define this to have access to a spell checker.]) fi AC_ARG_ENABLE(tabcomp, diff --git a/doc/faq.html b/doc/faq.html index 8d74b6b8..73f3863a 100644 --- a/doc/faq.html +++ b/doc/faq.html @@ -150,16 +150,18 @@ --disable-color Disable color and syntax highlighting --disable-comment Disable the comment/uncomment function --disable-extra Disable the easter egg + --disable-formatter Disable the formatting tool --disable-help Disable the built-in help texts --disable-histories Disable the saving of search strings and cursor positions --disable-justify Disable the justify/unjustify functions --disable-libmagic Disable the use of libmagic for determining a file's syntax --disable-linenumbers Disable line numbering + --disable-linter Disable the linting tool --disable-mouse Disable mouse support --disable-multibuffer Disable the opening of multiple file buffers --disable-nanorc Disable the use of .nanorc files --disable-operatingdir Disable the setting of an operating directory - --disable-speller Disable the spell-checker functions + --disable-speller Disable the spell-checking tool --disable-tabcomp Disable the tab-completion functions --disable-wordcomp Disable the word-completion function --disable-wrapping Disable all hard-wrapping of text diff --git a/doc/nano.1 b/doc/nano.1 index 1b46e3f0..d7d0723b 100644 --- a/doc/nano.1 +++ b/doc/nano.1 @@ -74,6 +74,12 @@ using the arrow keys. Holding down the Ctrl or Alt key too will increase the stride. Any cursor movement without Shift being held will cancel such a selection. .sp +Any valid Unicode code point can be inserted into the buffer by typing +\fBM\-V\fR followed by the hexadecimal digits of the code point (concluded +with \fB\fR or \fB\fR when it are fewer than six digits). +A literal control code (except \fB^J\fR) can be inserted by typing +\fBM\-V\fR followed by the pertinent keystroke. +.sp The two lines at the bottom of the screen show some important commands; the built-in help (\fB^G\fR) lists all the available ones. The default key bindings can be changed via a \fInanorc\fR file -- see diff --git a/doc/nano.texi b/doc/nano.texi index 816b95a2..aaed711d 100644 --- a/doc/nano.texi +++ b/doc/nano.texi @@ -222,14 +222,18 @@ typed the key with that value. @item For any possible character, pressing @kbd{M-V} (Alt+V) and then typing a -six-digit hexadecimal number (starting with @kbd{0} or @kbd{1}) will enter the -corresponding Unicode character into the buffer. +series of hexadecimal digits (at most six, or concluded with @kbd{Enter} or +@kbd{Space}) will enter the corresponding Unicode character into the buffer. @end itemize For example, typing @kbd{Esc Esc 2 3 4} will enter the character "ê" --- useful when writing about a French party. Typing @kbd{M-V 0 0 2 2 c 4} will enter the symbol "⋄", a little diamond. +Typing @kbd{M-V} followed by anything other than a hexadecimal digit +will enter this keystroke verbatim into the buffer, allowing the user +to insert literal control codes (except @code{^J}) or escape sequences. + @node Commands @section Commands @@ -1201,6 +1205,14 @@ It also means that lookahead and lookbehind are not possible. A complete explanation can be found in the manual of GNU grep: @code{info grep regular}. +Each regular expression in a @file{nanorc} file should be wrapped in +double quotes (@code{""}). Multiple regular expressions can follow +each other on a line by separating them with blanks. This means that +a regular expression cannot contain a double quote followed by a blank. +When you need this combination inside a regular expression, +then either the double quote or the blank should be put +between square brackets (@code{[]}). + A separate syntax can be defined for each kind of file via the following commands in a nanorc file: @@ -1336,9 +1348,9 @@ Rebinds @code{key} to @code{function} in the context of @code{menu} @item bind key "string" menu Makes @code{key} produce @code{string} in the context of @code{menu} (or in all menus where the key exists when @code{all} is used). -Besides literal text, the @code{string} may contain function names -between braces. These functions will be invoked when the key is typed. -To include a literal opening brace, use @code{@{@{@}}. +Besides literal text and/or control codes, the @code{string} may contain +function names between braces. These functions will be invoked when the +key is typed. To include a literal opening brace, use @code{@{@{@}}. @item unbind key menu Unbinds @code{key} from @code{menu} @@ -1346,6 +1358,16 @@ Unbinds @code{key} from @code{menu} @end table +Note that @code{bind key "@{function@}" menu} is equivalent to +@code{bind key function menu}, except that for the latter form +@command{nano} will check the availabilty of the @code{function} +in the given @code{menu} at startup time (and report an error if +it does not exist there), whereas for the first form @command{nano} +will check at execution time that the @code{function} exists but not +whether it makes any sense in the current menu. The user has to take +care that a function name between braces (or any sequence of them) +is appropriate. Strange behavior can result when it is not. + @sp 1 The format of @code{key} should be one of: @@ -1601,7 +1623,8 @@ Switches to editing/viewing the previous buffer when multiple buffers are open. Switches to editing/viewing the next buffer when multiple buffers are open. @item verbatim -Inserts the next keystroke verbatim into the file. +Inserts the next keystroke verbatim into the file, or begins Unicode input +when a hexadecimal digit is typed (@pxref{Entering Text} for details). @item tab Inserts a tab at the current cursor location. @@ -1941,6 +1964,9 @@ Exclude the single-keystroke comment/uncomment function (@w{@kbd{M-3}}). @item --disable-extra Exclude the Easter egg: a crawl of major contributors. +@item --disable-formatter +Exclude the code for calling a formatting tool. + @item --disable-help Exclude the help texts (@kbd{^G}). This makes the binary much smaller, but also makes it difficult for new users to learn more than very basic @@ -1966,6 +1992,9 @@ in most cases the regexes for filename and header line will be enough). Exclude the ability to show line numbers. This also eliminates the @option{-l} command-line option, which turns line numbering on. +@item --disable-linter +Exclude the code for calling a linting tool. + @item --disable-mouse Exclude all mouse functionality. This also eliminates the @option{-m} command-line option, which enables the mouse functionality. diff --git a/doc/nanorc.5 b/doc/nanorc.5 index c8166c02..6e73db1e 100644 --- a/doc/nanorc.5 +++ b/doc/nanorc.5 @@ -426,6 +426,14 @@ It also means that lookahead and lookbehind are not possible. A complete explanation can be found in the manual page of GNU grep: \fBman grep\fR. .sp +Each regular expression in a \fBnanorc\fR file should be wrapped in +double quotes (\fB""\fR). Multiple regular expressions can follow +each other on a line by separating them with blanks. This means that +a regular expression cannot contain a double quote followed by a blank. +When you need this combination inside a regular expression, +then either the double quote or the blank should be put +between square brackets (\fB[]\fR). +.sp For each kind of file a separate syntax can be defined via the following commands: .TP @@ -551,14 +559,23 @@ Rebinds the given \fIkey\fP to the given \fIfunction\fP in the given \fImenu\fP .BI bind " key " """" string """" " menu" Makes the given \fIkey\fR produce the given \fIstring\fR in the given \fImenu\fR (or in all menus where the key exists when \fBall\fR is used). -Besides literal text, the \fIstring\fR may contain function names -between braces. These functions will be invoked when the key is typed. -To include a literal opening brace, use \fB{{}\fR. +Besides literal text and/or control codes, the \fIstring\fR may contain +function names between braces. These functions will be invoked when +the key is typed. To include a literal opening brace, use \fB{{}\fR. .TP .BI unbind " key menu" Unbinds the given \fIkey\fP from the given \fImenu\fP (or from all menus where the key exists when \fBall\fP is used). .RE +.sp +Note that \fBbind \fIkey\fR \fB"{\fIfunction\fB}"\fR \fImenu\fR is equivalent +to \fBbind \fIkey\fR \fIfunction\fR \fImenu\fR, except that for the latter form +\fBnano\fR will check the availabilty of the \fIfunction\fR in the given \fImenu\fR +at startup time (and report an error if it does not exist there), whereas for the +first form \fBnano\fR will check at execution time that the \fIfunction\fR exists +but not whether it makes any sense in the current menu. The user has to take care +that a function name between braces (or any sequence of them) is appropriate. +Strange behavior can result when it is not. .TP The format of \fIkey\fP should be one of: @@ -811,7 +828,8 @@ Switches to editing/viewing the previous buffer when multiple buffers are open. Switches to editing/viewing the next buffer when multiple buffers are open. .TP .B verbatim -Inserts the next keystroke verbatim into the file. +Inserts the next keystroke verbatim into the file, or begins Unicode input +when a hexadecimal digit is typed. .TP .B tab Inserts a tab at the current cursor location. diff --git a/doc/sample.nanorc.in b/doc/sample.nanorc.in index e74c1077..f3c7ea37 100644 --- a/doc/sample.nanorc.in +++ b/doc/sample.nanorc.in @@ -292,6 +292,9 @@ ## For copying a marked region to the system clipboard: # bind Sh-M-T "{execute}|xsel -ib{enter}{undo}" main +## For snipping trailing blanks when you save a file: +# bind ^S "{execute}| sed 's/\s\+$//' {enter}{savefile}" main + ## If you would like nano to have keybindings that are more "usual", ## such as ^O for Open, ^F for Find, ^H for Help, and ^Q for Quit, ## then uncomment these: diff --git a/src/browser.c b/src/browser.c index e633592a..6f93226a 100644 --- a/src/browser.c +++ b/src/browser.c @@ -255,63 +255,39 @@ void browser_refresh(void) wnoutrefresh(midwin); } -/* Look for the given needle in the list of files. If forwards is TRUE, - * search forward in the list; otherwise, search backward. */ +/* Look for the given needle in the list of files, forwards or backwards. */ void findfile(const char *needle, bool forwards) { - size_t looking_at = selected; - /* The location in the file list of the filename we're looking at. */ - const char *thename; - /* The plain filename, without the path. */ - unsigned stash[sizeof(flags) / sizeof(flags[0])]; - /* A storage place for the current flag settings. */ + size_t began_at = selected; - /* Save the settings of all flags. */ - memcpy(stash, flags, sizeof(flags)); - - /* Search forward, case insensitive, and without regexes. */ - UNSET(BACKWARDS_SEARCH); - UNSET(CASE_SENSITIVE); - UNSET(USE_REGEXP); - - /* Step through each filename in the list until a match is found or + /* Iterate through the list of filenames, until a match is found or * we've come back to the point where we started. */ while (TRUE) { if (forwards) { - if (looking_at++ == list_length - 1) { - looking_at = 0; + if (selected++ == list_length - 1) { + selected = 0; statusbar(_("Search Wrapped")); } } else { - if (looking_at-- == 0) { - looking_at = list_length - 1; + if (selected-- == 0) { + selected = list_length - 1; statusbar(_("Search Wrapped")); } } - /* Get the bare filename, without the path. */ - thename = tail(filelist[looking_at]); - - /* If the needle matches, we're done. And if we're back at the file - * where we started, it is the only occurrence. */ - if (strstrwrapper(thename, needle, thename)) { - if (looking_at == selected) + /* When the needle occurs in the basename of the file, we have a match. */ + if (mbstrcasestr(tail(filelist[selected]), needle)) { + if (selected == began_at) statusbar(_("This is the only occurrence")); - break; + return; } - /* If we're back at the beginning and didn't find any match... */ - if (looking_at == selected) { + /* When we're back at the starting point without any match... */ + if (selected == began_at) { not_found_msg(needle); - break; + return; } } - - /* Restore the settings of all flags. */ - memcpy(flags, stash, sizeof(flags)); - - /* Select the one we've found. */ - selected = looking_at; } /* Prepare the prompt and ask the user what to search for; then search for it. diff --git a/src/definitions.h b/src/definitions.h index 23ffd223..2bc87667 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -214,16 +214,12 @@ #define SHIFT_DELETE 0x45D #define SHIFT_TAB 0x45F -/* A special keycode for when a string bind has been partially implanted. */ +/* Special keycodes for when a string bind has been partially implanted + * or has an unpaired opening brace, or when a function in a string bind + * needs execution or a specified function name is invalid. */ #define MORE_PLANTS 0x4EA - -/* A special keycode for when a string bind has an unpaired opening brace. */ #define MISSING_BRACE 0x4EB - -/* A special keycode for when a function in a string bind needs execution. */ #define PLANTED_COMMAND 0x4EC - -/* A special keycode for when a function name in a string bind is invalid. */ #define NO_SUCH_FUNCTION 0x4EF /* A special keycode for when is pressed while the mark is on. */ @@ -658,14 +654,13 @@ typedef struct keystruct { typedef struct funcstruct { void (*func)(void); /* The actual function to call. */ - const char *desc; - /* The function's short description, for example "Where Is". */ + const char *tag; + /* The function's help-line label, for example "Where Is". */ #ifdef ENABLE_HELP - const char *help; - /* The help-screen text for this function. */ + const char *phrase; + /* The function's description for in the help viewer. */ bool blank_after; - /* Whether there should be a blank line after the help text - * for this function. */ + /* Whether to distance this function from the next in the help viewer. */ #endif int menus; /* In what menus this function applies. */ diff --git a/src/files.c b/src/files.c index b885a0cd..75256a96 100644 --- a/src/files.c +++ b/src/files.c @@ -57,7 +57,7 @@ void make_new_buffer(void) openfile->next = newnode; /* There is more than one buffer: show "Close" in help lines. */ - exitfunc->desc = close_tag; + exitfunc->tag = close_tag; more_than_one = !inhelp || more_than_one; } #endif @@ -218,7 +218,7 @@ bool write_lockfile(const char *lockfilename, const char *filename, bool modifie strncpy(&lockdata[108], filename, 768); lockdata[1007] = (modified) ? 0x55 : 0x00; - wroteamt = fwrite(lockdata, sizeof(char), LOCKSIZE, filestream); + wroteamt = fwrite(lockdata, 1, LOCKSIZE, filestream); free(lockdata); @@ -627,7 +627,7 @@ void close_buffer(void) /* When just one buffer remains open, show "Exit" in the help lines. */ if (openfile && openfile == openfile->next) - exitfunc->desc = exit_tag; + exitfunc->tag = exit_tag; } #endif /* ENABLE_MULTIBUFFER */ @@ -989,7 +989,14 @@ void send_data(const linestruct *line, int fd) /* Send each line, except a final empty line. */ while (line != NULL && (line->next != NULL || line->data[0] != '\0')) { - fprintf(tube, "%s%s", line->data, line->next == NULL ? "" : "\n"); + size_t length = recode_LF_to_NUL(line->data); + + if (fwrite(line->data, 1, length, tube) < length) + exit(5); + + if (line->next && putc('\n', tube) == EOF) + exit(6); + line = line->next; } @@ -1530,12 +1537,12 @@ int copy_file(FILE *inn, FILE *out, bool close_out) int (*flush_out_fnc)(FILE *) = (close_out) ? fclose : fflush; do { - charsread = fread(buf, sizeof(char), BUFSIZ, inn); + charsread = fread(buf, 1, BUFSIZ, inn); if (charsread == 0 && ferror(inn)) { retval = -1; break; } - if (fwrite(buf, sizeof(char), charsread, out) < charsread) { + if (fwrite(buf, 1, charsread, out) < charsread) { retval = 2; break; } @@ -1857,13 +1864,12 @@ bool write_file(const char *name, FILE *thefile, bool normal, statusbar(_("Writing...")); while (TRUE) { - size_t data_len = strlen(line->data); - size_t wrote; + size_t data_len, wrote; /* Decode LFs as the NULs that they are, before writing to disk. */ - recode_LF_to_NUL(line->data); + data_len = recode_LF_to_NUL(line->data); - wrote = fwrite(line->data, sizeof(char), data_len, thefile); + wrote = fwrite(line->data, 1, data_len, thefile); /* Re-encode any embedded NULs as LFs. */ recode_NUL_to_LF(line->data, data_len); diff --git a/src/global.c b/src/global.c index fabca4c7..c6c9a48f 100644 --- a/src/global.c +++ b/src/global.c @@ -317,8 +317,8 @@ void discard_buffer(void) {;} void do_cancel(void) {;} /* Add a function to the linked list of functions. */ -void add_to_funcs(void (*function)(void), int menus, const char *desc, - const char *help, bool blank_after) +void add_to_funcs(void (*function)(void), int menus, const char *tag, + const char *phrase, bool blank_after) { funcstruct *f = nmalloc(sizeof(funcstruct)); @@ -331,9 +331,9 @@ void add_to_funcs(void (*function)(void), int menus, const char *desc, f->next = NULL; f->func = function; f->menus = menus; - f->desc = desc; + f->tag = tag; #ifdef ENABLE_HELP - f->help = help; + f->phrase = phrase; f->blank_after = blank_after; #endif } @@ -692,10 +692,12 @@ void shortcut_init(void) const char *browserrefresh_gist = N_("Refresh the file list"); const char *gotodir_gist = N_("Go to directory"); #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER const char *lint_gist = N_("Invoke the linter, if available"); const char *prevlint_gist = N_("Go to previous linter msg"); const char *nextlint_gist = N_("Go to next linter msg"); +#endif +#ifdef ENABLE_FORMATTER const char *formatter_gist = N_("Invoke a program to format/arrange/manipulate the buffer"); #endif @@ -1023,9 +1025,11 @@ void shortcut_init(void) add_to_funcs(do_spell, MMAIN, N_("Spell Check"), WHENHELP(spell_gist), TOGETHER); #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER add_to_funcs(do_linter, MMAIN, N_("Linter"), WHENHELP(lint_gist), TOGETHER); +#endif +#ifdef ENABLE_FORMATTER add_to_funcs(do_formatter, MMAIN, N_("Formatter"), WHENHELP(formatter_gist), BLANKAFTER); #endif @@ -1065,7 +1069,7 @@ void shortcut_init(void) add_to_funcs(do_spell, MEXECUTE, N_("Spell Check"), WHENHELP(spell_gist), TOGETHER); #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER add_to_funcs(do_linter, MEXECUTE, N_("Linter"), WHENHELP(lint_gist), BLANKAFTER); #endif @@ -1073,7 +1077,7 @@ void shortcut_init(void) add_to_funcs(do_full_justify, MEXECUTE, N_("Full Justify"), WHENHELP(fulljustify_gist), TOGETHER); #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_FORMATTER add_to_funcs(do_formatter, MEXECUTE, N_("Formatter"), WHENHELP(formatter_gist), BLANKAFTER); #endif @@ -1153,7 +1157,7 @@ void shortcut_init(void) add_to_funcs(discard_buffer, MWRITEFILE, N_("Discard buffer"), WHENHELP(discardbuffer_gist), BLANKAFTER); -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER add_to_funcs(do_page_up, MLINTER, /* TRANSLATORS: The next two strings may be up to 37 characters each. */ N_("Previous Linter message"), WHENHELP(prevlint_gist), TOGETHER); @@ -1207,9 +1211,11 @@ void shortcut_init(void) #ifdef ENABLE_JUSTIFY add_to_sclist(MMAIN, "^J", '\n', do_justify, 0); #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER add_to_sclist(MMAIN, "M-B", 0, do_linter, 0); add_to_sclist(MEXECUTE, "^Y", 0, do_linter, 0); +#endif +#ifdef ENABLE_FORMATTER add_to_sclist(MMAIN, "M-F", 0, do_formatter, 0); add_to_sclist(MEXECUTE, "^O", 0, do_formatter, 0); #endif diff --git a/src/help.c b/src/help.c index 91c75e7e..ebb90671 100644 --- a/src/help.c +++ b/src/help.c @@ -224,7 +224,7 @@ void help_init(void) * plus translated text, plus one or two \n's. */ for (f = allfuncs; f != NULL; f = f->next) if (f->menus & currmenu) - allocsize += strlen(_(f->help)) + 21; + allocsize += strlen(_(f->phrase)) + 21; #ifndef NANO_TINY /* If we're on the main list, we also count the toggle help text. @@ -282,7 +282,7 @@ void help_init(void) ptr += 10; /* The shortcut's description. */ - ptr += sprintf(ptr, "%s\n", _(f->help)); + ptr += sprintf(ptr, "%s\n", _(f->phrase)); if (f->blank_after) ptr += sprintf(ptr, "\n"); diff --git a/src/history.c b/src/history.c index 5cf4cb93..d5b96787 100644 --- a/src/history.c +++ b/src/history.c @@ -304,12 +304,10 @@ bool write_list(const linestruct *head, FILE *histfile) const linestruct *item; for (item = head; item != NULL; item = item->next) { - size_t length = strlen(item->data); - /* Decode 0x0A bytes as embedded NULs. */ - recode_LF_to_NUL(item->data); + size_t length = recode_LF_to_NUL(item->data); - if (fwrite(item->data, sizeof(char), length, histfile) < length) + if (fwrite(item->data, 1, length, histfile) < length) return FALSE; if (putc('\n', histfile) == EOF) return FALSE; @@ -451,14 +449,13 @@ void save_poshistory(void) path_and_place = nmalloc(strlen(item->filename) + 44); sprintf(path_and_place, "%s %zd %zd\n", item->filename, item->linenumber, item->columnnumber); - length = strlen(path_and_place); /* Encode newlines in filenames as NULs. */ - recode_LF_to_NUL(path_and_place); + length = recode_LF_to_NUL(path_and_place); /* Restore the terminating newline. */ path_and_place[length - 1] = '\n'; - if (fwrite(path_and_place, sizeof(char), length, histfile) < length) + if (fwrite(path_and_place, 1, length, histfile) < length) jot_error(N_("Error writing %s: %s"), poshistname, strerror(errno)); free(path_and_place); diff --git a/src/nano.c b/src/nano.c index 91fc1ce1..f784768f 100644 --- a/src/nano.c +++ b/src/nano.c @@ -430,10 +430,9 @@ void window_init(void) int toprows = ((ISSET(EMPTY_LINE) && LINES > 6) ? 2 : 1); int bottomrows = ((ISSET(NO_HELP) || LINES < 6) ? 1 : 3); -#ifndef NANO_TINY if (ISSET(MINIBAR) || ISSET(ZERO)) toprows = 0; -#endif + editwinrows = LINES - toprows - bottomrows + (ISSET(ZERO) ? 1 : 0); /* Set up the normal three subwindows. */ @@ -487,7 +486,7 @@ void mouse_init(void) #endif /* ENABLE_MOUSE */ /* Print the usage line for the given option to the screen. */ -void print_opt(const char *shortflag, const char *longflag, const char *desc) +void print_opt(const char *shortflag, const char *longflag, const char *description) { int firstwidth = breadth(shortflag); int secondwidth = breadth(longflag); @@ -500,7 +499,7 @@ void print_opt(const char *shortflag, const char *longflag, const char *desc) if (secondwidth < 24) printf("%*s", 24 - secondwidth, " "); - printf("%s\n", _(desc)); + printf("%s\n", _(description)); } /* Explain how to properly use nano and its command-line options. */ @@ -695,6 +694,9 @@ void version(void) #ifdef ENABLE_EXTRA printf(" --enable-extra"); #endif +#ifdef ENABLE_FORMATTER + printf(" --enable-formatter"); +#endif #ifdef ENABLE_HELP printf(" --enable-help"); #endif @@ -710,6 +712,9 @@ void version(void) #ifdef ENABLE_LINENUMBERS printf(" --enable-linenumbers"); #endif +#ifdef ENABLE_LINTER + printf(" --enable-linter"); +#endif #ifdef ENABLE_MOUSE printf(" --enable-mouse"); #endif @@ -744,6 +749,9 @@ void version(void) #ifndef ENABLE_EXTRA printf(" --disable-extra"); #endif +#ifndef ENABLE_FORMATTER + printf(" --disable-formatter"); +#endif #ifndef ENABLE_HELP printf(" --disable-help"); #endif @@ -759,6 +767,9 @@ void version(void) #ifndef ENABLE_LINENUMBERS printf(" --disable-linenumbers"); #endif +#ifndef ENABLE_LINTER + printf(" --disable-linter"); +#endif #ifndef ENABLE_MOUSE printf(" --disable-mouse"); #endif @@ -1277,10 +1288,10 @@ void unbound_key(int code) * (from the keyboard) that nano does not recognize. */ statusline(AHEM, _("Unknown sequence")); #ifdef ENABLE_NANORC + else if (code == NO_SUCH_FUNCTION) + statusline(AHEM, _("Unknown function: %s"), commandname); else if (code == MISSING_BRACE) statusline(AHEM, _("Missing }")); - else if (code == NO_SUCH_FUNCTION) - statusline(AHEM, _("No such function: %s"), commandname); #endif #ifndef NANO_TINY else if (code > KEY_F0 && code < KEY_F0 + 25) @@ -1297,17 +1308,17 @@ void unbound_key(int code) #endif #ifdef ENABLE_NANORC if (shifted_metas && 'A' <= code && code <= 'Z') - statusline(AHEM, _("Unbound key: Sh-M-%c"), code); + statusline(AHEM, _("Unbound key: %s%c"), "Sh-M-", code); else #endif - statusline(AHEM, _("Unbound key: M-%c"), toupper(code)); + statusline(AHEM, _("Unbound key: %s%c"), "M-", toupper(code)); } else if (code == ESC_CODE) statusline(AHEM, _("Unbindable key: ^[")); else if (code < 0x20) - statusline(AHEM, _("Unbound key: ^%c"), code + 0x40); + statusline(AHEM, _("Unbound key: %s%c"), "^", code + 0x40); #if defined(ENABLE_BROWSER) || defined (ENABLE_HELP) else - statusline(AHEM, _("Unbound key: %c"), code); + statusline(AHEM, _("Unbound key: %s%c"), "", code); #endif set_blankdelay_to_one(); } @@ -1386,9 +1397,8 @@ bool wanted_to_move(void (*func)(void)) /* Return TRUE when the given function makes a change -- no good for view mode. */ bool changes_something(const void *f) { - return (f == do_savefile || f == do_writeout || f == do_enter || - f == do_tab || f == do_delete || f == do_backspace || - f == cut_text || f == paste_text || f == do_replace || + return (f == do_savefile || f == do_writeout || f == do_enter || f == do_tab || + f == do_delete || f == do_backspace || f == cut_text || f == paste_text || #ifndef NANO_TINY f == chop_previous_word || f == chop_next_word || f == zap_text || f == cut_till_eof || f == do_execute || @@ -1400,13 +1410,13 @@ bool changes_something(const void *f) #ifdef ENABLE_SPELLER f == do_spell || #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_FORMATTER f == do_formatter || #endif #ifdef ENABLE_WORDCOMPLETION f == complete_a_word || #endif - f == do_verbatim_input); + f == do_replace || f == do_verbatim_input); } #ifndef NANO_TINY @@ -1545,6 +1555,8 @@ void process_a_keystroke(void) /* The keystroke we read in: a character or a shortcut. */ static char *puddle = NULL; /* The input buffer for actual characters. */ + static size_t capacity = 12; + /* The size of the input buffer; gets doubled whenever needed. */ static size_t depth = 0; /* The length of the input buffer. */ #ifndef NANO_TINY @@ -1591,21 +1603,23 @@ void process_a_keystroke(void) refresh_needed = TRUE; } #endif - /* Store the byte, and leave room for a terminating zero. */ - puddle = nrealloc(puddle, depth + 2); + /* When the input buffer (plus room for terminating NUL) is full, + * extend it; otherwise, if it does not exist yet, create it. */ + if (depth + 1 == capacity) { + capacity = 2 * capacity; + puddle = nrealloc(puddle, capacity); + } else if (!puddle) + puddle = nmalloc(capacity); + puddle[depth++] = (char)input; } } - /* If we have a command, or if there aren't any other key codes waiting, - * it's time to insert the gathered bytes into the edit buffer. */ - if ((function || waiting_keycodes() == 0) && puddle != NULL) { + /* If there are gathered bytes and we have a command or no other key codes + * are waiting, it's time to insert these bytes into the edit buffer. */ + if (depth > 0 && (function || waiting_keycodes() == 0)) { puddle[depth] = '\0'; - inject(puddle, depth); - - free(puddle); - puddle = NULL; depth = 0; } @@ -2506,6 +2520,10 @@ int main(int argc, char **argv) /* After handling the files on the command line, allow inserting files. */ UNSET(NOREAD_MODE); + /* Nano is a hands-on editor -- it needs a keyboard. */ + if (!isatty(STDIN_FILENO)) + die(_("Standard input is not a terminal\n")); + /* If no filenames were given, or all of them were invalid things like * directories, then open a blank buffer and allow editing. Otherwise, * switch from the last opened file to the next, that is: the first. */ diff --git a/src/prompt.c b/src/prompt.c index 4a828638..84d4ef64 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -182,16 +182,12 @@ void copy_the_answer(void) void paste_into_answer(void) { size_t pastelen = strlen(cutbuffer->data); - char *fusion = nmalloc(strlen(answer) + pastelen + 1); - /* Concatenate: the current answer before the cursor, the first line - * of the cutbuffer, plus the rest of the current answer. */ - strncpy(fusion, answer, typing_x); - strncpy(fusion + typing_x, cutbuffer->data, pastelen); - strcpy(fusion + typing_x + pastelen, answer + typing_x); + answer = nrealloc(answer, strlen(answer) + pastelen + 1); + memmove(answer + typing_x + pastelen, answer + typing_x, + strlen(answer) - typing_x + 1); + strncpy(answer + typing_x, cutbuffer->data, pastelen); - free(answer); - answer = fusion; typing_x += pastelen; } #endif @@ -256,6 +252,8 @@ void absorb_character(int input, functionptrtype function) { static char *puddle = NULL; /* The input buffer. */ + static size_t capacity = 8; + /* The size of the input buffer; gets doubled whenever needed. */ static size_t depth = 0; /* The length of the input buffer. */ @@ -267,21 +265,23 @@ void absorb_character(int input, functionptrtype function) beep(); else if (!ISSET(RESTRICTED) || currmenu != MWRITEFILE || openfile->filename[0] == '\0') { - puddle = nrealloc(puddle, depth + 2); + /* When the input buffer (plus room for terminating NUL) is full, + * extend it; otherwise, if it does not exist yet, create it. */ + if (depth + 1 == capacity) { + capacity = 2 * capacity; + puddle = nrealloc(puddle, capacity); + } else if (!puddle) + puddle = nmalloc(capacity); + puddle[depth++] = (char)input; } } - /* If we got a shortcut, or if there aren't any other keystrokes waiting, - * it's time to insert all characters in the input buffer (if not empty) - * into the answer, and then clear the input buffer. */ - if ((function || waiting_keycodes() == 0) && puddle != NULL) { + /* If there are gathered bytes and we have a command or no other key codes + * are waiting, it's time to insert these bytes into the answer. */ + if (depth > 0 && (function || waiting_keycodes() == 0)) { puddle[depth] = '\0'; - inject_into_answer(puddle, depth); - - free(puddle); - puddle = NULL; depth = 0; } } @@ -442,7 +442,7 @@ functionptrtype acquire_an_answer(int *actual, bool *listed, #ifndef NANO_TINY /* If the window size changed, go reformat the prompt string. */ if (input == KEY_WINCH) { - refresh_func(); + refresh_func(); /* Only needed when in file browser. */ *actual = KEY_WINCH; #ifdef ENABLE_HISTORIES free(stored_string); @@ -595,20 +595,18 @@ int do_prompt(int menu, const char *provided, linestruct **history_list, function = acquire_an_answer(&retval, &listed, history_list, refresh_func); free(prompt); - prompt = saved_prompt; #ifndef NANO_TINY if (retval == KEY_WINCH) goto redo_theprompt; #endif - /* If we're done with this prompt, restore the x position to what - * it was at a possible previous prompt. */ + /* Restore a possible previous prompt and maybe the typing position. */ + prompt = saved_prompt; if (function == do_cancel || function == do_enter) typing_x = was_typing_x; - /* If we left the prompt via Cancel or Enter, set the return value - * properly. */ + /* Set the proper return value for Cancel and Enter. */ if (function == do_cancel) retval = -1; else if (function == do_enter) diff --git a/src/prototypes.h b/src/prototypes.h index 35ce7f6c..48a5521c 100644 --- a/src/prototypes.h +++ b/src/prototypes.h @@ -470,7 +470,9 @@ ssize_t do_replace_loop(const char *needle, bool whole_word_only, const linestruct *real_current, size_t *real_current_x); void do_replace(void); void ask_for_and_do_replacements(void); +#if !defined(NANO_TINY) || defined(ENABLE_SPELLER) || defined (ENABLE_LINTER) || defined (ENABLE_FORMATTER) void goto_line_posx(ssize_t line, size_t pos_x); +#endif void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer, bool interactive); void do_gotolinecolumn(void); @@ -524,8 +526,10 @@ void do_full_justify(void); #ifdef ENABLE_SPELLER void do_spell(void); #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER void do_linter(void); +#endif +#ifdef ENABLE_FORMATTER void do_formatter(void); #endif #ifndef NANO_TINY @@ -544,7 +548,7 @@ int digits(ssize_t n); bool parse_num(const char *str, ssize_t *result); bool parse_line_column(const char *str, ssize_t *line, ssize_t *column); void recode_NUL_to_LF(char *string, size_t length); -void recode_LF_to_NUL(char *string); +size_t recode_LF_to_NUL(char *string); #if !defined(ENABLE_TINY) || defined(ENABLE_TABCOMP) || defined(ENABLE_BROWSER) void free_chararray(char **array, size_t len); #endif @@ -574,15 +578,18 @@ void get_region(linestruct **top, size_t *top_x, linestruct **bot, size_t *bot_x void get_range(linestruct **top, linestruct **bot); #endif size_t number_of_characters_in(const linestruct *begin, const linestruct *end); -#ifndef NANO_TINY +#if !defined(NANO_TINY) || defined(ENABLE_SPELLER) || defined (ENABLE_LINTER) || defined (ENABLE_FORMATTER) linestruct *line_from_number(ssize_t number); #endif /* Most functions in winio.c. */ linestruct *get_next_visible_line(linestruct *line); linestruct *get_prev_visible_line(linestruct *line); +#ifndef NANO_TINY void record_macro(void); void run_macro(void); +#endif +void reserve_space_for(size_t newsize); size_t waiting_keycodes(void); #ifdef ENABLE_NANORC void implant(const char *string); diff --git a/src/rcfile.c b/src/rcfile.c index 85f741ec..966da5d8 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -153,18 +153,6 @@ static bool seen_color_command = FALSE; static colortype *lastcolor = NULL; /* The end of the color list for the current syntax. */ #endif - -#define NUMBER_OF_MENUS 16 -char *menunames[NUMBER_OF_MENUS] = { "main", "search", "replace", "replacewith", - "yesno", "gotoline", "writeout", "insert", - "execute", "help", "spell", "linter", - "browser", "whereisfile", "gotodir", - "all" }; -int menusymbols[NUMBER_OF_MENUS] = { MMAIN, MWHEREIS, MREPLACE, MREPLACEWITH, - MYESNO, MGOTOLINE, MWRITEFILE, MINSERTFILE, - MEXECUTE, MHELP, MSPELL, MLINTER, - MBROWSER, MWHEREISFILE, MGOTODIR, - MMOST|MBROWSER|MHELP|MYESNO }; #endif /* ENABLE_NANORC */ #if defined(ENABLE_NANORC) || defined(ENABLE_HISTORIES) @@ -276,9 +264,11 @@ keystruct *strtosc(const char *input) !strcmp(input, "speller")) s->func = do_spell; #endif -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER else if (!strcmp(input, "linter")) s->func = do_linter; +#endif +#ifdef ENABLE_FORMATTER else if (!strcmp(input, "formatter")) s->func = do_formatter; #endif @@ -493,6 +483,18 @@ keystruct *strtosc(const char *input) return s; } +#define NUMBER_OF_MENUS 16 +char *menunames[NUMBER_OF_MENUS] = { "main", "search", "replace", "replacewith", + "yesno", "gotoline", "writeout", "insert", + "execute", "help", "spell", "linter", + "browser", "whereisfile", "gotodir", + "all" }; +int menusymbols[NUMBER_OF_MENUS] = { MMAIN, MWHEREIS, MREPLACE, MREPLACEWITH, + MYESNO, MGOTOLINE, MWRITEFILE, MINSERTFILE, + MEXECUTE, MHELP, MSPELL, MLINTER, + MBROWSER, MWHEREISFILE, MGOTODIR, + MMOST|MBROWSER|MHELP|MYESNO }; + /* Return the symbol that corresponds to the given menu name. */ int name_to_menu(const char *name) { @@ -502,7 +504,7 @@ int name_to_menu(const char *name) if (strcmp(name, menunames[index]) == 0) return menusymbols[index]; - return -1; + return 0; } /* Return the name that corresponds to the given menu symbol. */ @@ -785,6 +787,12 @@ void parse_binding(char *ptr, bool dobind) goto free_things; } + menu = name_to_menu(menuptr); + if (menu == 0) { + jot_error(N_("Unknown menu: %s"), menuptr); + goto free_things; + } + if (dobind) { /* If the thing to bind starts with a double quote, it is a string, * otherwise it is the name of a function. */ @@ -799,17 +807,11 @@ void parse_binding(char *ptr, bool dobind) newsc = strtosc(funcptr); if (newsc == NULL) { - jot_error(N_("Cannot map name \"%s\" to a function"), funcptr); + jot_error(N_("Unknown function: %s"), funcptr); goto free_things; } } - menu = name_to_menu(menuptr); - if (menu < 1) { - jot_error(N_("Cannot map name \"%s\" to a menu"), menuptr); - goto free_things; - } - /* Wipe the given shortcut from the given menu. */ for (keystruct *s = sclist; s != NULL; s = s->next) if ((s->menus & menu) && s->keycode == keycode) @@ -1348,7 +1350,7 @@ static void check_vitals_mapped(void) if (f->func == vitals[v] && f->menus & inmenus[v]) { if (first_sc_for(inmenus[v], f->func) == NULL) { jot_error(N_("No key is bound to function '%s' in menu '%s'. " - " Exiting.\n"), f->desc, menu_to_name(inmenus[v])); + " Exiting.\n"), f->tag, menu_to_name(inmenus[v])); die(_("If needed, use nano with the -I option " "to adjust your nanorc settings.\n")); } else @@ -1530,7 +1532,7 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only) } if (rcopts[i].name == NULL) { - jot_error(N_("Unknown option \"%s\""), option); + jot_error(N_("Unknown option: %s"), option); continue; } diff --git a/src/search.c b/src/search.c index 576b42e9..4b603225 100644 --- a/src/search.c +++ b/src/search.c @@ -222,8 +222,9 @@ int findnextstr(const char *needle, bool whole_word_only, int modus, continue; } #endif - /* The match is valid. */ - break; + /* When not on the magic line, the match is valid. */ + if (line->next || line->data[0]) + break; } #ifndef NANO_TINY @@ -738,6 +739,7 @@ void ask_for_and_do_replacements(void) "Replaced %zd occurrences", numreplaced), numreplaced); } +#if !defined(NANO_TINY) || defined(ENABLE_SPELLER) || defined (ENABLE_LINTER) || defined (ENABLE_FORMATTER) /* Go to the specified line and x position. */ void goto_line_posx(ssize_t linenumber, size_t pos_x) { @@ -747,13 +749,18 @@ void goto_line_posx(ssize_t linenumber, size_t pos_x) recook |= perturbed; #endif - openfile->current = line_from_number(linenumber); + if (linenumber < openfile->filebot->lineno) + openfile->current = line_from_number(linenumber); + else + openfile->current = openfile->filebot; + openfile->current_x = pos_x; openfile->placewewant = xplustabs(); UNFOLD_SEGMENT(openfile->current); refresh_needed = TRUE; } +#endif /* Go to the specified line and column, or ask for them if interactive * is TRUE. In the latter case also update the screen afterwards. @@ -839,9 +846,9 @@ void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer, openfile->placewewant = breadth(openfile->current->data); #endif - /* When the position was manually given, center the target line. */ + /* When a line number was manually given, center the target line. */ if (interactive) { - adjust_viewport(CENTERING); + adjust_viewport((*answer == ',') ? STATIONARY : CENTERING); refresh_needed = TRUE; } else { int rows_from_tail; diff --git a/src/text.c b/src/text.c index 48df9934..268ae35a 100644 --- a/src/text.c +++ b/src/text.c @@ -665,7 +665,7 @@ void do_undo(void) break; } - if (undidmsg && !pletion_line) + if (undidmsg && !ISSET(ZERO) && !pletion_line) statusline(HUSH, _("Undid %s"), undidmsg); openfile->current_undo = openfile->current_undo->next; @@ -835,7 +835,7 @@ void do_redo(void) break; } - if (redidmsg) + if (redidmsg && !ISSET(ZERO)) statusline(HUSH, _("Redid %s"), redidmsg); openfile->current_undo = u; @@ -2039,7 +2039,7 @@ void do_full_justify(void) } #endif /* ENABLE_JUSTIFY */ -#if defined(ENABLE_SPELLER) || defined (ENABLE_COLOR) +#if defined(ENABLE_SPELLER) || defined (ENABLE_LINTER) || defined (ENABLE_FORMATTER) /* Set up an argument list for executing the given command. */ void construct_argument_list(char ***arguments, char *command, char *filename) { @@ -2056,7 +2056,9 @@ void construct_argument_list(char ***arguments, char *command, char *filename) (*arguments)[count - 2] = filename; (*arguments)[count - 1] = NULL; } +#endif +#if defined(ENABLE_SPELLER) || defined (ENABLE_FORMATTER) /* Open the specified file, and if that succeeds, remove the text of the marked * region or of the entire buffer and read the file contents into its place. */ bool replace_buffer(const char *filename, undo_type action, const char *operation) @@ -2234,7 +2236,7 @@ void treat(char *tempfile_name, char *theprogram, bool spelling) statusline(REMARK, _("Buffer has been processed")); #endif } -#endif /* ENABLE_SPELLER || ENABLE_COLOR */ +#endif /* ENABLE_SPELLER || ENABLE_FORMATTER */ #ifdef ENABLE_SPELLER /* Let the user edit the misspelled word. Return FALSE if the user cancels. */ @@ -2579,7 +2581,7 @@ void do_spell(void) } #endif /* ENABLE_SPELLER */ -#ifdef ENABLE_COLOR +#ifdef ENABLE_LINTER /* Run a linting program on the current buffer. */ void do_linter(void) { @@ -2926,7 +2928,9 @@ void do_linter(void) titlebar(NULL); #endif } +#endif /* ENABLE_LINTER */ +#ifdef ENABLE_FORMATTER /* Run a manipulation program on the contents of the buffer. */ void do_formatter(void) { @@ -2961,7 +2965,7 @@ void do_formatter(void) unlink(temp_name); free(temp_name); } -#endif /* ENABLE_COLOR */ +#endif /* ENABLE_FORMATTER */ #ifndef NANO_TINY /* Our own version of "wc". Note that the character count is in @@ -3051,7 +3055,13 @@ void do_verbatim_input(void) if (count < 999) inject(bytes, count); - wipe_statusbar(); +#ifndef NANO_TINY + /* Ensure that the feedback will be overwritten, or clear it. */ + if (ISSET(ZERO) && currmenu == MMAIN) + wredrawln(midwin, editwinrows - 1, 1); + else +#endif + wipe_statusbar(); } else /* TRANSLATORS: An invalid verbatim Unicode code was typed. */ statusline(AHEM, _("Invalid code")); diff --git a/src/utils.c b/src/utils.c index 22631b5b..d6470ccc 100644 --- a/src/utils.c +++ b/src/utils.c @@ -168,14 +168,19 @@ void recode_NUL_to_LF(char *string, size_t length) } } -/* In the given string, recode each embedded newline as a NUL. */ -void recode_LF_to_NUL(char *string) +/* In the given string, recode each embedded newline as a NUL, + * and return the number of bytes in the string. */ +size_t recode_LF_to_NUL(char *string) { + char *beginning = string; + while (*string != '\0') { if (*string == '\n') *string = '\0'; string++; } + + return (string - beginning); } #if !defined(ENABLE_TINY) || defined(ENABLE_TABCOMP) || defined(ENABLE_BROWSER) @@ -484,7 +489,9 @@ void get_range(linestruct **top, linestruct **bot) also_the_last = TRUE; } } +#endif /* !NANO_TINY */ +#if !defined(NANO_TINY) || defined(ENABLE_SPELLER) || defined (ENABLE_LINTER) || defined (ENABLE_FORMATTER) /* Return a pointer to the line that has the given line number. */ linestruct *line_from_number(ssize_t number) { @@ -499,7 +506,7 @@ linestruct *line_from_number(ssize_t number) return line; } -#endif /* !NANO_TINY */ +#endif /* Count the number of characters from begin to end, and return it. */ size_t number_of_characters_in(const linestruct *begin, const linestruct *end) diff --git a/src/winio.c b/src/winio.c index d2e5a3f3..309233a3 100644 --- a/src/winio.c +++ b/src/winio.c @@ -46,8 +46,14 @@ static int *key_buffer = NULL; /* A buffer for the keystrokes that haven't been handled yet. */ static int *nextcodes = NULL; /* A pointer pointing at the next keycode in the keystroke buffer. */ +static size_t capacity = 32; + /* The size of the keystroke buffer; gets doubled whenever needed. */ static size_t waiting_codes = 0; /* The number of key codes waiting in the keystroke buffer. */ +#ifdef ENABLE_NANORC +static const char *plants_pointer = NULL; + /* Points into the expansion string for the current implantation. */ +#endif static int digit_count = 0; /* How many digits of a three-digit character code we've eaten. */ static bool reveal_cursor = FALSE; @@ -56,18 +62,14 @@ static bool linger_after_escape = FALSE; /* Whether to give ncurses some time to get the next code. */ static int statusblank = 0; /* The number of keystrokes left before we blank the status bar. */ -size_t from_x = 0; +static size_t from_x = 0; /* From where in the relevant line the current row is drawn. */ -size_t till_x = 0; +static size_t till_x = 0; /* Until where in the relevant line the current row is drawn. */ static bool has_more = FALSE; /* Whether the current line has more text after the displayed part. */ static bool is_shorter = TRUE; /* Whether a row's text is narrower than the screen's width. */ -#ifdef ENABLE_NANORC -static const char *plants_pointer = NULL; - /* Points into the expansion string for the current implantation. */ -#endif #ifndef NANO_TINY static size_t sequel_column = 0; /* The starting column of the next chunk when softwrapping. */ @@ -86,7 +88,7 @@ void add_to_macrobuffer(int code) macro_buffer[macro_length - 1] = code; } -/* Remove the last key code plus any trailing Esc codes from macro buffer. */ +/* Remove the last key code plus any leading Esc codes from macro buffer. */ void snip_last_keystroke(void) { macro_length--; @@ -126,7 +128,8 @@ void run_macro(void) return; } - key_buffer = nrealloc(key_buffer, macro_length * sizeof(int)); + if (macro_length > capacity) + reserve_space_for(macro_length); for (size_t i = 0; i < macro_length; i++) key_buffer[i] = macro_buffer[i]; @@ -186,6 +189,17 @@ linestruct *get_prev_visible_line(linestruct *line) return line->prev; } +/* Allocate the requested space for the keystroke buffer. */ +void reserve_space_for(size_t newsize) +{ + if (newsize < capacity) + die(_("Too much input at once\n")); + + key_buffer = nrealloc(key_buffer, newsize * sizeof(int)); + nextcodes = key_buffer; + capacity = newsize; +} + /* Control character compatibility: * * - Ctrl-H is Backspace under ASCII, ANSI, VT100, and VT220. @@ -209,22 +223,17 @@ linestruct *get_prev_visible_line(linestruct *line) * We support escape sequences for ANSI, VT100, VT220, VT320, the Linux * console, the FreeBSD console, the Mach console, xterm, and Terminal, * and some for Konsole, rxvt, Eterm, and iTerm2. Among these sequences, - * there are several conflicts and omissions: + * there are some conflicts: * - * - Tab on ANSI == PageUp on FreeBSD console; the former is omitted. + * - PageUp on FreeBSD console == Tab on ANSI; the latter is omitted. * (Ctrl-I is also Tab on ANSI, which we already support.) * - PageDown on FreeBSD console == Center (5) on numeric keypad with - * NumLock off on Linux console; the latter is omitted. (The editing - * keypad key is more important to have working than the numeric - * keypad key, because the latter has no value when NumLock is off.) - * - F1 on FreeBSD console == the mouse key on xterm/rxvt/Eterm; the - * latter is omitted. (Mouse input will only work properly if the - * extended keypad value KEY_MOUSE is generated on mouse events - * instead of the escape sequence.) + * NumLock off on Linux console; the latter is useless and omitted. + * - F1 on FreeBSD console == the mouse sequence on xterm/rxvt/Eterm; + * the latter is omitted. (Mouse input works only when KEY_MOUSE + * is generated on mouse events, not with the raw escape sequence.) * - F9 on FreeBSD console == PageDown on Mach console; the former is - * omitted. (The editing keypad is more important to have working - * than the function keys, because the functions of the former are - * not arbitrary and the functions of the latter are.) + * omitted. (Moving the cursor is more important than a function key.) * - F10 on FreeBSD console == PageUp on Mach console; the former is * omitted. (Same as above.) */ @@ -251,6 +260,7 @@ void read_keys_from(WINDOW *frame) lastmessage != INFO) || spotlighted)) { timed = TRUE; halfdelay(ISSET(QUICK_BLANK) ? 8 : 15); + /* Counteract a side effect of half-delay mode. */ disable_kb_interrupt(); } #endif @@ -267,6 +277,7 @@ void read_keys_from(WINDOW *frame) if (timed) { timed = FALSE; + /* Leave half-delay mode. */ raw(); if (input == ERR) { @@ -298,10 +309,13 @@ void read_keys_from(WINDOW *frame) curs_set(0); - /* Initiate the keystroke buffer, and save the keycode in it. */ - key_buffer = nrealloc(key_buffer, sizeof(int)); - nextcodes = key_buffer; + /* When there is no keystroke buffer yet, allocate one. */ + if (!key_buffer) + reserve_space_for(capacity); + key_buffer[0] = input; + + nextcodes = key_buffer; waiting_codes = 1; #ifndef NANO_TINY @@ -335,10 +349,11 @@ void read_keys_from(WINDOW *frame) if (input == ERR) break; - /* Extend the keystroke buffer, and save the keycode at its end. */ - key_buffer = nrealloc(key_buffer, ++waiting_codes * sizeof(int)); - key_buffer[waiting_codes - 1] = input; - nextcodes = key_buffer; + /* When the keystroke buffer is full, extend it. */ + if (waiting_codes == capacity) + reserve_space_for(2 * capacity); + + key_buffer[waiting_codes++] = input; } /* Restore blocking-input mode. */ @@ -361,16 +376,11 @@ size_t waiting_keycodes(void) /* Add the given keycode to the front of the keystroke buffer. */ void put_back(int keycode) { - /* If the keystroke buffer is at maximum capacity, don't add anything. */ - if (waiting_codes + 1 < waiting_codes) - return; - /* If there is no room at the head of the keystroke buffer, make room. */ if (nextcodes == key_buffer) { - key_buffer = nrealloc(key_buffer, (waiting_codes + 1) * sizeof(int)); - if (waiting_codes) - memmove(key_buffer + 1, key_buffer, waiting_codes * sizeof(int)); - nextcodes = key_buffer; + if (waiting_codes == capacity) + reserve_space_for(2 * capacity); + memmove(key_buffer + 1, key_buffer, waiting_codes * sizeof(int)); } else nextcodes--; @@ -411,19 +421,24 @@ int get_code_from_plantation(void) commandname = measured_copy(plants_pointer + 1, closing - plants_pointer - 1); planted_shortcut = strtosc(commandname); - if (planted_shortcut) { - plants_pointer = closing + 1; - if (*plants_pointer != '\0') - put_back(MORE_PLANTS); - return PLANTED_COMMAND; - } else + if (!planted_shortcut) return NO_SUCH_FUNCTION; + + plants_pointer = closing + 1; + + if (*plants_pointer != '\0') + put_back(MORE_PLANTS); + + return PLANTED_COMMAND; } else { char *opening = strchr(plants_pointer, '{'); - int length = (opening ? opening - plants_pointer : strlen(plants_pointer)); + int length; - if (opening) + if (opening) { + length = opening - plants_pointer; put_back(MORE_PLANTS); + } else + length = strlen(plants_pointer); for (int index = length - 1; index >= 0; index--) put_back((unsigned char)plants_pointer[index]); @@ -1397,79 +1412,48 @@ int get_kbinput(WINDOW *frame, bool showcursor) #ifdef ENABLE_UTF8 #define INVALID_DIGIT -77 -/* If the given symbol is a valid hexadecimal digit, multiply it by factor - * and add the result to the given unicode, and return PROCEED to signify - * okay. When not a hexadecimal digit, return the symbol itself. */ -long add_unicode_digit(int symbol, long factor, long *unicode) -{ - if ('0' <= symbol && symbol <= '9') - *unicode += (symbol - '0') * factor; - else if ('a' <= tolower(symbol) && tolower(symbol) <= 'f') - *unicode += (tolower(symbol) - 'a' + 10) * factor; - else - return INVALID_DIGIT; - - return PROCEED; -} - -/* For each consecutive call, gather the given symbol into a six-digit Unicode - * (from 000000 to 10FFFF, case-insensitive). When it is complete, return the - * assembled Unicode; until then, return PROCEED when the symbol is valid. */ +/* For each consecutive call, gather the given symbol into a Unicode code point. + * When it's complete (with six digits, or when Space or Enter is typed), return + * the assembled code. Until then, return PROCEED when the symbol is valid, or + * an error code for anything other than hexadecimal, Space, and Enter. */ long assemble_unicode(int symbol) { static long unicode = 0; static int digits = 0; - long retval = PROCEED; + long outcome = PROCEED; - switch (++digits) { - case 1: - unicode = (symbol - '0') * 0x100000; - break; - case 2: - /* The second digit must be zero if the first was one, but - * may be any hexadecimal value if the first was zero. */ - if (symbol == '0' || unicode == 0) - retval = add_unicode_digit(symbol, 0x10000, &unicode); - else - retval = INVALID_DIGIT; - break; - case 3: - /* Later digits may be any hexadecimal value. */ - retval = add_unicode_digit(symbol, 0x1000, &unicode); - break; - case 4: - retval = add_unicode_digit(symbol, 0x100, &unicode); - break; - case 5: - retval = add_unicode_digit(symbol, 0x10, &unicode); - break; - case 6: - retval = add_unicode_digit(symbol, 0x1, &unicode); - /* If also the sixth digit was a valid hexadecimal value, then - * the Unicode sequence is complete, so return it. */ - if (retval == PROCEED) - retval = unicode; - break; - } + if ('0' <= symbol && symbol <= '9') + unicode = (unicode << 4) + symbol - '0'; + else if ('a' <= (symbol | 0x20) && (symbol | 0x20) <= 'f') + unicode = (unicode << 4) + (symbol | 0x20) - 'a' + 10; + else if (symbol == '\r' || symbol == ' ') + outcome = unicode; + else + outcome = INVALID_DIGIT; + + /* If also the sixth digit was a valid hexadecimal value, then the + * Unicode sequence is complete, so return it (when it's valid). */ + if (++digits == 6 && outcome == PROCEED) + outcome = (unicode < 0x110000) ? unicode : INVALID_DIGIT; /* Show feedback only when editing, not when at a prompt. */ - if (retval == PROCEED && currmenu == MMAIN) { - char partial[7] = "......"; + if (outcome == PROCEED && currmenu == MMAIN) { + char partial[7] = " "; - /* Construct the partial result, right-padding it with dots. */ - snprintf(partial, digits + 1, "%06lX", unicode); - partial[digits] = '.'; + sprintf(partial + 6 - digits, "%0*lX", digits, unicode); /* TRANSLATORS: This is shown while a six-digit hexadecimal * Unicode character code (%s) is being typed in. */ statusline(INFO, _("Unicode Input: %s"), partial); } - /* If we have an end result, reset the Unicode digit counter. */ - if (retval != PROCEED) + /* If we have an end result, reset the value and the counter. */ + if (outcome != PROCEED) { + unicode = 0; digits = 0; + } - return retval; + return outcome; } #endif /* ENABLE_UTF8 */ @@ -1483,7 +1467,6 @@ int *parse_verbatim_kbinput(WINDOW *frame, size_t *count) reveal_cursor = TRUE; - /* Read in the first code. */ keycode = get_input(frame); #ifndef NANO_TINY @@ -1498,14 +1481,14 @@ int *parse_verbatim_kbinput(WINDOW *frame, size_t *count) yield = nmalloc(6 * sizeof(int)); #ifdef ENABLE_UTF8 - /* If the first code is a valid Unicode starter digit (0 or 1), - * commence Unicode input. Otherwise, put the code back. */ - if (using_utf8() && (keycode == '0' || keycode == '1')) { + /* If the key code is a hexadecimal digit, commence Unicode input. */ + if (using_utf8() && isxdigit(keycode)) { long unicode = assemble_unicode(keycode); char multibyte[MB_CUR_MAX]; reveal_cursor = FALSE; + /* Gather at most six hexadecimal digits. */ while (unicode == PROCEED) { keycode = get_input(frame); unicode = assemble_unicode(keycode); @@ -1518,7 +1501,7 @@ int *parse_verbatim_kbinput(WINDOW *frame, size_t *count) return NULL; } #endif - /* For an invalid digit, discard its possible continuation bytes. */ + /* For an invalid keystroke, discard its possible continuation bytes. */ if (unicode == INVALID_DIGIT) { if (keycode == ESC_CODE && waiting_codes) { get_input(NULL); @@ -1599,9 +1582,6 @@ char *get_verbatim_kbinput(WINDOW *frame, size_t *count) /* Turn bracketed-paste mode back on. */ printf("\x1B[?2004h"); fflush(stdout); - - if (ISSET(ZERO) && currmenu == MMAIN) - wredrawln(midwin, editwinrows - 1, 1); #endif /* Turn flow control characters back on if necessary and turn the @@ -2533,7 +2513,7 @@ void bottombars(int menu) if (index + 2 >= number) thiswidth += COLS % itemwidth; - post_one_key(s->keystr, _(f->desc), thiswidth); + post_one_key(s->keystr, _(f->tag), thiswidth); index++; } @@ -3794,16 +3774,17 @@ void spotlight_softwrapped(size_t from_col, size_t to_col) #endif #ifdef ENABLE_EXTRA -#define CREDIT_LEN 54 +#define CREDIT_LEN 52 #define XLCREDIT_LEN 9 /* Fully blank the terminal screen, then slowly "crawl" the credits over it. * Abort the crawl upon any keystroke. */ void do_credits(void) { - bool with_empty_line = ISSET(EMPTY_LINE); + bool with_interface = !ISSET(ZERO); bool with_help = !ISSET(NO_HELP); - int kbinput = ERR, crpos = 0, xlpos = 0; + int crpos = 0, xlpos = 0; + const char *credits[CREDIT_LEN] = { NULL, /* "The nano text editor" */ NULL, /* "version" */ @@ -3851,13 +3832,11 @@ void do_credits(void) "", "", "", - "", "(C) 2022", "Free Software Foundation, Inc.", "", "", "", - "", "https://nano-editor.org/" }; @@ -3873,8 +3852,8 @@ void do_credits(void) N_("Thank you for using nano!") }; - if (with_empty_line || with_help) { - UNSET(EMPTY_LINE); + if (with_interface || with_help) { + SET(ZERO); SET(NO_HELP); window_init(); } @@ -3882,49 +3861,38 @@ void do_credits(void) nodelay(midwin, TRUE); scrollok(midwin, TRUE); - blank_titlebar(); blank_edit(); - blank_statusbar(); - - wrefresh(topwin); wrefresh(midwin); - wrefresh(footwin); - napms(700); + napms(600); for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) { if (crpos < CREDIT_LEN) { - const char *what; + const char *text = credits[crpos]; - if (credits[crpos] == NULL) - what = _(xlcredits[xlpos++]); - else - what = credits[crpos]; + if (!text) + text = _(xlcredits[xlpos++]); - mvwaddstr(midwin, editwinrows - 1 - (editwinrows % 2), - COLS / 2 - breadth(what) / 2 - 1, what); + mvwaddstr(midwin, editwinrows - 1, (COLS - breadth(text)) / 2, text); wrefresh(midwin); } - if ((kbinput = wgetch(midwin)) != ERR) + if (wgetch(midwin) != ERR) break; - napms(700); + napms(600); wscrl(midwin, 1); wrefresh(midwin); - if ((kbinput = wgetch(midwin)) != ERR) + if (wgetch(midwin) != ERR) break; - napms(700); + napms(600); wscrl(midwin, 1); wrefresh(midwin); } - if (kbinput != ERR) - ungetch(kbinput); - - if (with_empty_line) - SET(EMPTY_LINE); + if (with_interface) + UNSET(ZERO); if (with_help) UNSET(NO_HELP); window_init();