Compare commits

...

212 Commits
v4.8 ... v4.9

Author SHA1 Message Date
Benno Schulenberg
e7fa43d600 po: update translations and regenerate POT file and PO files 2020-03-24 11:21:03 +01:00
Benno Schulenberg
49a84f9e8a bump version numbers and add a news item for the 4.9 release 2020-03-24 11:11:22 +01:00
Benno Schulenberg
6ee657b7d8 docs: trim some TODO items, and condense several others
Remove the suggested clever bracket matching.
2020-03-23 11:30:34 +01:00
Benno Schulenberg
6866ea0d0e justify: skip over in-line whitespace only, not over leading whitespace
This fixes https://savannah.gnu.org/bugs/?58028.

Bug existed since commit 107abd26 from yesterday.
2020-03-23 11:00:17 +01:00
Benno Schulenberg
34d38fe1c6 undo: treat a cut-until-end-of-buffer like a backward marked region
This fixes https://savannah.gnu.org/bugs/?58024.

Bug existed since commit ac612545 from two weeks go.
2020-03-22 19:16:22 +01:00
Benno Schulenberg
b17309b652 tweaks: when extending the marked region, include also exotic blanks
Just in case someone uses multibyte whitespace characters.
2020-03-22 18:45:46 +01:00
Benno Schulenberg
107abd2654 justify: skip over blanks after the region, to not skew the indentation
This fixes https://savannah.gnu.org/bugs/?58023.

Bug existed since commit 3225c71e from six days ago.
2020-03-22 17:48:39 +01:00
Benno Schulenberg
f528ced22b tweaks: use a symbol instead of a number, and drop two unneeded casts 2020-03-22 14:29:10 +01:00
Benno Schulenberg
71628ad0b8 build: fix compilation for --enable-tiny --enable-justify 2020-03-22 13:41:28 +01:00
Benno Schulenberg
481529e865 input: accommodate silly emulators that have LF instead of CR in a paste
Even though a user would never type ^J to start a new line, accept it
as a synonym of <Enter> (^M) in a bracketed paste, because apparently
bash accepts it as a synonym too.

This works around https://savannah.gnu.org/bugs/?58010.
2020-03-20 14:25:27 +01:00
Benno Schulenberg
bc6645f753 tweaks: move two functions to before the ones that call them 2020-03-19 15:12:40 +01:00
Benno Schulenberg
c1ca578853 tweaks: unwrap four lines, and use explicit codes where possible
The explicit codes are slightly faster, and they show the correspondence.
2020-03-19 14:55:39 +01:00
Benno Schulenberg
4ce2e146ea tweaks: elide three unneeded #defines
Backspace and Tab and Carriage Return have standard backslash escapes.
2020-03-19 14:40:51 +01:00
Benno Schulenberg
39ed865043 docs: improve the descriptions of --softwrap and 'set softwrap' 2020-03-19 13:10:28 +01:00
Benno Schulenberg
0de4700002 gnulib: update to its current upstream state 2020-03-17 09:56:54 +01:00
Benno Schulenberg
ea95ad24be usage: improve the description of --softwrap
Concisely say what the option actually does.
2020-03-17 09:51:03 +01:00
Benno Schulenberg
415e55ff37 tweaks: move a function to before the one that calls it 2020-03-16 10:17:15 +01:00
Benno Schulenberg
c20134c20d tweaks: rename a variable, away from a single letter 2020-03-16 10:15:01 +01:00
Benno Schulenberg
56308b3256 display: keep the help items aligned, by not writing too many characters
Only for the last item at the end of each of the two help lines there
are sometimes a few extra columns available.  Do not use these extra
columns also for the other help items, because apparently, when wmove()
lands in the second column of a double-width character, it does not
wipe out this character (replacing it with a space) but goes to the
next column instead, causing a misalignment of the text.

This fixes https://savannah.gnu.org/bugs/?57994.

Bug existed since version 1.3.12, commit e806ab84.
2020-03-16 09:44:12 +01:00
Benno Schulenberg
3d8cd65b1b tweaks: reshuffle a condition, for compactness 2020-03-15 19:01:07 +01:00
Benno Schulenberg
2b6924ef6e display: do not show a "[" double-width placeholder when softwrapping
The placeholder half-duplicates the character that is actually shown
at the beginning of the next row.  When NOT softwrapping, showing a
"[" before the ">" kind of makes sense: to avoid having a gaping gap
there.  But when softwrapping, all characters of the line will be on
the screen (except when going offscreen at the bottom), so there is
no need to show a placeholder for anything.

I imagine that when reading softwrapped Chinese or Japanese text,
things are easier to understand when no characters are shown that
are not actually in the text.

This avoids https://savannah.gnu.org/bugs/?57993.

Bug existed since version 2.8.6, commit 8490f4ac.
2020-03-15 17:00:05 +01:00
Benno Schulenberg
01d8b0e15d help: increase the minimum help-text width from 32 to 40 columns
Getting long words like "Rechtschreibprüfung" broken somewhere in
the middle doesn't look nice.
2020-03-15 14:04:57 +01:00
Benno Schulenberg
16f17f3f1b help: do not break a line inside the 17-column keystrokes area
When the description of a keystroke starts with a very long word,
or when it does not contain any spaces at all (like for Chinese
and Japanese), do not break in the blanks before the description
but instead break IN the description, at the edge of the screen.

In other words: when wrapping a help text, do not consider blanks in
the first 17 columns to be blanks (unless it is a continuation line).

This fixes https://savannah.gnu.org/bugs/?57987.

Bug existed since version 4.8, commit 1486f32d,
when the wrapping of help texts was improved.
2020-03-15 13:59:04 +01:00
Benno Schulenberg
09042f5499 tweaks: do some text alignments properly: with spaces, not tabs 2020-03-15 11:26:55 +01:00
Benno Schulenberg
3690d60b71 rcfile: rename bindable function 'suspendenable' to 'suspendable'
The name of a toggle function should match the name of the corresponding
option, like for 'mouse' and 'linenumbers' and 'smarthome' and such.
2020-03-15 11:17:46 +01:00
Benno Schulenberg
4dedb1c1b1 moving: do not put the cursor at end-of-line when in a help text
Keeping the invisible cursor at the left edge avoids any line getting
scrolled horizontally.

This fixes https://savannah.gnu.org/bugs/?57991.

Bug existed since version 2.8.2, since the help texts got generated
and displayed in a different way in order to become searchable.
2020-03-13 19:37:02 +01:00
Benno Schulenberg
067ee1a6b1 tweaks: highlight keystrokes in the documentation more consistently 2020-03-13 16:40:34 +01:00
Benno Schulenberg
0c1db4879e docs: update the missed occurrences of --suspendable / 'set suspendable' 2020-03-13 16:34:41 +01:00
Benno Schulenberg
02efc760bf tweaks: rename a symbol, to match the corresponding renamed option 2020-03-13 16:17:10 +01:00
Benno Schulenberg
a91bc04e04 docs: update the form of an option, --suspendable / 'set suspendable' 2020-03-13 15:58:56 +01:00
Benno Schulenberg
9a59cddcd4 options: rename --suspend to --suspendable, to make more sense 2020-03-13 15:54:24 +01:00
Benno Schulenberg
6ab754676a build: update the conditional placement of the "Go To Line" menu item
When either only --disable-justify or --disable-speller is used, the
"Go To Line" item should not be placed after the ^C Position item, to
keep the subsequent items (Undo, Redo, and so on) nicely paired.

(Things changed since the Linter is no longer bound to ^T by default.)
2020-03-13 12:24:44 +01:00
Benno Schulenberg
9917a05f04 tweaks: exclude a function when compiled without spell-checking support 2020-03-13 11:59:08 +01:00
Benno Schulenberg
fcda76f684 build: restore non-UTF8 fallbacks, to allow compiling with --disable-utf8
Commits b2c63c3d and 004af03e from yesterday mistakenly removed those
calls.
2020-03-13 11:43:31 +01:00
Benno Schulenberg
639f37669a docs: remove the note saying that nanorc files must be in Unix format
Since commit 98f038ad from a month ago, nanorc files do not need to be
in Unix format (LF) but may also be in DOS (CR+LF) and Mac (CR) format.
2020-03-12 19:03:02 +01:00
Benno Schulenberg
6d720bf7b2 tweaks: improve two comments, and remove an unneeded one 2020-03-12 17:18:10 +01:00
Benno Schulenberg
f3b030c8e5 tweaks: condense a fragment of code
Leave the skipping of subsequent blanks to the succeeding code.
2020-03-12 16:59:27 +01:00
Benno Schulenberg
6b8b7c9d55 tweaks: elide a supporting variable, to make four loops slightly faster
At the cost of making the return statements slower.  But, all-in-all,
this should be slightly quicker.
2020-03-12 16:47:39 +01:00
Benno Schulenberg
21ed79938e tweaks: normalize the indentation after the previous two changes 2020-03-12 15:54:19 +01:00
Benno Schulenberg
004af03ea5 tweaks: remove non-UTF-8 code from three more functions 2020-03-12 15:54:19 +01:00
Benno Schulenberg
b2c63c3d3c chars: optimize a function for the most common blanks: space and tab
Also, do not bother to provide separate code for the non-UTF-8 case.
Instead, optimize for plain ASCII characters.
2020-03-12 15:54:19 +01:00
Benno Schulenberg
ae139021eb tweaks: rename four more functions, to get rid of an abbreviation
Also, improve their comments.
2020-03-12 15:54:19 +01:00
Benno Schulenberg
f6dedf3598 tweaks: rename another function, to remove the obscuring abbreviation 2020-03-12 15:54:19 +01:00
Benno Schulenberg
8003842e5c tweaks: rename a function, to remove an obscuring abbreviation
The "mb" made the name harder to read.  Also, the function is
not only for multibyte characters but for any character.
2020-03-12 15:53:49 +01:00
Benno Schulenberg
01477d3b81 justify: never break a line in leading whitespace
This fixes https://savannah.gnu.org/bugs/?57984.

The bug was old -- it existed since at least version 2.0.6.
2020-03-12 10:15:57 +01:00
Benno Schulenberg
ebf9279f95 tweaks: rename two more functions, to match the style of others 2020-03-11 19:47:21 +01:00
Benno Schulenberg
304548ef82 tweaks: rename two functions, to match the style of others 2020-03-11 19:45:06 +01:00
Benno Schulenberg
dd6f129c3c tweaks: rename two functions, for shortness 2020-03-11 19:43:03 +01:00
Benno Schulenberg
c2f007afc5 tweaks: trim an unnecessary detail from an error message 2020-03-11 14:23:22 +01:00
Benno Schulenberg
8dc83951ee tweaks: normalize a translator hint, update one, and add another 2020-03-11 14:21:08 +01:00
Benno Schulenberg
b7dc5abca7 tweaks: rename two more variables, to harmonize with two others 2020-03-11 12:53:39 +01:00
Benno Schulenberg
8b784785ec tweaks: rename two variables, for distinctiveness 2020-03-11 12:23:07 +01:00
Benno Schulenberg
750b51c343 copyright: update to the current year for significantly changed files 2020-03-11 11:52:15 +01:00
Benno Schulenberg
e09ed765a1 syntax: nanorc: colorize 'rawsequences', not the obsolete 'rebindkeypad'
Bug existed since version 4.0, commit b596639f.
2020-03-10 16:21:02 +01:00
Benno Schulenberg
70a765e387 feedback: give a clearer message when trying to justify an empty region
Also, increase the level of the message, as trying to justify an empty
selection is a user error.
2020-03-10 15:58:30 +01:00
Benno Schulenberg
c65fa25ce2 docs: mention that ^[ (Esc) is unbindable, and explain why 2020-03-10 15:55:19 +01:00
Benno Schulenberg
cbbfebbc7a bindings: remove the translation of ^H to Backspace on the BSDs
This avoids an unintended rebinding of the <Backspace> key
on terminal emulators.

This fixes https://savannah.gnu.org/bugs/?57981.
Reported-by: Ciprian Tomoiaga <ciprian.tomoiaga@gmail.com>.
2020-03-10 14:28:55 +01:00
Benno Schulenberg
9d499c769b justify: do not take an empty line as template for first-line indentation
This fixes https://savannah.gnu.org/bugs/?57437.
2020-03-10 10:56:05 +01:00
Benno Schulenberg
dad2b5541e tweaks: rename a variable, for aptness 2020-03-10 10:50:27 +01:00
Benno Schulenberg
56960ce05e tweaks: reshuffle some stuff, to have related things together
Also for compactness, and not to use 'lead_len' for two purposes.
2020-03-10 10:45:46 +01:00
Benno Schulenberg
e247be69bf justify: when the cursor is at the left edge, keep it there
Only when the endpoint of the selection is actually IN a leading part,
then it should be advanced to include the whole leading part, so that
the succeeding paragraph will get the proper first-line indentation.
2020-03-10 10:32:21 +01:00
Benno Schulenberg
e209686835 justify: give the first line of a marked region its proper indentation
When justifying a selection from the middle of a paragraph, the first
line of both that selection and of the new paragraph after it should
get the same indentation as the first line of the original paragraph.

This fulfills https://savannah.gnu.org/bugs/?56308.
2020-03-10 10:29:49 +01:00
Benno Schulenberg
047936c338 tweaks: reshuffle an assignment and a free() 2020-03-09 14:28:57 +01:00
Benno Schulenberg
ca0571ea52 tweaks: remove an unneeded indirection
The concatenating function does not change the passed linestruct.
The data yes, but not the linestruct itself.
2020-03-09 14:25:41 +01:00
Benno Schulenberg
655cf9573f tweaks: always determine the second lead, to simplify the rewrap call
Also rename some variables.
2020-03-09 14:11:47 +01:00
Benno Schulenberg
c457f744ef justify: trim the leading blanks of a marked region at the right moment
This fixes https://savannah.gnu.org/bugs/?57977.

Bug existed since commit 5310a355 from three hours ago.
2020-03-09 14:05:18 +01:00
Benno Schulenberg
8f3ea23ece tweaks: rename a variable, to not be a substring of a function name 2020-03-09 12:31:10 +01:00
Benno Schulenberg
3225c71ecd justify: when appropriate, move end point of marked region forward
So that also the line after the newly made paragraph gets the
leading part that belongs to the start of a new paragraph.

This https://savannah.gnu.org/bugs/?57974.

Bug existed since version 4.0, since the justifying
of a marked region was introduced.
2020-03-09 12:15:01 +01:00
Benno Schulenberg
5310a355ef tweaks: reshuffle the trimming of leading whitespace, for compactness 2020-03-09 11:43:50 +01:00
Benno Schulenberg
d7875cdf4f tweaks: when appropriate, move starting point of justified region back
This makes things simpler, so that I can understand them.
2020-03-09 11:33:16 +01:00
Benno Schulenberg
dc05defb8a tweaks: rename four variables, to be a bit more telling 2020-03-09 11:10:07 +01:00
Benno Schulenberg
98c11d0a82 tweaks: reshuffle some code, in preparation for improving it 2020-03-09 11:09:56 +01:00
Benno Schulenberg
14f1652842 tweaks: move a function to its proper place in the order of things 2020-03-09 09:44:43 +01:00
Benno Schulenberg
d4b97a08cd tweaks: elide a variable and an unneeded iteration 2020-03-08 16:55:02 +01:00
Benno Schulenberg
c934c1d71b justify: trim prefixed whitespace when justifying a marked region
This fixes https://savannah.gnu.org/bugs/?56307.

Bug existed since version 4.0, when the justifying
of a marked region was introduced.
2020-03-08 16:18:34 +01:00
Benno Schulenberg
a16315a65f tweaks: remove a redundant statement, a spurious reference to 'cutbottom'
This assignment should have been removed in commit f5ac8c1e from
six years ago, as it became superfluous then.  Later changes made
'cutbottom' become valid only when something had actually been cut.
2020-03-08 15:50:55 +01:00
Benno Schulenberg
d6beca5dcf justify: do not copy too many bytes when trimming leading whitespace
This fixes https://savannah.gnu.org/bugs/?57972.

Bug existed since version 4.0, commit 877a6498.
2020-03-08 15:09:40 +01:00
Benno Schulenberg
9d7e62be1a tweaks: rename two variables, to be different or for more contrast
Also, invert the logic of the second.
2020-03-08 14:12:45 +01:00
Benno Schulenberg
5c008726f0 tweaks: rename two variables, to harmonize with two others 2020-03-08 14:04:29 +01:00
Benno Schulenberg
8c506d86af tweaks: rename two variables, to suit both the marked and unmarked case 2020-03-08 13:57:04 +01:00
Benno Schulenberg
db7a49edc6 tweaks: create an undo item earlier, and discard it when needed
This avoids having to temporariyly store and then later fill in
the original cursor position.
2020-03-08 13:40:38 +01:00
Benno Schulenberg
2428c5cbe6 tweaks: strip a parameter that is equivalent to 'openfile' for both calls 2020-03-08 12:54:47 +01:00
Benno Schulenberg
01bc0148c0 tweaks: strip a parameter that is TRUE for both calls
The parameter has been redundant since version 4.0, commit d54840c1,
when the last call with FALSE was removed.
2020-03-08 12:48:41 +01:00
Benno Schulenberg
a92511ceaf tweaks: move another function to where it is used 2020-03-06 18:52:42 +01:00
Benno Schulenberg
19517fc51e tweaks: elide a parameter, and rename a variable 2020-03-06 17:06:14 +01:00
Benno Schulenberg
71c35d3ca2 tweaks: move a function to where it is used 2020-03-06 17:00:31 +01:00
Benno Schulenberg
38af812a82 tweaks: reshuffle the setting of the starting point of a cut 2020-03-06 16:57:22 +01:00
Benno Schulenberg
622111613f tweaks: elide two variables and their two assignments
Also reshuffle two declarations, and confine the swapping of
the endpoints of a backward-marked region to a smaller scope.
2020-03-06 12:49:06 +01:00
Benno Schulenberg
21ef05058c tweaks: add a COUPLE_END undo item a bit later, instead of updating it 2020-03-06 12:30:24 +01:00
Benno Schulenberg
abcdd6605c tweaks: reshuffle a condition, for compacter code 2020-03-05 19:37:09 +01:00
Benno Schulenberg
f2d89cb3bc justify: restore a region properly when it was marked backwards
This fixes https://savannah.gnu.org/bugs/?57960.

Bug existed since version 4.8, commit e01651cd.
2020-03-05 18:46:37 +01:00
Benno Schulenberg
2d05ce37c6 justify: do not crash when the user attempts to justify an empty region
This fixes https://savannah.gnu.org/bugs/?57959.

Bug existed since version 4.0, since the justifying
of a marked region was introduced.
2020-03-05 16:57:16 +01:00
Benno Schulenberg
2bc5c1987c tweaks: factor out a three-line condition into a separate function
For clarity.
2020-03-05 16:09:56 +01:00
Benno Schulenberg
e39e2ddf52 tweaks: add a supporting variable, in order to condense some statements 2020-03-05 16:07:52 +01:00
Benno Schulenberg
ac61254581 tweaks: invert the logic of a symbol, to make more sense
Giving a name to the uncommon case is more fitting.

It also avoids a negation and allows to elide a condition.
2020-03-05 15:12:08 +01:00
Benno Schulenberg
9e182fde66 tweaks: swap the use of 'head' and 'tail' for CUT and PASTE undo items
Now the 'head' endpoint is closer to the top of the buffer, and the
'tail' endpoint closer to the bottom.  That makes more sense.
2020-03-05 14:52:02 +01:00
Benno Schulenberg
70d59ffe16 tweaks: remove a redundant assignment
Consecutive additions all happen on the same line.  When automatic
hard-wrapping occurs, the ADD item is grouped together with an ENTER
item, and subsequent additions go into a new ADD item.
2020-03-05 13:32:20 +01:00
Benno Schulenberg
fb4bcd8cfd undo: store the cursor row, for redoing filtering & justification better
This fixes https://savannah.gnu.org/bugs/?57958.
2020-03-05 12:34:55 +01:00
Benno Schulenberg
2288abcd49 tweaks: use the variable that suits 'END' better 2020-03-05 12:34:17 +01:00
Benno Schulenberg
42fde1cbc6 tweaks: improve four comments, and condense two fragments of code 2020-03-04 16:45:26 +01:00
Benno Schulenberg
d675a549de rcfile: don't store a coloring rule before it is complete
This really fixes https://savannah.gnu.org/bugs/?57950.
2020-03-04 16:08:34 +01:00
Benno Schulenberg
6bab8d19c6 tweaks: adjust the indentation after the previous change
And regroup things a bit.
2020-03-03 19:46:46 +01:00
Benno Schulenberg
365058c48f rcfile: when a start= is not matched with an end=, abandon the whole rule
This fixes https://savannah.gnu.org/bugs/?57950.

Bug existed since before version 2.2.0.
2020-03-03 19:42:40 +01:00
Benno Schulenberg
5eb7145939 rcfile: do not allow a regex for name, header, or magic to be empty
If the user really wants to match anything, ".*" should be used.

(This also stops nano looking at the rest of the line as soon as an
empty regular expression is encountered.  This may seem like poorer
feedback than before, but... I think that multiple error messages
per line are more confusing than helpful.)

This fixes https://savannah.gnu.org/bugs/?57942.

Bug existed since before version 2.2.0.
2020-03-03 19:21:08 +01:00
Benno Schulenberg
4dab491df2 tweaks: improve three comments, and reshuffle two declarations 2020-03-03 16:42:40 +01:00
Benno Schulenberg
9f92341c84 tweaks: check for a starting quote in one place instead of three 2020-03-03 16:31:28 +01:00
Benno Schulenberg
3357712928 tweaks: remove two superfluous conditions
The 'ptr' variable can never be NULL in those places -- whenever it
becomes NULL, the routine returns immediately.
2020-03-03 12:08:09 +01:00
Benno Schulenberg
0e59c12af4 tweaks: rename a function, and split a variable into two separate ones 2020-03-03 11:27:37 +01:00
Benno Schulenberg
aab0d43f47 tweaks: rename a function, to be more fitting, and improve some comments 2020-03-03 11:16:51 +01:00
Benno Schulenberg
9cc32c920b rcfile: when finding a mistake, skip the rest of the line 2020-03-03 10:55:23 +01:00
Benno Schulenberg
72ad00f9d0 tweaks: change an integer to a short, and reshuffle it for better packing
A short allows for more than 32 thousand values, and the maximum number
of multiline regexes in any of the current syntaxes is... just four.

Reshuffle it to the beginning also because it is used the most often.
2020-03-02 19:50:08 +01:00
Benno Schulenberg
89217f20af tweaks: rename two symbols, to be more precise 2020-03-02 17:16:07 +01:00
Benno Schulenberg
477a9b33b4 files: when piping, always pipe whatever was cut to the external command
Either the marked region was cut (even when zero characters) or
the entire contents of the buffer were cut.  In both cases this
is what needs to be piped to the command.  That is: the contents
of the cutbuffer.

This fixes https://savannah.gnu.org/bugs/?57931.

Bug existed since version 2.9.8, commit f304b9ae,
when piping text to an external command was introduced.
2020-03-02 13:36:56 +01:00
Benno Schulenberg
5c6fe50487 tweaks: rename a variable, to be a bit clearer 2020-03-01 19:45:02 +01:00
Benno Schulenberg
6c34ae5804 files: don't check uninitialized memory when writing new file [valgrind]
This fixes https://savannah.gnu.org/bugs/?57934.

Bug existed since version 4.3, commit e8e30e51.
2020-03-01 19:22:35 +01:00
Benno Schulenberg
4409b275fc tweaks: avoid a complaint about uninitialized memory [valgrind]
This addresses https://savannah.gnu.org/bugs/?57932.
2020-03-01 17:08:50 +01:00
Benno Schulenberg
8848ac5a9b undo: do not try to copy a cutbuffer that is NULL
Allow the creation of an empty CUT undo item (because the result of a
filtering operation may be empty), but then don't crash when undoing
such an empty CUT.

This fixes https://savannah.gnu.org/bugs/?57929.

Bug existed since version 2.9.8, when filtering was introduced.
2020-03-01 12:59:54 +01:00
Benno Schulenberg
c055e629c7 tweaks: copy and store a deleted character in a conciser manner 2020-02-28 19:24:31 +01:00
Benno Schulenberg
e6f26464d7 tweaks: add calls of die() for five theoretical programming mistakes 2020-02-28 19:15:23 +01:00
Benno Schulenberg
a0aeeba075 tweaks: remove two redundant case labels
The add_undo() and update_undo() functions are never called with
JOIN as argument, so there is no need to have a case label for it.
2020-02-28 19:05:09 +01:00
Benno Schulenberg
c640a55b1c tweaks: frob a statement, rewrap two lines, and remove a pair of braces 2020-02-28 18:59:04 +01:00
Benno Schulenberg
7d0b7e47c1 undo: use the correct original fusion point when unjoining two lines
When a <Backspace> did the join, 'head' and 'tail' are not the same.

This fixes https://savannah.gnu.org/bugs/?57916.

Bug existed since commit 7cf08b93 from yesterday.
2020-02-28 17:09:03 +01:00
Benno Schulenberg
132ed5c8f0 tweaks: instead of swapping the end points later, assign them correctly
(This also makes it clear that the naming of 'head' and 'tail'
is the wrong way around for CUT too.)
2020-02-28 15:25:23 +01:00
Benno Schulenberg
bd88b8b5a6 undo: do not mark the buffer as modified when pasting back nothing
This completes the fix for https://savannah.gnu.org/bugs/?57915.
2020-02-28 13:40:35 +01:00
Benno Schulenberg
0568f7a2b6 undo: do not try to paste back an empty cutbuffer
Trying to do so would dereference a NULL pointer in copy_node().

This fixes https://savannah.gnu.org/bugs/?57915.

Bug existed since before version 2.2.0.
2020-02-28 13:40:06 +01:00
Benno Schulenberg
1a28b08694 tweaks: frob two statements, condense another, and add a comment
Also, remove two superfluous closings of file descriptors.  The second
one has most likely already been closed by send_data(), by closing the
tube, and the first one will be closed by exiting from the process.
2020-02-28 12:41:27 +01:00
Benno Schulenberg
3b3a6b12a7 tweaks: remove two superfluous checks
The single check when creating any CUT undo item is enough.
2020-02-28 10:28:23 +01:00
Benno Schulenberg
50213eee81 tweaks: reorder a case item, to have SPLIT_BEGIN always before SPLIT_END
Also, avoid two superfluous assignments of a message, as they get
overwritten immediately by other ones, and later with the same.

Furthermore, remove two pointless placements of the cursor.  Any
necessary placements are handled by the actual adding of data and
the breaking of lines in the ADD and ENTER items.
2020-02-27 16:38:52 +01:00
Benno Schulenberg
7cd2a7da7c tweaks: remove a superfluous assignment, and reshuffle a call 2020-02-27 15:09:36 +01:00
Benno Schulenberg
4f67f364bc tweaks: when undoing an addition or redoing a deletion, do not reallocate
Especially when undoing an addition, reallocating the line data is a
waste of time, because most likely other text will be added instead.

(This also removes a coding error in the redo code for a deletion:
it allocated too many bytes for the new line data: twice the amount
of the deletion too much.)
2020-02-27 14:16:49 +01:00
Benno Schulenberg
7cf08b93ff tweaks: don't bother reallocating the line data when undoing a line join 2020-02-27 13:51:56 +01:00
Benno Schulenberg
474a1f0447 tweaks: rename to the same name two variables that have the same role 2020-02-27 13:46:57 +01:00
Benno Schulenberg
76d7fbd5ee tweaks: rename a variable, away from a single letter 2020-02-27 13:28:01 +01:00
Benno Schulenberg
8d87410c94 tweaks: rearrange a case item, so that PASTE is always after CUT
Also, for a REPLACE, the 'head' point does not need to be updated,
because a replacement does not change the cursor position.
2020-02-27 12:13:24 +01:00
Benno Schulenberg
1961c052c8 tweaks: simplify the undoing and redoing of an <Enter>
I don't know what I was thinking three years ago with this convoluted
'from_x' and 'to_x'.  When undoing an <Enter>, the cursor always needs
to be placed back at the 'head' point.  And the data always needs to be
copied starting from 'tail_x' (skipping any whitespace that autoindent
might have added).

When redoing an <Enter>, there is no need to reallocate and copy the
data of the line, it is enough to just clip it at the original cursor
position.  (This wastes some memory, but... how often does one redo
an <Enter>?  So, conciseness of the code is preferable.)
2020-02-27 12:11:58 +01:00
Benno Schulenberg
843dbd56ef tweaks: rename two more elements of an undo record, for symmetry
(Note that for a PASTE and a COUPLE_END the variables seem to be used
the wrong way around.  Maybe this can be improved.)
2020-02-27 10:08:48 +01:00
Benno Schulenberg
be3c54c670 tweaks: rename two elements of an undo record, to be more telling 2020-02-27 10:08:48 +01:00
Benno Schulenberg
01afcb243c tweaks: rename a variable, to get out of the way of a later rename 2020-02-27 09:35:23 +01:00
Benno Schulenberg
b0eb73f7b3 feedback: make sure that a later message can overwrite a short warning
This fixes https://savannah.gnu.org/bugs/?57901.

Bug existed since version 3.0, commit 25e98776.
2020-02-26 16:41:09 +01:00
Benno Schulenberg
1907174c5f tweaks: drop a check for something that will not occur
The check has been there for a few months, and will be there for
five more years in the next Ubuntu LTS.
2020-02-26 14:01:25 +01:00
Benno Schulenberg
4da604be5a tweaks: relocate a function to before its callers 2020-02-26 13:27:22 +01:00
Benno Schulenberg
43754bd11d cutting: let M-T cut a trailing empty line, but not nothing at all
This fixes https://savannah.gnu.org/bugs/?57900.

Bug existed since version 4.0, commit 67873e96.
2020-02-26 12:38:00 +01:00
Benno Schulenberg
acd9046093 tweaks: rearrange some conditions, for compactness 2020-02-26 12:10:28 +01:00
Benno Schulenberg
2fd6181873 cutting: with --cutfromcursor, allow ^K to cut the penultimate empty line
This fixes https://savannah.gnu.org/bugs/?57897.

Bug existed since version 4.0, commit 67873e96.
2020-02-26 11:55:19 +01:00
Benno Schulenberg
b15c5a7eaf tweaks: abort in three situations that should never occur
Since is_cuttable() was added just over a year ago, ^K will
not add an undo item when it doesn't actually cut anything.
2020-02-26 11:53:27 +01:00
Benno Schulenberg
1b1d60b9d7 tweaks: drop two comments that contain variable names
Grepping for those names shouldn't find comments.  Also, the name
of the relevant boolean is clear enough to not need a comment.
2020-02-25 16:59:27 +01:00
Benno Schulenberg
9c70fdaef9 syntax: spec: drop invalid parentheses after "Summary"
Also, add the missing "RemovePathPostfixes", add extra word boundaries,
and reshuffle and regroup some things.
2020-02-25 11:47:11 +01:00
Benno Schulenberg
fcbe5aedbd tweaks: use spaces when aligning things, not tabs
Also, remove a reference to the French man page that no longer exists.
2020-02-24 19:43:19 +01:00
Benno Schulenberg
6c6a3f6bd3 syntax: spec: colorize the date and author of changelog items differently
Also, remove superfluous parentheses, add missing edge-of-word markers,
correct the "ppciseries" keyword, and fold some regexes (or elements of
them) together.
2020-02-23 20:02:48 +01:00
Benno Schulenberg
e873121372 syntax: spec: add two missing % signs, and colorize also "%triggerprein"
Furthermore, actually colorize the header line of a %changelog entry,
and remove two redundant fragments and simplify their overrides.
2020-02-23 19:38:54 +01:00
Neal Gompa
ad52830fb8 syntax: spec: add some keywords that were added in RPM 4.15 and 4.13
In RPM 4.15, a number of new spec section features were introduced:

* %dnl                       - macro-engine level comments
* %elif, %elifos, %elifarch  - 'else if' stanzas for conditionals
* %sourcelist                - section for listing sources line by line
* %patchlist                 - section for listing patches line by line
* %generate_buildrequires    - script section after %prep for
                               generating build dependencies

(Reference: https://rpm.org/wiki/Releases/4.15.0.html.)

An enhancement added in RPM 4.13:

* Sections for file trigger stanzas

(Reference: https://rpm.org/user_doc/file_triggers.html.)

As these are being increasingly used in Fedora and other distributions,
it's quite helpful to have syntax highlighting be up to date on handling
them.

Signed-off-by: Neal Gompa <ngompa13@gmail.com>
2020-02-23 15:12:04 +01:00
Benno Schulenberg
c802f91f4e tweaks: remove some unneeded braces, not used elsewhere either 2020-02-21 17:07:11 +01:00
Benno Schulenberg
51913542f4 tweaks: rename a variable, and reshuffle a declaration 2020-02-21 17:02:24 +01:00
Benno Schulenberg
be7e000389 tweaks: pull the NUL-terminating of a string into a function
Also, avoid copying one byte too many that afterward gets
overwritten with '\0'.
2020-02-20 17:08:32 +01:00
Benno Schulenberg
1d4411a474 tweaks: elide a function call, by copying a byte directly
Now all remaining calls of measured_copy() have a "+ 1" in their
second argument, and can thus be simplified.  And each of those
calls is followed by terminating the string with a NUL byte, so
thát can be pulled into the function.
2020-02-20 16:38:14 +01:00
Benno Schulenberg
76f1a4af4f tweaks: add a different helping variable 2020-02-19 19:06:04 +01:00
Benno Schulenberg
bb1a0bd86c input: after reallocating a string, do not write to its old address
The old address has become invalid, and probably does not have enough
room for the write, thus causing a crash.

This fixes https://savannah.gnu.org/bugs/?57858.

Bug existed since commit 8625609c from two days ago.
2020-02-19 18:54:01 +01:00
Benno Schulenberg
8b2b9654ab tweaks: condense a comment, reshuffle conditions, and remove unwanted one 2020-02-18 16:26:56 +01:00
Benno Schulenberg
60ae71bf3a softwrap: when typing goes beyond the bottom row, scroll just one row
This fixes https://savannah.gnu.org/bugs/?57851.

Bug existed since version 2.8.0, when scrolling per soft-wrapped chunk
was made possible.
2020-02-18 15:42:20 +01:00
Benno Schulenberg
8625609c2d tweaks: rename a variable, and add a helping one 2020-02-18 15:00:27 +01:00
Benno Schulenberg
3c4a4b04ce tweaks: normalize the indentation after the previous change
Also rewrap two lines, reshuffle three, and improve three comments.
2020-02-18 14:58:06 +01:00
Benno Schulenberg
b3faf353c9 tweaks: inject the entire burst of bytes at once into the edit buffer
Not doing it characterwise saves a reallocation, a memmove(), several
checks, and an update_undo() for each character over one.

This has been tested by temporarily setting 'bracketed_paste = FALSE'
instead of 'bracketed_paste = TRUE' in convert_sequence() in winio.c,
and then pasting stuff into nano.
2020-02-18 14:54:19 +01:00
Benno Schulenberg
f700b422be tweaks: call add_undo() before the character is actually added
And before the buffer size is adjusted, so that the add_undo()
doesn't need to correct the stored former size of the buffer.

This will allow the injection of multiple characters at once,
in the next commit.
2020-02-18 14:40:15 +01:00
Saagar Jha
1b2a2e6737 rcfile: fix an out-of-bounds read on empty lines
Signed-off-by: Saagar Jha <saagar@saagarjha.com>

Bug existed since commit 98f038ad from a week ago.
2020-02-17 12:11:40 +01:00
Benno Schulenberg
12e5c2f07d undo: for an automatic hard-wrap, store the correct previous buffer size
The SPLIT_BEGIN item gets tucked underneath the ADD item on the top of
the undo stack.  Thus: that SPLIT_BEGIN item should copy the 'wassize'
of the ADD item instead of storing the current buffer size.

This fixes https://savannah.gnu.org/bugs/?57832.

Bug existed since the undo functionality was added, and existed
in this form since version 2.3.5, commit be10c2a4.
2020-02-17 11:41:52 +01:00
Benno Schulenberg
5c07a747bb tweaks: adjust some whitespace, reshuffle two ifs, and remove two braces 2020-02-17 11:29:13 +01:00
Benno Schulenberg
a9f7277b1b tweaks: remove a now-unused helper function 2020-02-16 12:33:29 +01:00
Benno Schulenberg
d13b6d6896 tweaks: elide an intermediate copy of an added character
In theory this would allow injecting more than one character at a time
into the edit buffer.  But the creation and updating of an undo item for
the addition or the deletion of a character are a bit strange.  For all
other operations and add_undo() is called before the operation, and an
update_undo() afterward.  But for an ADD, the add_undo() is called after
the operation, and for a DEL/BACK, the update_undo() is called before
the operation.  There is some logic to that, but things would be easier
to understand if all operations were handled the same: an add_undo()
beforehand (when needed), and an update_undo() afterward.
2020-02-16 12:32:10 +01:00
Benno Schulenberg
819066c5d5 tweaks: extend the undo data for deleting and backspacing more directly
To get rid of an intermediate copy of the deleted character.
2020-02-16 11:06:45 +01:00
Benno Schulenberg
a65982bffb tweaks: relocate eleven functions to before they are called 2020-02-14 19:10:21 +01:00
Benno Schulenberg
6b8f446b52 tweaks: rename a constant, and rename and relocate a function 2020-02-14 19:00:16 +01:00
Benno Schulenberg
2b97d6563d tweaks: correct a typo, improve two indentations, and rewrap a line 2020-02-14 16:03:15 +01:00
Benno Schulenberg
a0506a15ee undo: when undoing a paste or an insertion, remove an added magic line
When something is pasted or inserted onto the final, empty line of a
buffer, an automatic new magic line is created after it, when needed.
When this paste or insertion is undone, the added magic line should
also be removed again.

This fixes https://savannah.gnu.org/bugs/?57808.

Bug existed since the undo capabilities were added,
since before version 2.3.0.
2020-02-14 15:27:06 +01:00
Benno Schulenberg
68ca1732b8 prompt: insert a burst of bytes in one go instead of characterwise
There is no need to count characters, so just insert the whole batch
of bytes at once.
2020-02-13 14:42:47 +01:00
Benno Schulenberg
75f4309c1f tweaks: rename four parameters, to be more distinct and telling 2020-02-13 14:22:40 +01:00
Benno Schulenberg
78767b583d tweaks: elide an intermediate copy of a character during injection, twice 2020-02-13 14:22:23 +01:00
Benno Schulenberg
a0703ab62d tweaks: mesh two bits of code together 2020-02-13 13:46:31 +01:00
Benno Schulenberg
9d232cd8af input: keep a multibyte character together during verbatim entry
When the user (unexpectedly) types a multibyte character after M-V,
put its first byte back into the keyboard buffer, so that the next
call of process_a_keystroke() will retrieve the byte sequence as a
whole, and will inject them as one character into the edit buffer.

This fixes https://savannah.gnu.org/bugs/?57804.

Bug existed since version 2.6.3, commit 08c51cfd.
2020-02-13 13:15:46 +01:00
Benno Schulenberg
03d296eb35 input: allocate sufficient bytes for entering a Unicode codepoint
This fixes https://savannah.gnu.org/bugs/?57803.

Bug existed since commit 2b27df67 from yesterday.
2020-02-13 11:57:35 +01:00
Benno Schulenberg
2b27df6733 tweaks: convert integers to bytes in one place instead of two 2020-02-12 17:16:27 +01:00
Benno Schulenberg
f012d54a1d tweaks: make prompt-bar input more similar to edit-buffer input
In preparation for the next commit.
2020-02-12 16:59:48 +01:00
Benno Schulenberg
61dc2cab0b tweaks: rename two variables, and frob four comments 2020-02-12 16:20:20 +01:00
Benno Schulenberg
a2313f499c tweaks: move a function to after the one that it calls 2020-02-12 16:06:57 +01:00
Benno Schulenberg
cbda89766f tweaks: rename a parameter and a variable, and reword two comments
Also reshuffle a declaration.
2020-02-12 16:05:18 +01:00
Benno Schulenberg
070ccf4a5b tweaks: rename two variables, and reshuffle a few things 2020-02-12 14:28:07 +01:00
Benno Schulenberg
8e4b68917c tweaks: rename a function, and condense a few comments 2020-02-12 14:15:35 +01:00
Benno Schulenberg
0d0d011579 tweaks: combine two ifs into one 2020-02-12 12:09:12 +01:00
Benno Schulenberg
71daa1ef6b tweaks: update several comments after the previous changes 2020-02-12 12:05:52 +01:00
Benno Schulenberg
0ed62e84de tweaks: weld two fragments together, twice, by eliding an unneeded 'if'
The get_kbinput() routine will never return ERR, so there is no need
to check for that.
2020-02-12 11:46:15 +01:00
Benno Schulenberg
0a31a9aa38 tweaks: make two conditions more direct, and thus elide two functions
Using straightforward comparisons is clearer and faster and shorter.

Again, note that this does not filter out 0x7F (DEL).  But that is
okay, as that code will never be returned from get_kbinput().
2020-02-12 11:38:33 +01:00
Benno Schulenberg
6747142cd7 tweaks: remove some redundant filtering, and thus elide a parameter
The few calls of the injection routines that had 'filtering' set to
TRUE have already filtered out ASCII control codes earlier on.  And
in the case of injecting a completion, this completion only contains
word-forming characters -- and if the user somehow added a control
code to the word-forming characters, then nano should NOT filter it
out, so in fact that setting of 'filtering' to TRUE was mistaken.

Note that this filtering did not filter out 0x7F (DEL).  But that is
fine: it should not occur in the input stream at that point anyway,
as it gets translated to either KEY_DC or KEY_BACKSPACE earlier in
the keyboard parsing routines.
2020-02-12 11:12:34 +01:00
Benno Schulenberg
6cf442e329 docs: mark bracketed pasting as done in the TODO list 2020-02-11 16:59:15 +01:00
Benno Schulenberg
d26f16a175 tweaks: correct two typos in a changelog, and drop a doubled word 2020-02-10 19:04:42 +01:00
Benno Schulenberg
8d7f8cda8c tweaks: improve two comments 2020-02-10 17:13:12 +01:00
Benno Schulenberg
75a70d98d5 tweaks: reshuffle two declarations plus a fragment of code
Also, don't bother statting the path that the user provided,
as that case will happen right away in the next 'if'.
2020-02-10 17:12:53 +01:00
Benno Schulenberg
b3374ea1fb tweaks: rename two parameters, to not overlap with other names 2020-02-10 16:11:07 +01:00
Benno Schulenberg
0905c6ae7e files: be consistent in which code means "New File"
This fixes https://savannah.gnu.org/bugs/?57782.

Bug existed since commit dd429d9c from yesterday.
2020-02-10 15:57:40 +01:00
Benno Schulenberg
3eeedd7caf tweaks: move a function to before the one that calls it 2020-02-10 09:55:21 +01:00
Benno Schulenberg
410dcee0a1 tweaks: move some definitions closer to where they are used
Also slightly improve a few comments.
2020-02-10 09:53:10 +01:00
Benno Schulenberg
5b4d2350e6 tweaks: condense two fragments of code 2020-02-10 09:40:12 +01:00
Benno Schulenberg
cbeea859c2 shutdown: don't refer to an open file when there aren't any
This fixes https://savannah.gnu.org/bugs/?57780.

Bug existed since commit b63c90bf from yesterday.
2020-02-10 09:28:37 +01:00
Benno Schulenberg
422cd726fa tweaks: normalize the indentation after the previous change 2020-02-09 20:17:05 +01:00
Benno Schulenberg
9f43b4c758 tweaks: update some comments after the previous changes 2020-02-09 20:10:28 +01:00
Benno Schulenberg
dd429d9c00 tweaks: rename a variable, reshuffle an assignment, and change a code 2020-02-09 19:50:33 +01:00
Benno Schulenberg
b63c90bf6b locking: do not open an empty buffer when respecting the first lock file
When opening multiple files, and the first of them has a lock file,
and the user chooses to not open the corresponding file, then nano
should NOT create an empty buffer in its stead.

This fixes https://savannah.gnu.org/bugs/?57777.

Bug existed since lock files were introduced, in version 2.4.0,
specifically since commit 6948d2e7.
2020-02-09 17:35:00 +01:00
Benno Schulenberg
fa26889d03 tweaks: change a function to return the name of the lock file on success
This makes it possible to fix a small bug in the next commit.
2020-02-09 17:01:38 +01:00
Benno Schulenberg
30011b8525 tweaks: elide one variable and three gotos
Returning the relevant value directly is clearer.
2020-02-09 16:35:18 +01:00
Benno Schulenberg
3ac5432e29 tweaks: reshuffle a few declarations and assignments
Also reshuffle two fragments of code.
2020-02-09 14:54:15 +01:00
Benno Schulenberg
1d52548575 tweaks: change another function with two possible results to boolean 2020-02-09 14:30:57 +01:00
Benno Schulenberg
d5ce44820a tweaks: change a function with two possible results to boolean 2020-02-09 14:24:26 +01:00
Benno Schulenberg
cc2b4f712c tweaks: unabbreviate the name of a variable 2020-02-09 12:02:37 +01:00
Benno Schulenberg
98f038ad96 rcfile: allow alternate line endings in nanorc files
When copy-pasting has resulted in a nanorc file with DOS line endings
(CR+LF), then silently ignore the carriage return, to avoid printing
an error message that partly overwrites itself.

This fulfills https://savannah.gnu.org/bugs/?57756.
Requested-by: Matthias Aßhauer <mha1993@live.de>
2020-02-09 11:55:07 +01:00
69 changed files with 23798 additions and 23653 deletions

221
ChangeLog
View File

@@ -1,3 +1,224 @@
Changes between v4.8 and v4.9:
------------------------------
Benno Schulenberg (209):
bindings: remove the translation of ^H to Backspace on the BSDs
build: fix compilation for --enable-tiny --enable-justify
build: restore non-UTF8 fallbacks, to allow compiling with --disable-utf8
build: update the conditional placement of the "Go To Line" menu item
bump version numbers and add a news item for the 4.9 release
chars: optimize a function for the most common blanks: space and tab
copyright: update to the current year for significantly changed files
cutting: let M-T cut a trailing empty line, but not nothing at all
cutting: with --cutfromcursor, allow ^K to cut the penultimate empty line
display: do not show a "[" double-width placeholder when softwrapping
display: keep the help items aligned, by not writing too many characters
docs: improve the descriptions of --softwrap and 'set softwrap'
docs: mark bracketed pasting as done in the TODO list
docs: mention that ^[ (Esc) is unbindable, and explain why
docs: remove the note saying that nanorc files must be in Unix format
docs: trim some TODO items, and condense several others
docs: update the form of an option, --suspendable / 'set suspendable'
docs: update the missed occurrences of --suspendable / 'set suspendable'
feedback: give a clearer message when trying to justify an empty region
feedback: make sure that a later message can overwrite a short warning
files: be consistent in which code means "New File"
files: don't check uninitialized memory when writing new file [valgrind]
files: when piping, always pipe whatever was cut to the external command
gnulib: update to its current upstream state
help: do not break a line inside the 17-column keystrokes area
help: increase the minimum help-text width from 32 to 40 columns
input: accommodate silly emulators that have LF instead of CR in a paste
input: after reallocating a string, do not write to its old address
input: allocate sufficient bytes for entering a Unicode codepoint
input: keep a multibyte character together during verbatim entry
justify: do not copy too many bytes when trimming leading whitespace
justify: do not crash when the user attempts to justify an empty region
justify: do not take an empty line as template for first-line indentation
justify: give the first line of a marked region its proper indentation
justify: never break a line in leading whitespace
justify: restore a region properly when it was marked backwards
justify: skip over blanks after the region, to not skew the indentation
justify: skip over in-line whitespace only, not over leading whitespace
justify: trim prefixed whitespace when justifying a marked region
justify: trim the leading blanks of a marked region at the right moment
justify: when appropriate, move end point of marked region forward
justify: when the cursor is at the left edge, keep it there
locking: do not open an empty buffer when respecting the first lock file
moving: do not put the cursor at end-of-line when in a help text
options: rename --suspend to --suspendable, to make more sense
prompt: insert a burst of bytes in one go instead of characterwise
rcfile: allow alternate line endings in nanorc files
rcfile: do not allow a regex for name, header, or magic to be empty
rcfile: don't store a coloring rule before it is complete
rcfile: rename bindable function 'suspendenable' to 'suspendable'
rcfile: when a start= is not matched with an end=, abandon the whole rule
rcfile: when finding a mistake, skip the rest of the line
shutdown: don't refer to an open file when there aren't any
softwrap: when typing goes beyond the bottom row, scroll just one row
syntax: nanorc: colorize 'rawsequences', not the obsolete 'rebindkeypad'
syntax: spec: add two missing % signs, and colorize also "%triggerprein"
syntax: spec: colorize the date and author of changelog items differently
syntax: spec: drop invalid parentheses after "Summary"
tweaks: abort in three situations that should never occur
tweaks: add a COUPLE_END undo item a bit later, instead of updating it
tweaks: add a different helping variable
tweaks: add a supporting variable, in order to condense some statements
tweaks: add calls of die() for five theoretical programming mistakes
tweaks: adjust some whitespace, reshuffle two ifs, and remove two braces
tweaks: adjust the indentation after the previous change
tweaks: always determine the second lead, to simplify the rewrap call
tweaks: avoid a complaint about uninitialized memory [valgrind]
tweaks: call add_undo() before the character is actually added
tweaks: change a function to return the name of the lock file on success
tweaks: change a function with two possible results to boolean
tweaks: change an integer to a short, and reshuffle it for better packing
tweaks: change another function with two possible results to boolean
tweaks: check for a starting quote in one place instead of three
tweaks: combine two ifs into one
tweaks: condense a comment, reshuffle conditions, and remove unwanted one
tweaks: condense a fragment of code
tweaks: condense two fragments of code
tweaks: convert integers to bytes in one place instead of two
tweaks: copy and store a deleted character in a conciser manner
tweaks: correct a typo, improve two indentations, and rewrap a line
tweaks: correct two typos in a changelog, and drop a doubled word
tweaks: create an undo item earlier, and discard it when needed
tweaks: don't bother reallocating the line data when undoing a line join
tweaks: do some text alignments properly: with spaces, not tabs
tweaks: drop a check for something that will not occur
tweaks: drop two comments that contain variable names
tweaks: elide a function call, by copying a byte directly
tweaks: elide an intermediate copy of a character during injection, twice
tweaks: elide an intermediate copy of an added character
tweaks: elide a parameter, and rename a variable
tweaks: elide a supporting variable, to make four loops slightly faster
tweaks: elide a variable and an unneeded iteration
tweaks: elide one variable and three gotos
tweaks: elide three unneeded #defines
tweaks: elide two variables and their two assignments
tweaks: exclude a function when compiled without spell-checking support
tweaks: extend the undo data for deleting and backspacing more directly
tweaks: factor out a three-line condition into a separate function
tweaks: frob a statement, rewrap two lines, and remove a pair of braces
tweaks: frob two statements, condense another, and add a comment
tweaks: highlight keystrokes in the documentation more consistently
tweaks: improve four comments, and condense two fragments of code
tweaks: improve three comments, and reshuffle two declarations
tweaks: improve two comments
tweaks: improve two comments, and remove an unneeded one
tweaks: inject the entire burst of bytes at once into the edit buffer
tweaks: instead of swapping the end points later, assign them correctly
tweaks: invert the logic of a symbol, to make more sense
tweaks: make prompt-bar input more similar to edit-buffer input
tweaks: make two conditions more direct, and thus elide two functions
tweaks: mesh two bits of code together
tweaks: move a function to after the one that it calls
tweaks: move a function to before the one that calls it
tweaks: move a function to before the one that calls it
tweaks: move a function to its proper place in the order of things
tweaks: move a function to where it is used
tweaks: move another function to where it is used
tweaks: move some definitions closer to where they are used
tweaks: move two functions to before the ones that call them
tweaks: normalize a translator hint, update one, and add another
tweaks: normalize the indentation after the previous change
tweaks: normalize the indentation after the previous change
tweaks: normalize the indentation after the previous two changes
tweaks: pull the NUL-terminating of a string into a function
tweaks: rearrange a case item, so that PASTE is always after CUT
tweaks: rearrange some conditions, for compactness
tweaks: relocate a function to before its callers
tweaks: relocate eleven functions to before they are called
tweaks: remove a now-unused helper function
tweaks: remove an unneeded indirection
tweaks: remove a redundant assignment
tweaks: remove a redundant statement, a spurious reference to 'cutbottom'
tweaks: remove a superfluous assignment, and reshuffle a call
tweaks: remove non-UTF-8 code from three more functions
tweaks: remove some redundant filtering, and thus elide a parameter
tweaks: remove some unneeded braces, not used elsewhere either
tweaks: remove two redundant case labels
tweaks: remove two superfluous checks
tweaks: remove two superfluous conditions
tweaks: rename a constant, and rename and relocate a function
tweaks: rename a function, and condense a few comments
tweaks: rename a function, and split a variable into two separate ones
tweaks: rename a function, to be more fitting, and improve some comments
tweaks: rename a function, to remove an obscuring abbreviation
tweaks: rename another function, to remove the obscuring abbreviation
tweaks: rename a parameter and a variable, and reword two comments
tweaks: rename a symbol, to match the corresponding renamed option
tweaks: rename a variable, and add a helping one
tweaks: rename a variable, and reshuffle a declaration
tweaks: rename a variable, away from a single letter
tweaks: rename a variable, away from a single letter
tweaks: rename a variable, for aptness
tweaks: rename a variable, reshuffle an assignment, and change a code
tweaks: rename a variable, to be a bit clearer
tweaks: rename a variable, to get out of the way of a later rename
tweaks: rename a variable, to not be a substring of a function name
tweaks: rename four more functions, to get rid of an abbreviation
tweaks: rename four parameters, to be more distinct and telling
tweaks: rename four variables, to be a bit more telling
tweaks: rename to the same name two variables that have the same role
tweaks: rename two elements of an undo record, to be more telling
tweaks: rename two functions, for shortness
tweaks: rename two functions, to match the style of others
tweaks: rename two more elements of an undo record, for symmetry
tweaks: rename two more functions, to match the style of others
tweaks: rename two more variables, to harmonize with two others
tweaks: rename two parameters, to not overlap with other names
tweaks: rename two symbols, to be more precise
tweaks: rename two variables, and frob four comments
tweaks: rename two variables, and reshuffle a few things
tweaks: rename two variables, for distinctiveness
tweaks: rename two variables, to be different or for more contrast
tweaks: rename two variables, to harmonize with two others
tweaks: rename two variables, to suit both the marked and unmarked case
tweaks: reorder a case item, to have SPLIT_BEGIN always before SPLIT_END
tweaks: reshuffle a condition, for compacter code
tweaks: reshuffle a condition, for compactness
tweaks: reshuffle a few declarations and assignments
tweaks: reshuffle an assignment and a free()
tweaks: reshuffle some code, in preparation for improving it
tweaks: reshuffle some stuff, to have related things together
tweaks: reshuffle the setting of the starting point of a cut
tweaks: reshuffle the trimming of leading whitespace, for compactness
tweaks: reshuffle two declarations plus a fragment of code
tweaks: simplify the undoing and redoing of an <Enter>
tweaks: strip a parameter that is equivalent to 'openfile' for both calls
tweaks: strip a parameter that is TRUE for both calls
tweaks: swap the use of 'head' and 'tail' for CUT and PASTE undo items
tweaks: trim an unnecessary detail from an error message
tweaks: unabbreviate the name of a variable
tweaks: unwrap four lines, and use explicit codes where possible
tweaks: update several comments after the previous changes
tweaks: update some comments after the previous changes
tweaks: use a symbol instead of a number, and drop two unneeded casts
tweaks: use spaces when aligning things, not tabs
tweaks: use the variable that suits 'END' better
tweaks: weld two fragments together, twice, by eliding an unneeded 'if'
tweaks: when appropriate, move starting point of justified region back
tweaks: when extending the marked region, include also exotic blanks
tweaks: when undoing an addition or redoing a deletion, do not reallocate
undo: do not mark the buffer as modified when pasting back nothing
undo: do not try to copy a cutbuffer that is NULL
undo: do not try to paste back an empty cutbuffer
undo: for an automatic hard-wrap, store the correct previous buffer size
undo: store the cursor row, for redoing filtering & justification better
undo: treat a cut-until-end-of-buffer like a backward marked region
undo: use the correct original fusion point when unjoining two lines
undo: when undoing a paste or an insertion, remove an added magic line
usage: improve the description of --softwrap
Neal Gompa (1):
syntax: spec: add some keywords that were added in RPM 4.15 and 4.13
Saagar Jha (1):
rcfile: fix an out-of-bounds read on empty lines
Changes between v4.7 and v4.8:
------------------------------

View File

@@ -3448,14 +3448,14 @@ GNU nano 2.1.8 - 2009.02.07
2009-01-25 Chris Allegretta <chrisa@asty.org>
* files.c (open_file), nanorc.c (parse_include): Don't get_full_path on included
rc files, due to it potentially impacting the ability to read files in nano's
cwd(). Fixes Savnanah bug #25297 reported by Mike Frysinger.
cwd(). Fixes Savannah bug #25297 reported by Mike Frysinger.
2009-01-24 Chris Allegretta <chrisa@asty.org>
* First pass at some caching of caching color info. Right now it's only for
* First pass at some caching of color info. Right now it's only for
multi-line regexes but this may not be enough to increase performance.
* Add interruptability to search functions. New functions enable_nodelay and
disable_nodelay and changes to the routines to handle checking for pending
searches. Fixes Savnnah bug #24946: Need interrrupt for search.
searches. Fixes Savannah bug #24946: Need interrrupt for search.
2009-01-19 Chris Allegretta <chrisa@asty.org>
* Change function definitions to shorts instead of (void *)s. New mapping function

11
NEWS
View File

@@ -1,3 +1,14 @@
2020.03.24 - GNU nano 4.9 "die fetten Jahre sind vorbei"
• When justifying a selection, the new paragraph and the
succeeding one get the appropriate first-line indent.
• Trying to justify an empty selection does not crash.
• Redoing the insertion of an empty file does not crash.
• On the BSDs and macOS, ^H has become rebindable again
(in most terminal emulators, not on the console).
• DOS line endings in nanorc files are accepted.
• Option --suspend / 'set suspend' has been renamed to
the more logical --suspendable / 'set suspendable'.
2020.02.07 - GNU nano 4.8 "Jaška"
• When something is pasted into nano, auto-indentation is suppressed,
and the paste can be undone as a whole with a single M-U.

44
TODO
View File

@@ -1,20 +1,20 @@
A list of desired features
--------------------------
Somewhat urgent:
- Detect when text is being pasted, so that we can handle it faster.
See https://savannah.gnu.org/bugs/?40060.
- Allow color syntaxes to apply to more than just color, so that we can
* Allow color syntaxes to apply to more than just color, so that we can
e.g. specify a different alternate spell checker depending on which
file type we have open.
Vague musings:
- Add support for right-to-left text (RTL), through FriBidi?
- Allow setting marks (saved positions, not to be confused with the
* Add support for right-to-left text (RTL).
* Allow setting bookmarks (saved positions, not to be confused with the
mark set via Ctrl-^) at various lines and/or columns in the buffer,
and allow movement between them with a single keystroke?
- Make matching bracket searches sophisticated enough to skip over
brackets inside comments?
See https://savannah.gnu.org/bugs/?57577.
For version 4.8:
- Detect when text is being pasted, so that we can handle it faster. [DONE]
For version 4.0:
- Make undo/redo work also for justifying. [DONE]
@@ -31,8 +31,7 @@ For version 2.8:
For version 2.4:
- Handle window resizes better. After we've resized, we should stay
wherever we were before we resized, as Pico does. [DONE]
- Allow even better file-type detection than we have currently, e.g.
through libmagic. [DONE]
- Allow even better file-type detection, e.g. through libmagic. [DONE]
- Compatibility with vim status files to let other editors know we're
in a file. [DONE]
@@ -49,36 +48,31 @@ For version 2.0:
- Support for paragraph searches. [DONE]
- Support for justifying the entire file at once. [DONE]
- Support for filename searches in the file browser. [DONE]
- Keystroke to implement "Add next sequence as raw" like vi's ^V. [DONE]
- A keystroke to implement "Add next sequence as raw", like vi's ^V. [DONE]
- Spell check selected text only. [DONE]
- Make "To Line" (^W^T) and "Read from Command" (^R^X) reenter their
parent menu when their keystroke is entered a second time (^W^T^T and
(^R^X^X) (requires figuring out when to keep cursor position and when
not to). [DONE]
- Fix resetstatuspos global which we shouldn't have. [DONE]
- Make "To Line" (^W^T) and "Read from Command" (^R^X) return to their parent
menu when their keystroke is entered again (^W^T^T and ^R^X^X). [DONE]
For version 1.2:
- Single-line scroll up/down? [DONE]
- Color syntax highlighting? (There seems to be a demand for it.) [DONE]
- Color syntax highlighting? [DONE]
- Support for a .nanorc file. [DONE]
- Backup making (filename~)? [DONE]
- Search/replace string history. [DONE]
- Implement Pico's -j and -g flags, as they are pretty easy to do. [DONE]
- Make mouse support work with clicking on the shortcuts (-m). [DONE]
- Implement Pico's -j and -g flags. [DONE]
- Make mouse support (-m) work with clicking on the shortcuts. [DONE]
- Implement -o (--operatingdir, a chroot of sorts). [DONE]
- Allow -r to take a negative argument, meaning right margin instead of
left (allows resizing that way), formerly -W arg. [DONE]
- Allow -r to take a negative argument. [DONE]
For version 1.0:
- Implement Spelling. [DONE]
- Implement Help. [DONE]
- Internationalization [In progress, translators welcome!]
- Allow nano to be resized in X. [DONE]
- On PageUp/Down, put the cursor on the first line (like Pico), not the
center line. [DONE]
- On PageUp/Down, put the cursor on the first screen line (like Pico). [DONE]
- Implement justify function. [DONE]
- Cut to end of line. [DONE]
- Built-in speller command. [DONE]
- Better statusbar interaction (scrolling, tab completion). [DONE]
- Better status-bar interaction (scrolling, tab completion). [DONE]
- Unjustify command (^U after ^J). [DONE]
- Username completion (~user). [DONE]

View File

@@ -2,7 +2,7 @@
# Generate configure & friends for GIT users.
gnulib_url="git://git.sv.gnu.org/gnulib.git"
gnulib_hash="e313a5367b1e4edfa19ab5f0d001d0b060a77066"
gnulib_hash="360979fd6fb84567e0ffc6839b0431fc00d6362f"
modules="
futimens

View File

@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
AC_INIT([GNU nano], [4.8], [nano-devel@gnu.org], [nano])
AC_INIT([GNU nano], [4.9], [nano-devel@gnu.org], [nano])
AC_CONFIG_SRCDIR([src/nano.c])
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE([1.14])

View File

@@ -89,7 +89,7 @@
<h3><a name="1.3"></a>1.3. Why the name change from TIP?</h3>
<blockquote><p>On January 10, 2000, TIP was officially renamed to nano because of a namespace conflict with another program called 'tip'. The original 'tip' program &quot;establishes a full duplex terminal connection to a remote host&quot;, and was included with many older Unix systems (and newer ones like Solaris). The conflict was not noticed at first because there is no 'tip' utility included with most GNU/Linux distributions (where nano was developed).</p></blockquote>
<h3><a name="1.4"></a>1.4. What is the current version of nano?</h3>
<blockquote><p>The current version of nano <i>should</i> be <b>4.8</b>. Of course, you should always check the <a href="https://nano-editor.org/">nano homepage</a> to see what the latest and greatest version is.</p></blockquote>
<blockquote><p>The current version of nano <i>should</i> be <b>4.9</b>. Of course, you should always check the <a href="https://nano-editor.org/">nano homepage</a> to see what the latest and greatest version is.</p></blockquote>
<h3><a name="1.5"></a>1.5. I want to read the man page without having to download the program!</h3>
<blockquote><p>Jeez, demanding, aren't we? Okay, look <a href="https://nano-editor.org/dist/latest/nano.1.html">here</a>.</p></blockquote>
<hr width="100%">

View File

@@ -16,7 +16,7 @@
.\" Documentation License along with this program. If not, see
.\" <https://www.gnu.org/licenses/>.
.\"
.TH NANO 1 "version 4.8" "February 2020"
.TH NANO 1 "version 4.9" "March 2020"
.SH NAME
nano \- Nano's ANOther editor, inspired by Pico
@@ -281,8 +281,8 @@ Set the operating directory. This makes \fBnano\fP set up something
similar to a chroot.
.TP
.BR \-p ", " \-\-preserve
Preserve the XON and XOFF sequences (^Q and ^S) so they will be caught
by the terminal.
Preserve the XON and XOFF sequences (\fB^Q\fR and \fB^S\fR) so they
will be caught by the terminal.
.TP
.BR \-r\ \fInumber ", " \-\-fill= \fInumber
Set the target width for justifying and automatic hard-wrapping at this
@@ -320,13 +320,14 @@ Don't show the two help lines at the bottom of the screen.
.BR \-y ", " \-\-afterends
Make Ctrl+Right stop at word ends instead of beginnings.
.TP
.BR \-z ", " \-\-suspend
Enable the suspend ability.
.BR \-z ", " \-\-suspendable
Allow the user to suspend the editor (with \fB^Z\fR by default).
.TP
.BR \-$ ", " \-\-softwrap
Enable 'soft wrapping'. This will make \fBnano\fP attempt to display the
entire contents of any line, even if it is longer than the screen width, by
continuing it over multiple screen lines. Since
Display lines that exceed the screen's width over multiple screen lines.
(You can make this soft-wrapping occur at whitespace instead of rudely at
the screen's edge, by using also \fB\-\-atblanks\fR.)
Since
\&'$' normally refers to a variable in the Unix shell, you should specify
this option last when using other options (e.g.\& 'nano \-wS$') or pass it
separately (e.g.\& 'nano \-wS \-$').

View File

@@ -8,8 +8,8 @@
@smallbook
@set EDITION 0.5
@set VERSION 4.8
@set UPDATED February 2020
@set VERSION 4.9
@set UPDATED March 2020
@dircategory Editors
@direntry
@@ -23,7 +23,7 @@
@titlepage
@title GNU @command{nano}
@subtitle a small and friendly text editor
@subtitle version 4.8
@subtitle version 4.9
@author Chris Allegretta
@page
@@ -63,7 +63,7 @@ e-mail: @email{chrisa@@asty.org}@*
@node Top
@top
This manual documents the GNU @command{nano} editor, version 4.8.
This manual documents the GNU @command{nano} editor, version 4.9.
@menu
* Introduction::
@@ -456,15 +456,16 @@ disabled to display the help-system navigation keys.
Make Ctrl+Right stop at word ends instead of beginnings.
@item -z
@itemx --suspend
@itemx --suspendable
Enable the ability to suspend @command{nano} using the system's suspend
keystroke (usually @kbd{^Z}).
@item -$
@itemx --softwrap
Enable 'soft wrapping'. This will make @command{nano} attempt to display the
entire contents of any line, even if it is longer than the screen width, by
continuing it over multiple screen lines. Since
Display lines that exceed the screen's width over multiple screen lines.
(You can make this soft-wrapping occur at whitespace instead of rudely at
the screen's edge, by using also @code{--atblanks}.)
Since
@code{$} normally refers to a variable in the Unix shell, you should specify
this option last when using other options (e.g.@: @code{nano -wS$}) or pass it
separately (e.g.@: @code{nano -wS -$}).
@@ -682,7 +683,7 @@ The following global toggles are available:
--- @pxref{Syntax Highlighting}).
@item Suspension
@kbd{Meta-Z} toggles the @option{-z} (@option{--suspend}) command-line option.
@kbd{Meta-Z} toggles the @option{-z} (@option{--suspendable}) command-line option.
@end table
@@ -690,9 +691,8 @@ The following global toggles are available:
@node Nanorc Files
@chapter Nanorc Files
The nanorc files contain the default settings for @command{nano}. They
should be in Unix format, not in DOS or Mac format. During startup,
if @option{--rcfile} is not given, @command{nano} will read two files:
The nanorc files contain the default settings for @command{nano}. During
startup, if @option{--rcfile} is not given, @command{nano} will read two files:
first the system-wide settings, from @file{/etc/nanorc} (the exact path
might be different on your system), and then the user-specific settings,
either from @file{~/.nanorc} or from @file{$XDG_CONFIG_HOME/nano/nanorc}
@@ -935,7 +935,9 @@ Deprecated option since it has become the default setting.
When needed, use @code{unset jumpyscrolling} instead.
@item set softwrap
Enable soft line wrapping for easier viewing of very long lines.
Display lines that exceed the screen's width over multiple screen lines.
(You can make this soft-wrapping occur at whitespace instead of rudely at
the screen's edge, by using also @code{set atblanks}.)
@item set speller "@var{program} [@var{argument} @dots{}]"
Use the given program to do spell checking and correcting.
@@ -949,8 +951,8 @@ Use this color combination for the status bar.
Use this color combination for the vertical guiding stripe.
@xref{@code{set functioncolor}} for valid color names.
@item set suspend
Allow @command{nano} to be suspended.
@item set suspendable
Allow @command{nano} to be suspended (with @kbd{^Z} by default).
@item set tabsize @var{number}
Use a tab size of @var{number} columns. The value of @var{number} must be
@@ -1186,6 +1188,8 @@ with Shift.)
@end itemize
Rebinding @code{^M} (Enter) or @code{^I} (Tab) is probably not a good idea.
Rebinding @code{^[} (Esc) is not possible, because its keycode
is the starter byte of escape sequences and Meta keystrokes.
On some terminals it's not possible to rebind @code{^H} (unless @code{--raw}
is used) because its keycode is identical to that of the Backspace key.
@@ -1248,7 +1252,7 @@ Throws away the current line (or the marked region).
Deletes from the cursor position to the beginning of the preceding word.
(This function is bound by default to <Shift+Ctrl+Delete>. If your terminal
produces @code{^H} for <Ctrl+Backspace>, you can make <Ctrl+Backspace> delete
the word to the left of the cursor by rebinding ^H to this function.)
the word to the left of the cursor by rebinding @kbd{^H} to this function.)
@item chopwordright
Deletes from the cursor position to the beginning of the next word.
@@ -1405,7 +1409,7 @@ Refreshes the screen.
@item suspend
Suspends the editor (if the suspending function is enabled, see the
"suspendenable" entry below).
@code{suspendable} toggle item below).
@item casesens
Toggles whether searching/replacing ignores or respects the case of
@@ -1516,8 +1520,10 @@ Toggles whether typed tabs will be converted to spaces.
@item mouse
Toggles mouse support.
@item suspendenable
Toggles whether the suspend shortcut (normally @kbd{^Z}) will suspend the editor.
@item suspendable
Toggles whether the suspend keystroke (@kbd{^Z} by default)
will actually suspend the editor.
(The old name of this function, 'suspendenable', is deprecated.)
@end table

View File

@@ -16,16 +16,15 @@
.\" Documentation License along with this program. If not, see
.\" <https://www.gnu.org/licenses/>.
.\"
.TH NANORC 5 "version 4.8" "February 2020"
.TH NANORC 5 "version 4.9" "March 2020"
.SH NAME
nanorc \- GNU nano's configuration file
.SH DESCRIPTION
The \fInanorc\fP files contain the default settings for \fBnano\fP, a
small and friendly editor. They should be in Unix format, not in
DOS or Mac format. During startup, if \fB\-\-rcfile\fR is not given,
\fBnano\fR will read two files: first the
The \fInanorc\fP files contain the default settings for \fBnano\fP,
a small and friendly editor. During startup, if \fB\-\-rcfile\fR
is not given, \fBnano\fR will read two files: first the
system-wide settings, from \fI/etc/nanorc\fP (the exact path might be
different on your system), and then the user-specific settings, either
from \fI~/.nanorc\fR or from \fI$XDG_CONFIG_HOME/nano/nanorc\fR
@@ -180,7 +179,7 @@ System, and on the console when gpm is running. Text can still be
selected through dragging by holding down the Shift key.
.TP
.B set multibuffer
When reading in a file with ^R, insert it into a new buffer by default.
When reading in a file with \fB^R\fR, insert it into a new buffer by default.
.TP
.B set noconvert
Don't convert files from DOS/Mac format.
@@ -214,7 +213,7 @@ Save the cursor position of files between editing sessions.
The cursor position is remembered for the 200 most-recently edited files.
.TP
.B set preserve
Preserve the XON and XOFF keys (^Q and ^S).
Preserve the XON and XOFF keys (\fB^Q\fR and \fB^S\fR).
.TP
.BI "set punct """ characters """
Set the characters treated as closing punctuation when justifying
@@ -268,7 +267,9 @@ Deprecated option since it has become the default setting.
When needed, use \fBunset jumpyscrolling\fR instead.
.TP
.B set softwrap
Enable soft line wrapping for easier viewing of very long lines.
Display lines that exceed the screen's width over multiple screen lines.
(You can make this soft-wrapping occur at whitespace instead of rudely at
the screen's edge, by using also \fBset atblanks\fR.)
.TP
.B set speller """\fIprogram\fR [\fIargument \fR...]\fB"""
Use the given \fIprogram\fR to do spell checking and correcting, instead of
@@ -282,8 +283,8 @@ See \fBset titlecolor\fR for more details.
Specify the color combination to use for the vertical guiding stripe.
See \fBset titlecolor\fR for more details.
.TP
.B set suspend
Allow \fBnano\fP to be suspended.
.B set suspendable
Allow \fBnano\fP to be suspended (with \fB^Z\fR by default).
.TP
.B set tabsize \fInumber\fR
Use a tab size of \fInumber\fR columns. The value of \fInumber\fP must be
@@ -509,6 +510,8 @@ Example: F10.
.RE
Rebinding \fB^M\fR (Enter) or \fB^I\fR (Tab) is probably not a good idea.
Rebinding \fB^[\fR (Esc) is not possible, because its keycode
is the starter byte of escape sequences and Meta keystrokes.
On some terminals it's not possible to rebind \fB^H\fR (unless \fB\-\-raw\fR
is used) because its keycode is identical to that of the Backspace key.
@@ -570,7 +573,7 @@ Throws away the current line (or the marked region).
Deletes from the cursor position to the beginning of the preceding word.
(This function is bound by default to <Shift+Ctrl+Delete>. If your terminal
produces \fB^H\fR for <Ctrl+Backspace>, you can make <Ctrl+Backspace> delete
the word to the left of the cursor by rebinding ^H to this function.)
the word to the left of the cursor by rebinding \fB^H\fR to this function.)
.TP
.B chopwordright
Deletes from the cursor position to the beginning of the next word.
@@ -726,7 +729,7 @@ Refreshes the screen.
.TP
.B suspend
Suspends the editor (if the suspending function is enabled, see the
"suspendenable" entry below).
\fBsuspendable\fR toggle item below).
.TP
.B casesens
Toggles whether searching/replacing ignores or respects the case of
@@ -837,8 +840,10 @@ Toggles whether typed tabs will be converted to spaces.
.B mouse
Toggles mouse support.
.TP
.B suspendenable
Toggles whether the suspend sequence (normally ^Z) will suspend the editor window.
.B suspendable
Toggles whether the suspend keystroke (\fB^Z\fR by default)
will actually suspend the editor.
(The old name of this function, 'suspendenable', is deprecated.)
.RE
.TP

View File

@@ -16,7 +16,7 @@
.\" Documentation License along with this program. If not, see
.\" <https://www.gnu.org/licenses/>.
.\"
.TH RNANO 1 "version 4.8" "February 2020"
.TH RNANO 1 "version 4.9" "March 2020"
.SH NAME
rnano \- a restricted nano

View File

@@ -1,15 +1,12 @@
## Sample initialization file for GNU nano.
##
## This file should not be in DOS or Mac format, and characters
## specially interpreted by the shell should not be escaped here.
##
## To make sure an option is disabled, use "unset <option>".
##
## For the options that take parameters, the default value is given.
## Other options are unset by default.
##
## Inside string parameters, quotes should not be escaped. The last
## Characters that are special in a shell should not be escaped here.
## Inside string parameters, quotes should not be escaped -- the last
## double quote on the line will be seen as the closing quote.
##
## For the options that take parameters, the default value is shown.
## Other options are unset by default. To make sure that an option
## is disabled, you can use "unset <option>".
## Make the 'nextword' function (Ctrl+Right) stop at word ends
## instead of at beginnings.
@@ -145,15 +142,16 @@
## beginning of the line.
# set smarthome
## Enable soft line wrapping (AKA full-line display).
## Spread overlong lines over multiple screen lines.
# set softwrap
## Use this spelling checker instead of the internal one. This option
## does not have a default value.
# set speller "aspell -x -c"
## Allow nano to be suspended.
# set suspend
## Allow nano to be suspended (with ^Z by default).
# set suspendable
## (The old form of this option, 'set suspend', is deprecated.)
## Use this tab size instead of the default; it must be greater than 0.
# set tabsize 8

View File

@@ -1,21 +1,21 @@
%define name @PACKAGE@
%define version @VERSION@
%define release 1
%define name @PACKAGE@
%define version @VERSION@
%define release 1
Name : %{name}
Version : %{version}
Release : %{release}
Summary : a user-friendly editor, a Pico clone with enhancements
Name : %{name}
Version : %{version}
Release : %{release}
Summary : a user-friendly editor, a Pico clone with enhancements
License : GPLv3+
Group : Applications/Editors
URL : https://nano-editor.org/
Source : https://nano-editor.org/dist/latest/%{name}-%{version}.tar.gz
License : GPLv3+
Group : Applications/Editors
URL : https://nano-editor.org/
Source : https://nano-editor.org/dist/latest/%{name}-%{version}.tar.gz
BuildRoot : %{_tmppath}/%{name}-%{version}-root
BuildRequires : autoconf, automake, gettext-devel, ncurses-devel, texinfo
Requires(post) : info
Requires(preun) : info
BuildRoot : %{_tmppath}/%{name}-%{version}-root
BuildRequires : autoconf, automake, gettext-devel, ncurses-devel, texinfo
Requires(post) : info
Requires(preun) : info
%description
GNU nano is a small and friendly text editor. It aims to emulate the
@@ -47,7 +47,6 @@ fi
%{_bindir}/*
%{_docdir}/nano/*
%{_mandir}/man*/*
%{_mandir}/fr/man*/*
%{_infodir}/nano.info*
%{_datadir}/locale/*/LC_MESSAGES/nano.mo
%{_datadir}/nano/*

1188
po/bg.po

File diff suppressed because it is too large Load Diff

1198
po/ca.po

File diff suppressed because it is too large Load Diff

1190
po/cs.po

File diff suppressed because it is too large Load Diff

1190
po/da.po

File diff suppressed because it is too large Load Diff

1265
po/de.po

File diff suppressed because it is too large Load Diff

1192
po/eo.po

File diff suppressed because it is too large Load Diff

1223
po/es.po

File diff suppressed because it is too large Load Diff

1188
po/eu.po

File diff suppressed because it is too large Load Diff

1190
po/fi.po

File diff suppressed because it is too large Load Diff

1204
po/fr.po

File diff suppressed because it is too large Load Diff

1190
po/ga.po

File diff suppressed because it is too large Load Diff

1190
po/gl.po

File diff suppressed because it is too large Load Diff

1546
po/hr.po

File diff suppressed because it is too large Load Diff

1190
po/hu.po

File diff suppressed because it is too large Load Diff

1188
po/id.po

File diff suppressed because it is too large Load Diff

1191
po/it.po

File diff suppressed because it is too large Load Diff

1215
po/ja.po

File diff suppressed because it is too large Load Diff

1190
po/ko.po

File diff suppressed because it is too large Load Diff

1188
po/ms.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1190
po/nb.po

File diff suppressed because it is too large Load Diff

1199
po/nl.po

File diff suppressed because it is too large Load Diff

1186
po/nn.po

File diff suppressed because it is too large Load Diff

1190
po/pl.po

File diff suppressed because it is too large Load Diff

1194
po/pt.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1198
po/ro.po

File diff suppressed because it is too large Load Diff

1190
po/ru.po

File diff suppressed because it is too large Load Diff

1190
po/sl.po

File diff suppressed because it is too large Load Diff

1190
po/sr.po

File diff suppressed because it is too large Load Diff

1190
po/sv.po

File diff suppressed because it is too large Load Diff

1196
po/tr.po

File diff suppressed because it is too large Load Diff

1194
po/uk.po

File diff suppressed because it is too large Load Diff

1190
po/vi.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
#!/bin/bash
VERSION="4.8"
VERSION="4.9"
./configure -C --enable-tiny && make && ./configure -C &&

View File

@@ -183,9 +183,9 @@ char *do_browser(char *path)
} else if (func == do_right) {
if (selected < filelist_len - 1)
selected++;
} else if (func == do_prev_word_void) {
} else if (func == to_prev_word) {
selected -= (selected % width);
} else if (func == do_next_word_void) {
} else if (func == to_next_word) {
selected += width - 1 - (selected % width);
if (selected >= filelist_len)
selected = filelist_len - 1;
@@ -195,10 +195,10 @@ char *do_browser(char *path)
} else if (func == do_down) {
if (selected + width <= filelist_len - 1)
selected += width;
} else if (func == do_prev_block) {
} else if (func == to_prev_block) {
selected = ((selected / (editwinrows * width)) *
editwinrows * width) + selected % width;
} else if (func == do_next_block) {
} else if (func == to_next_block) {
selected = ((selected / (editwinrows * width)) *
editwinrows * width) + selected % width +
editwinrows * width - width;

View File

@@ -44,118 +44,91 @@ bool using_utf8(void)
}
#endif /* ENABLE_UTF8 */
/* Concatenate two allocated strings, and free the second. */
char *addstrings(char* str1, size_t len1, char* str2, size_t len2)
{
str1 = charealloc(str1, len1 + len2 + 1);
str1[len1] = '\0';
strncat(&str1[len1], str2, len2);
free(str2);
return str1;
}
/* Return TRUE if the value of c is in byte range, and FALSE otherwise. */
bool is_byte(int c)
{
return ((unsigned int)c == (unsigned char)c);
}
/* This function is equivalent to isalpha() for multibyte characters. */
bool is_alpha_mbchar(const char *c)
#ifdef ENABLE_SPELLER
/* Return TRUE when the given character is some kind of letter. */
bool is_alpha_char(const char *c)
{
#ifdef ENABLE_UTF8
if (use_utf8) {
wchar_t wc;
wchar_t wc;
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
return iswalpha(wc);
} else
return iswalpha(wc);
#else
return isalpha((unsigned char)*c);
#endif
return isalpha((unsigned char)*c);
}
#endif /* ENABLE_SPELLER */
/* This function is equivalent to isalnum() for multibyte characters. */
bool is_alnum_mbchar(const char *c)
/* Return TRUE when the given character is some kind of letter or a digit. */
bool is_alnum_char(const char *c)
{
#ifdef ENABLE_UTF8
if (use_utf8) {
wchar_t wc;
wchar_t wc;
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
return iswalnum(wc);
} else
return iswalnum(wc);
#else
return isalnum((unsigned char)*c);
#endif
return isalnum((unsigned char)*c);
}
/* This function is equivalent to isblank() for multibyte characters. */
bool is_blank_mbchar(const char *c)
/* Return TRUE when the given character is space or tab or other whitespace. */
bool is_blank_char(const char *c)
{
#ifdef ENABLE_UTF8
if (use_utf8) {
wchar_t wc;
wchar_t wc;
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
if ((signed char)*c >= 0)
return (*c == ' ' || *c == '\t');
return iswblank(wc);
} else
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
return iswblank(wc);
#else
return isblank((unsigned char)*c);
#endif
return isblank((unsigned char)*c);
}
/* This function is equivalent to iscntrl(), except in that it only
* handles non-high-bit control characters. */
bool is_ascii_cntrl_char(int c)
{
return (0 <= c && c < 32);
}
/* This function is equivalent to iscntrl() for multibyte characters,
* except in that it also handles multibyte control characters with
* their high bits set. */
bool is_cntrl_mbchar(const char *c)
/* Return TRUE when the given character is a control character. */
bool is_cntrl_char(const char *c)
{
#ifdef ENABLE_UTF8
if (use_utf8) {
return ((c[0] & 0xE0) == 0 || c[0] == 127 ||
return ((c[0] & 0xE0) == 0 || c[0] == DEL_CODE ||
((signed char)c[0] == -62 && (signed char)c[1] < -96));
} else
#endif
return (((unsigned char)*c & 0x60) == 0 || (unsigned char)*c == 127);
return ((*c & 0x60) == 0 || *c == DEL_CODE);
}
/* This function is equivalent to ispunct() for multibyte characters. */
bool is_punct_mbchar(const char *c)
/* Return TRUE when the given character is a punctuation character. */
bool is_punct_char(const char *c)
{
#ifdef ENABLE_UTF8
if (use_utf8) {
wchar_t wc;
wchar_t wc;
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
if (mbtowc(&wc, c, MAXCHARLEN) < 0)
return FALSE;
return iswpunct(wc);
} else
return iswpunct(wc);
#else
return ispunct((unsigned char)*c);
#endif
return ispunct((unsigned char)*c);
}
/* 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)
/* Return TRUE when the given character is word-forming (it is alphanumeric or
* specified in 'wordchars', or it is punctuation when allow_punct is TRUE). */
bool is_word_char(const char *c, bool allow_punct)
{
if (*c == '\0')
return FALSE;
if (is_alnum_mbchar(c))
if (is_alnum_char(c))
return TRUE;
if (word_chars != NULL && *word_chars != '\0') {
@@ -166,7 +139,7 @@ bool is_word_mbchar(const char *c, bool allow_punct)
return (strstr(word_chars, symbol) != NULL);
}
return (allow_punct && is_punct_mbchar(c));
return (allow_punct && is_punct_char(c));
}
/* Return the visible representation of control character c. */
@@ -229,11 +202,10 @@ int mbwidth(const char *c)
* allocated) multibyte character and a length of zero. */
char *make_mbchar(long code, int *length)
{
char *mb_char;
char *mb_char = charalloc(MAXCHARLEN);
#ifdef ENABLE_UTF8
if (use_utf8) {
mb_char = charalloc(MAXCHARLEN);
*length = wctomb(mb_char, (wchar_t)code);
/* Reject invalid Unicode characters. */
@@ -244,7 +216,7 @@ char *make_mbchar(long code, int *length)
} else
#endif
{
mb_char = measured_copy((char *)&code, 1);
*mb_char = (char)code;
*length = 1;
}
@@ -319,7 +291,7 @@ int advance_over(const char *string, size_t *column)
int charlen = mblen(string, MAXCHARLEN);
if (charlen > 0) {
if (is_cntrl_mbchar(string))
if (is_cntrl_char(string))
*column += 2;
else
*column += mbwidth(string);
@@ -612,7 +584,7 @@ bool has_blank_char(const char *string)
while (*string != '\0') {
string += collect_char(string, symbol);
if (is_blank_mbchar(symbol))
if (is_blank_char(symbol))
return TRUE;
}
@@ -623,7 +595,7 @@ bool has_blank_char(const char *string)
/* Return TRUE when the given string is empty or consists of only blanks. */
bool white_string(const char *string)
{
while (*string != '\0' && (is_blank_mbchar(string) || *string == '\r'))
while (*string != '\0' && (is_blank_char(string) || *string == '\r'))
string += char_length(string);
return !*string;

View File

@@ -40,13 +40,13 @@
/* Assign pair numbers for the colors in the given syntax, giving identical
* color pairs the same number. */
void set_syntax_colorpairs(syntaxtype *sint)
void set_syntax_colorpairs(syntaxtype *sntx)
{
int new_number = NUMBER_OF_ELEMENTS + 1;
colortype *ink;
for (ink = sint->color; ink != NULL; ink = ink->next) {
const colortype *beforenow = sint->color;
for (ink = sntx->color; ink != NULL; ink = ink->next) {
const colortype *beforenow = sntx->color;
while (beforenow != ink && (beforenow->fg != ink->fg ||
beforenow->bg != ink->bg))
@@ -65,7 +65,6 @@ void set_syntax_colorpairs(syntaxtype *sint)
* for the colors in each loaded syntax. */
void set_colorpairs(void)
{
syntaxtype *sint;
bool using_defaults = FALSE;
/* Tell ncurses to enable colors. */
@@ -105,9 +104,9 @@ void set_colorpairs(void)
}
/* For each loaded syntax, assign pair numbers to color combinations. */
for (sint = syntaxes; sint != NULL; sint = sint->next)
if (sint->filename == NULL)
set_syntax_colorpairs(sint);
for (syntaxtype *sntx = syntaxes; sntx != NULL; sntx = sntx->next)
if (sntx->filename == NULL)
set_syntax_colorpairs(sntx);
}
/* Initialize the color information. */
@@ -167,7 +166,7 @@ bool found_in_list(regexlisttype *head, const char *shibboleth)
/* Update the color information based on the current filename and content. */
void color_update(void)
{
syntaxtype *sint = NULL;
syntaxtype *sntx = NULL;
/* If the rcfiles were not read, or contained no syntaxes, get out. */
if (syntaxes == NULL)
@@ -179,42 +178,39 @@ void color_update(void)
if (strcmp(syntaxstr, "none") == 0)
return;
for (sint = syntaxes; sint != NULL; sint = sint->next) {
if (strcmp(sint->name, syntaxstr) == 0)
for (sntx = syntaxes; sntx != NULL; sntx = sntx->next)
if (strcmp(sntx->name, syntaxstr) == 0)
break;
}
if (sint == NULL && !inhelp)
if (sntx == NULL && !inhelp)
statusline(ALERT, _("Unknown syntax name: %s"), syntaxstr);
}
/* If no syntax-override string was specified, or it didn't match,
* try finding a syntax based on the filename (extension). */
if (sint == NULL && !inhelp) {
if (sntx == NULL && !inhelp) {
char *fullname = get_full_path(openfile->filename);
if (fullname == NULL)
fullname = mallocstrcpy(fullname, openfile->filename);
for (sint = syntaxes; sint != NULL; sint = sint->next) {
if (found_in_list(sint->extensions, fullname))
for (sntx = syntaxes; sntx != NULL; sntx = sntx->next)
if (found_in_list(sntx->extensions, fullname))
break;
}
free(fullname);
}
/* If the filename didn't match anything, try the first line. */
if (sint == NULL && !inhelp) {
for (sint = syntaxes; sint != NULL; sint = sint->next) {
if (found_in_list(sint->headers, openfile->filetop->data))
if (sntx == NULL && !inhelp) {
for (sntx = syntaxes; sntx != NULL; sntx = sntx->next)
if (found_in_list(sntx->headers, openfile->filetop->data))
break;
}
}
#ifdef HAVE_LIBMAGIC
/* If we still don't have an answer, try using magic. */
if (sint == NULL && !inhelp) {
if (sntx == NULL && !inhelp) {
struct stat fileinfo;
magic_t cookie = NULL;
const char *magicstring = NULL;
@@ -238,10 +234,9 @@ void color_update(void)
/* Now try and find a syntax that matches the magic string. */
if (magicstring != NULL) {
for (sint = syntaxes; sint != NULL; sint = sint->next) {
if (found_in_list(sint->magics, magicstring))
for (sntx = syntaxes; sntx != NULL; sntx = sntx->next)
if (found_in_list(sntx->magics, magicstring))
break;
}
}
if (stat(openfile->filename, &fileinfo) == 0)
@@ -250,21 +245,20 @@ void color_update(void)
#endif /* HAVE_LIBMAGIC */
/* If nothing at all matched, see if there is a default syntax. */
if (sint == NULL && !inhelp) {
for (sint = syntaxes; sint != NULL; sint = sint->next) {
if (strcmp(sint->name, "default") == 0)
if (sntx == NULL && !inhelp) {
for (sntx = syntaxes; sntx != NULL; sntx = sntx->next)
if (strcmp(sntx->name, "default") == 0)
break;
}
}
/* When the syntax isn't loaded yet, parse it and initialize its colors. */
if (sint != NULL && sint->filename != NULL) {
parse_one_include(sint->filename, sint);
set_syntax_colorpairs(sint);
if (sntx != NULL && sntx->filename != NULL) {
parse_one_include(sntx->filename, sntx);
set_syntax_colorpairs(sntx);
}
openfile->syntax = sint;
openfile->colorstrings = (sint == NULL ? NULL : sint->color);
openfile->syntax = sntx;
openfile->colorstrings = (sntx == NULL ? NULL : sntx->color);
}
/* Allocate and initialize (for the given line) the cache for multiline info. */
@@ -272,7 +266,7 @@ void set_up_multicache(linestruct *line)
{
line->multidata = (short *)nmalloc(openfile->syntax->nmultis * sizeof(short));
for (int index = 0; index < openfile->syntax->nmultis; index++)
for (short index = 0; index < openfile->syntax->nmultis; index++)
line->multidata[index] = -1;
}

View File

@@ -41,7 +41,7 @@ void do_deletion(undo_type action)
/* If the type of action changed or the cursor moved to a different
* line, create a new undo item, otherwise update the existing item. */
if (action != openfile->last_action ||
openfile->current->lineno != openfile->current_undo->lineno)
openfile->current->lineno != openfile->current_undo->head_lineno)
add_undo(action, NULL);
else
update_undo(action);
@@ -120,7 +120,7 @@ void do_delete(void)
zap_text();
else
#endif
do_deletion(DEL);
do_deletion(DEL);
}
/* Backspace over one character. That is, move the cursor left one
@@ -132,12 +132,38 @@ void do_backspace(void)
zap_text();
else
#endif
if (openfile->current != openfile->filetop || openfile->current_x > 0) {
if (openfile->current_x > 0 || openfile->current != openfile->filetop) {
do_left();
do_deletion(BACK);
}
}
/* Return FALSE when a cut command would not actually cut anything: when
* on an empty line at EOF, or when the mark covers zero characters, or
* (when test_cliff is TRUE) when the magic line would be cut. */
bool is_cuttable(bool test_cliff)
{
size_t from = (test_cliff) ? openfile->current_x : 0;
if ((openfile->current->next == NULL && openfile->current->data[from] == '\0'
#ifndef NANO_TINY
&& openfile->mark == NULL) ||
(openfile->mark == openfile->current &&
openfile->mark_x == openfile->current_x) ||
(from > 0 && !ISSET(NO_NEWLINES) &&
openfile->current->data[from] == '\0' &&
openfile->current->next == openfile->filebot
#endif
)) {
#ifndef NANO_TINY
statusbar(_("Nothing was cut"));
openfile->mark = NULL;
#endif
return FALSE;
} else
return TRUE;
}
#ifndef NANO_TINY
/* Delete text from the cursor until the first start of a word to
* the left, or to the right when forward is TRUE. */
@@ -205,7 +231,7 @@ void chop_next_word(void)
{
openfile->mark = NULL;
if (is_cuttable(openfile->current_x > 0))
if (is_cuttable(TRUE))
chop_word(FORWARD);
}
#endif /* !NANO_TINY */
@@ -304,10 +330,8 @@ void ingraft_buffer(linestruct *topline)
bool edittop_inside = (openfile->edittop == openfile->current);
#ifndef NANO_TINY
/* Remember whether mark and cursor are on the same line, and their order. */
bool right_side_up = (openfile->mark && mark_is_before_cursor());
bool same_line = (openfile->mark == openfile->current);
bool right_side_up = (openfile->mark &&
(openfile->mark->lineno < openfile->current->lineno ||
(same_line && openfile->mark_x <= openfile->current_x)));
#endif
/* Partition the buffer so that it contains no text, then delete it.*/
@@ -508,30 +532,6 @@ void do_snip(bool copying, bool marked, bool until_eof, bool append)
refresh_needed = TRUE;
}
/* Return FALSE when a cut command would not actually cut anything: when
* on an empty line at EOF, or when the mark covers zero characters, or
* (when test_cliff is TRUE) when the magic line would be cut. */
bool is_cuttable(bool test_cliff)
{
if ((openfile->current->next == NULL && openfile->current->data[0] == '\0'
#ifndef NANO_TINY
&& openfile->mark == NULL) ||
(openfile->mark == openfile->current &&
openfile->mark_x == openfile->current_x) ||
(test_cliff && openfile->current->data[openfile->current_x] == '\0' &&
((ISSET(NO_NEWLINES) && openfile->current == openfile->filebot) ||
(!ISSET(NO_NEWLINES) && openfile->current == openfile->filebot->prev))
#endif
)) {
#ifndef NANO_TINY
statusbar(_("Nothing was cut"));
openfile->mark = NULL;
#endif
return FALSE;
} else
return TRUE;
}
/* Move text from the current buffer into the cutbuffer. */
void cut_text(void)
{
@@ -587,9 +587,10 @@ void copy_text(void)
/* Cut from the current cursor position to the end of the file. */
void cut_till_eof(void)
{
if ((openfile->current == openfile->filebot && openfile->current->data[0] == '\0') ||
(!ISSET(NO_NEWLINES) && openfile->current->next == openfile->filebot &&
openfile->current->data[openfile->current_x] == '\0')) {
if (openfile->current->data[openfile->current_x] == '\0' &&
(openfile->current->next == NULL ||
(!ISSET(NO_NEWLINES) && openfile->current_x > 0 &&
openfile->current->next == openfile->filebot))) {
statusbar(_("Nothing was cut"));
return;
}

View File

@@ -2,7 +2,7 @@
* files.c -- This file is part of GNU nano. *
* *
* Copyright (C) 1999-2011, 2013-2020 Free Software Foundation, Inc. *
* Copyright (C) 2015-2019 Benno Schulenberg *
* Copyright (C) 2015-2020 Benno Schulenberg *
* *
* GNU nano is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published *
@@ -29,43 +29,13 @@
#endif
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#ifndef NANO_TINY
#define LOCKSIZE 1024
const char *locking_prefix = ".";
const char *locking_suffix = ".swp";
/* Prefix and suffix for the name of the vim-style lock file. */
static pid_t pid_of_command = -1;
/* The PID of a forked process -- needed when wanting to abort it. */
#endif
/* Verify that the containing directory of the given filename exists. */
bool has_valid_path(const char *filename)
{
char *namecopy = copy_of(filename);
char *parentdir = dirname(namecopy);
struct stat parentinfo;
bool validity = FALSE;
if (stat(parentdir, &parentinfo) == -1) {
if (errno == ENOENT)
statusline(ALERT, _("Directory '%s' does not exist"), parentdir);
else
statusline(ALERT, _("Path '%s': %s"), parentdir, strerror(errno));
} else if (!S_ISDIR(parentinfo.st_mode))
statusline(ALERT, _("Path '%s' is not a directory"), parentdir);
else if (access(parentdir, X_OK) == -1)
statusline(ALERT, _("Path '%s' is not accessible"), parentdir);
#ifndef NANO_TINY
else if (ISSET(LOCKING) && !ISSET(VIEW_MODE) && access(parentdir, W_OK) < 0)
statusline(MILD, _("Directory '%s' is not writable"), parentdir);
#endif
else
validity = TRUE;
free(namecopy);
return validity;
}
/* Add an item to the circular list of openfile structs. */
void make_new_buffer(void)
{
@@ -132,55 +102,53 @@ void make_new_buffer(void)
}
#ifndef NANO_TINY
/* Delete the lockfile. Return -1 if unsuccessful, and 1 otherwise. */
int delete_lockfile(const char *lockfilename)
/* Delete the lockfile. Return TRUE on success, and FALSE otherwise. */
bool delete_lockfile(const char *lockfilename)
{
if (unlink(lockfilename) < 0 && errno != ENOENT) {
statusline(MILD, _("Error deleting lock file %s: %s"),
lockfilename, strerror(errno));
return -1;
}
return 1;
return FALSE;
} else
return TRUE;
}
/* Write a lockfile, under the given lockfilename. This ALWAYS annihilates
* an existing version of that file. Return 1 on success, and 0 on failure. */
int write_lockfile(const char *lockfilename, const char *filename, bool modified)
#define LOCKSIZE 1024
#define SKIPTHISFILE (char *)-1
const char *locking_prefix = ".";
const char *locking_suffix = ".swp";
/* Write a lock file, under the given lockfilename. This always annihilates an
* existing version of that file. Return TRUE on success; FALSE otherwise. */
bool write_lockfile(const char *lockfilename, const char *filename, bool modified)
{
#ifdef HAVE_PWD_H
pid_t mypid = getpid();
uid_t myuid = geteuid();
struct passwd *mypwuid = getpwuid(myuid);
char myhostname[32];
struct stat fileinfo;
int cflags, fd;
FILE *filestream;
pid_t mypid;
uid_t myuid;
struct passwd *mypwuid;
struct stat fileinfo;
char *lockdata;
char myhostname[32];
size_t wroteamt;
mypid = getpid();
myuid = geteuid();
/* First run things that might fail before blowing away the old state. */
if ((mypwuid = getpwuid(myuid)) == NULL) {
if (mypwuid == NULL) {
/* TRANSLATORS: Keep the next eight messages at most 76 characters. */
statusline(MILD, _("Couldn't determine my identity for lock file"));
return 0;
return FALSE;
}
if (gethostname(myhostname, 31) < 0) {
if (errno == ENAMETOOLONG)
myhostname[31] = '\0';
else {
statusline(MILD, _("Couldn't determine hostname: %s"), strerror(errno));
return 0;
}
}
if (gethostname(myhostname, 31) < 0 && errno != ENAMETOOLONG) {
statusline(MILD, _("Couldn't determine hostname: %s"), strerror(errno));
return FALSE;
} else
myhostname[31] = '\0';
/* If the lockfile exists, try to delete it. */
if (stat(lockfilename, &fileinfo) != -1)
if (delete_lockfile(lockfilename) < 0)
return 0;
if (!delete_lockfile(lockfilename))
return FALSE;
if (ISSET(INSECURE_BACKUP))
cflags = O_WRONLY | O_CREAT | O_APPEND;
@@ -190,10 +158,11 @@ int write_lockfile(const char *lockfilename, const char *filename, bool modified
/* Try to create the lockfile. */
fd = open(lockfilename, cflags,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd < 0) {
statusline(MILD, _("Error writing lock file %s: %s"),
lockfilename, strerror(errno));
return 0;
return FALSE;
}
/* Try to associate a stream with the now open lockfile. */
@@ -203,20 +172,20 @@ int write_lockfile(const char *lockfilename, const char *filename, bool modified
statusline(MILD, _("Error writing lock file %s: %s"),
lockfilename, strerror(errno));
close(fd);
return 0;
return FALSE;
}
lockdata = charalloc(LOCKSIZE);
memset(lockdata, 0, LOCKSIZE);
/* This is the lock data we will store (other bytes are 0x00):
/* This is the lock data we will store (other bytes remain 0x00):
*
* bytes 0-1 - 0x62 0x30
* bytes 2-11 - name of program that created the lock
* bytes 24-27 - PID (little endian) of creator process
* bytes 28-43 - username of who created the lock
* bytes 68-99 - hostname of where the lock was created
* bytes 108-876 - filename the lock is for
* bytes 28-43 - username of the user who created the lock
* bytes 68-99 - hostname of machine from where the lock was created
* bytes 108-876 - filename that the lock is for
* byte 1007 - 0x55 if file is modified
*
* Nano does not write the page size (bytes 12-15), nor the modification
@@ -243,18 +212,17 @@ int write_lockfile(const char *lockfilename, const char *filename, bool modified
if (fclose(filestream) == EOF || wroteamt < LOCKSIZE) {
statusline(MILD, _("Error writing lock file %s: %s"),
lockfilename, strerror(errno));
return 0;
return FALSE;
}
openfile->lock_filename = (char *)lockfilename;
#endif
return 1;
return TRUE;
}
/* Deal with lockfiles. Return -1 on refusing to override the lockfile,
* and 1 on successfully creating it; 0 means we were not successful in
* creating the lockfile but we should continue to load the file. */
int do_lockfile(const char *filename, bool ask_the_user)
/* First check if a lock file already exists. If so, and ask_the_user is TRUE,
* then ask whether to open the corresponding file anyway. Return SKIPTHISFILE
* when the user answers "No", return the name of the lock file on success, and
* return NULL on failure. */
char *do_lockfile(const char *filename, bool ask_the_user)
{
char *namecopy = copy_of(filename);
char *secondcopy = copy_of(filename);
@@ -262,7 +230,6 @@ int do_lockfile(const char *filename, bool ask_the_user)
strlen(locking_suffix) + 3;
char *lockfilename = charalloc(locknamesize);
struct stat fileinfo;
int retval = 0;
snprintf(lockfilename, locknamesize, "%s/%s%s%s", dirname(namecopy),
locking_prefix, basename(secondcopy), locking_suffix);
@@ -280,7 +247,8 @@ int do_lockfile(const char *filename, bool ask_the_user)
if ((lockfd = open(lockfilename, O_RDONLY)) < 0) {
statusline(ALERT, _("Error opening lock file %s: %s"),
lockfilename, strerror(errno));
goto free_the_name;
free(lockfilename);
return NULL;
}
lockbuf = charalloc(LOCKSIZE);
@@ -293,8 +261,9 @@ int do_lockfile(const char *filename, bool ask_the_user)
* or the two magic bytes are not there, skip the lock file. */
if (readamt < 68 || lockbuf[0] != 0x62 || lockbuf[1] != 0x30) {
statusline(ALERT, _("Bad lock file is ignored: %s"), lockfilename);
free(lockfilename);
free(lockbuf);
goto free_the_name;
return NULL;
}
strncpy(lockprog, &lockbuf[2], 10);
@@ -342,19 +311,17 @@ int do_lockfile(const char *filename, bool ask_the_user)
finish();
if (choice < 1) {
retval = -1;
free(lockfilename);
wipe_statusbar();
goto free_the_name;
return SKIPTHISFILE;
}
}
retval = write_lockfile(lockfilename, filename, FALSE);
if (write_lockfile(lockfilename, filename, FALSE))
return lockfilename;
free_the_name:
if (retval < 1)
free(lockfilename);
return retval;
free(lockfilename);
return NULL;
}
/* Perform a stat call on the given filename, allocating a stat struct
@@ -372,18 +339,49 @@ void stat_with_alloc(const char *filename, struct stat **pstat)
}
#endif /* !NANO_TINY */
/* Verify that the containing directory of the given filename exists. */
bool has_valid_path(const char *filename)
{
char *namecopy = copy_of(filename);
char *parentdir = dirname(namecopy);
struct stat parentinfo;
bool validity = FALSE;
if (stat(parentdir, &parentinfo) == -1) {
if (errno == ENOENT)
statusline(ALERT, _("Directory '%s' does not exist"), parentdir);
else
statusline(ALERT, _("Path '%s': %s"), parentdir, strerror(errno));
} else if (!S_ISDIR(parentinfo.st_mode))
statusline(ALERT, _("Path '%s' is not a directory"), parentdir);
else if (access(parentdir, X_OK) == -1)
statusline(ALERT, _("Path '%s' is not accessible"), parentdir);
#ifndef NANO_TINY
else if (ISSET(LOCKING) && !ISSET(VIEW_MODE) && access(parentdir, W_OK) < 0)
statusline(MILD, _("Directory '%s' is not writable"), parentdir);
#endif
else
validity = TRUE;
free(namecopy);
return validity;
}
/* This does one of three things. If the filename is "", it just creates
* a new empty buffer. When the filename is not empty, it reads that file
* into a new buffer when requested, otherwise into the existing buffer. */
bool open_buffer(const char *filename, bool new_buffer)
bool open_buffer(const char *filename, bool new_one)
{
char *realname;
/* The filename after tilde expansion. */
#ifndef NANO_TINY
char *thelocksname = NULL;
#endif
struct stat fileinfo;
int descriptor = 0;
/* Code 0 means new file, -1 means failure, and else it's the fd. */
FILE *f;
int rc;
/* rc == -2 means that we have a new file. -1 means that the
* open() failed. 0 means that the open() succeeded. */
/* Display newlines in filenames as ^J. */
as_an_at = FALSE;
@@ -418,39 +416,34 @@ bool open_buffer(const char *filename, bool new_buffer)
#endif
}
/* If we're going to load into a new buffer, first create the new
* buffer and (if possible) lock the corresponding file. */
if (new_buffer) {
make_new_buffer();
if (has_valid_path(realname)) {
/* When loading into a new buffer, first check the file's path is valid,
* and then (if requested and possible) create a lock file for it. */
if (new_one && has_valid_path(realname)) {
#ifndef NANO_TINY
if (ISSET(LOCKING) && !ISSET(VIEW_MODE) && filename[0] != '\0') {
/* When not overriding an existing lock, discard the buffer. */
if (do_lockfile(realname, TRUE) < 0) {
#ifdef ENABLE_MULTIBUFFER
if (openfile != openfile->next)
close_buffer();
#endif
free(realname);
return FALSE;
}
if (ISSET(LOCKING) && !ISSET(VIEW_MODE) && filename[0] != '\0') {
thelocksname = do_lockfile(realname, TRUE);
/* When not overriding an existing lock, don't open a buffer. */
if (thelocksname == SKIPTHISFILE) {
free(realname);
return FALSE;
}
#endif /* !NANO_TINY */
}
#endif
}
/* If the filename isn't blank, and we are not in NOREAD_MODE,
* open the file. Otherwise, treat it as a new file. */
rc = (filename[0] != '\0' && !ISSET(NOREAD_MODE)) ?
open_file(realname, new_buffer, &f) : -2;
if (new_one)
make_new_buffer();
/* If we have a non-new file, read it in. Then, if the buffer has
* no stat, update the stat, if applicable. */
if (rc > 0) {
/* If we have a filename and are not in NOREAD mode, open the file. */
if (filename[0] != '\0' && !ISSET(NOREAD_MODE))
descriptor = open_file(realname, new_one, &f);
/* If we've successfully opened an existing file, read it in. */
if (descriptor > 0) {
install_handler_for_Ctrl_C();
read_file(f, rc, realname, !new_buffer);
read_file(f, descriptor, realname, !new_one);
restore_handler_for_Ctrl_C();
@@ -460,10 +453,13 @@ bool open_buffer(const char *filename, bool new_buffer)
#endif
}
/* If we have a file, and we've loaded it into a new buffer, set
* the filename and put the cursor at the start of the buffer. */
if (rc != -1 && new_buffer) {
/* When we've loaded a file into a new buffer, set the filename
* and put the cursor at the start of the buffer. */
if (descriptor >= 0 && new_one) {
openfile->filename = mallocstrcpy(openfile->filename, realname);
#ifndef NANO_TINY
openfile->lock_filename = thelocksname;
#endif
openfile->current = openfile->filetop;
openfile->current_x = 0;
openfile->placewewant = 0;
@@ -472,60 +468,13 @@ bool open_buffer(const char *filename, bool new_buffer)
#ifdef ENABLE_COLOR
/* If we're loading into a new buffer, update the colors to account
* for it, if applicable. */
if (new_buffer)
if (new_one)
color_update();
#endif
free(realname);
return TRUE;
}
#ifdef ENABLE_SPELLER
/* 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, bool marked,
const char *operation)
{
linestruct *was_cutbuffer = cutbuffer;
int descriptor;
FILE *f;
descriptor = open_file(filename, FALSE, &f);
if (descriptor < 0)
return FALSE;
#ifndef NANO_TINY
add_undo(COUPLE_BEGIN, operation);
#endif
/* When nothing is marked, start at the top of the buffer. */
if (!marked) {
openfile->current = openfile->filetop;
openfile->current_x = 0;
}
/* Throw away the marked region or the whole buffer. */
cutbuffer = NULL;
#ifndef NANO_TINY
add_undo(action, NULL);
#endif
do_snip(FALSE, marked, !marked, FALSE);
#ifndef NANO_TINY
update_undo(action);
#endif
free_lines(cutbuffer);
cutbuffer = was_cutbuffer;
/* Insert the spell-checked file into the cleared area. */
read_file(f, descriptor, filename, TRUE);
#ifndef NANO_TINY
add_undo(COUPLE_END, operation);
#endif
return TRUE;
}
#endif /* ENABLE_SPELLER */
/* Mark the current file as modified if it isn't already, and
* then update the title bar to display the file's new status. */
void set_modified(void)
@@ -569,7 +518,7 @@ void mention_name_and_linecount(void)
(openfile->filebot->data[0] == '\0' ? 1 : 0);
#ifndef NANO_TINY
if (openfile->fmt != NIX_FILE)
/* TRANSLATORS: first %s is file name, second %s is file format. */
/* TRANSLATORS: First %s is file name, second %s is file format. */
statusline(HUSH, P_("%s -- %zu line (%s)", "%s -- %zu lines (%s)", count),
openfile->filename[0] == '\0' ?
_("New Buffer") : tail(openfile->filename), count,
@@ -644,7 +593,7 @@ void close_buffer(void)
free(orphan->current_stat);
free(orphan->lock_filename);
/* Free the undo stack. */
discard_until(NULL, orphan, TRUE);
discard_until(NULL);
#endif
openfile = orphan->prev;
@@ -904,31 +853,30 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable)
}
/* Open the file with the given name. If the file does not exist, display
* "New File" if newfie is TRUE, and say "File not found" otherwise.
* Return -2 if we say "New File", -1 if the file isn't opened, and the
* obtained fd otherwise. *f is set to the opened file. */
int open_file(const char *filename, bool newfie, FILE **f)
* "New File" if new_one is TRUE, and say "File not found" otherwise.
* Return 0 if we say "New File", -1 upon failure, and the obtained file
* descriptor otherwise. The opened filestream is returned in *f. */
int open_file(const char *filename, bool new_one, FILE **f)
{
struct stat fileinfo, fileinfo2;
int fd;
char *full_filename = get_full_path(filename);
struct stat fileinfo;
int fd;
/* If the full path is unusable (due to some component's permissions),
* but the relative path is okay, then just use that one. */
if (full_filename == NULL || (stat(full_filename, &fileinfo) == -1 &&
stat(filename, &fileinfo2) != -1))
/* If the absolute path is unusable (due to some component's permissions),
* try the given path instead (as it is probably relative). */
if (full_filename == NULL || stat(full_filename, &fileinfo) == -1)
full_filename = mallocstrcpy(full_filename, filename);
if (stat(full_filename, &fileinfo) == -1) {
if (newfie) {
statusbar(_("New File"));
free(full_filename);
return -2;
}
statusline(ALERT, _("File \"%s\" not found"), filename);
free(full_filename);
return -1;
if (new_one) {
statusbar(_("New File"));
return 0;
} else {
statusline(ALERT, _("File \"%s\" not found"), filename);
return -1;
}
}
#ifndef NANO_TINY
@@ -1005,6 +953,161 @@ char *get_next_filename(const char *name, const char *suffix)
return buf;
}
#ifndef NANO_TINY
/* Send an unconditional kill signal to the running external command. */
RETSIGTYPE cancel_the_command(int signal)
{
kill(pid_of_command, SIGKILL);
}
/* Send the text that starts at the given line to file descriptor fd. */
void send_data(const linestruct *line, int fd)
{
FILE *tube = fdopen(fd, "w");
if (tube == NULL)
exit(4);
/* 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");
line = line->next;
}
fclose(tube);
}
/* Execute the given command in a shell. Return TRUE on success. */
bool execute_command(const char *command)
{
int from_fd[2], to_fd[2];
/* The pipes through which text will be written and read. */
const bool should_pipe = (command[0] == '|');
FILE *stream;
struct sigaction oldaction, newaction = {{0}};
/* Original and temporary handlers for SIGINT. */
/* Create a pipe to read the command's output from, and, if needed,
* a pipe to feed the command's input through. */
if (pipe(from_fd) == -1 || (should_pipe && pipe(to_fd) == -1)) {
statusline(ALERT, _("Could not create pipe"));
return FALSE;
}
/* Fork a child process to run the command in. */
if ((pid_of_command = fork()) == 0) {
const char *theshell = getenv("SHELL");
if (theshell == NULL)
theshell = (char *)"/bin/sh";
/* Child: close the unused read end of the output pipe. */
close(from_fd[0]);
/* Connect the write end of the output pipe to the process' output streams. */
dup2(from_fd[1], fileno(stdout));
dup2(from_fd[1], fileno(stderr));
/* If the parent sends text, connect the read end of the
* feeding pipe to the child's input stream. */
if (should_pipe) {
dup2(to_fd[0], fileno(stdin));
close(to_fd[1]);
}
/* Run the given command inside the preferred shell. */
execl(theshell, tail(theshell), "-c", should_pipe ? &command[1] : command, NULL);
/* If the exec call returns, there was an error. */
exit(1);
}
/* Parent: close the unused write end of the pipe. */
close(from_fd[1]);
if (pid_of_command == -1) {
statusline(ALERT, _("Could not fork"));
close(from_fd[0]);
return FALSE;
}
statusbar(_("Executing..."));
/* If the command starts with "|", pipe buffer or region to the command. */
if (should_pipe) {
linestruct *was_cutbuffer = cutbuffer;
cutbuffer = NULL;
#ifdef ENABLE_MULTIBUFFER
if (ISSET(MULTIBUFFER)) {
openfile = openfile->prev;
if (openfile->mark)
do_snip(TRUE, TRUE, FALSE, FALSE);
} else
#endif
{
/* TRANSLATORS: This one goes with Undid/Redid messages. */
add_undo(COUPLE_BEGIN, N_("filtering"));
if (openfile->mark == NULL) {
openfile->current = openfile->filetop;
openfile->current_x = 0;
}
add_undo(CUT, NULL);
do_snip(FALSE, openfile->mark != NULL, openfile->mark == NULL, FALSE);
update_undo(CUT);
}
/* Create a separate process for piping the data to the command. */
if (fork() == 0) {
send_data(cutbuffer, to_fd[1]);
exit(0);
}
close(to_fd[0]);
close(to_fd[1]);
#ifdef ENABLE_MULTIBUFFER
if (ISSET(MULTIBUFFER))
openfile = openfile->next;
#endif
free_lines(cutbuffer);
cutbuffer = was_cutbuffer;
}
/* Re-enable interpretation of the special control keys so that we get
* SIGINT when Ctrl-C is pressed. */
enable_kb_interrupt();
/* Set up a signal handler so that ^C will terminate the forked process. */
newaction.sa_handler = cancel_the_command;
newaction.sa_flags = 0;
sigaction(SIGINT, &newaction, &oldaction);
stream = fdopen(from_fd[0], "rb");
if (stream == NULL)
statusline(ALERT, _("Failed to open pipe: %s"), strerror(errno));
else
read_file(stream, 0, "pipe", TRUE);
if (should_pipe && !ISSET(MULTIBUFFER))
add_undo(COUPLE_END, N_("filtering"));
/* Wait for the external command (and possibly data sender) to terminate. */
wait(NULL);
if (should_pipe)
wait(NULL);
/* Restore the original handler for SIGINT. */
sigaction(SIGINT, &oldaction, NULL);
/* Restore the terminal to its desired state, and disable
* interpretation of the special control keys again. */
terminal_init();
return TRUE;
}
#endif /* NANO_TINY */
/* Insert a file into the current buffer, or into a new buffer when
* the MULTIBUFFER flag is set. */
void do_insertfile(void)
@@ -1491,12 +1594,12 @@ bool write_file(const char *name, FILE *thefile, bool tmp,
kind_of_writing_type method, bool fullbuffer)
{
#ifndef NANO_TINY
bool isactualfile = FALSE;
bool is_existing_file;
/* Becomes TRUE when the file is non-temporary and exists. */
struct stat st;
/* The status fields filled in by stat(). */
#endif
char *realname;
char *realname = real_dir_from_tilde(name);
/* The filename after tilde expansion. */
char *tempname = NULL;
/* The name of the temporary file we use when prepending. */
@@ -1507,13 +1610,6 @@ bool write_file(const char *name, FILE *thefile, bool tmp,
bool retval = FALSE;
/* The return value, to become TRUE when writing has succeeded. */
#ifndef NANO_TINY
if (*name == '\0')
die("Tried to write a nameless file -- please report a bug\n");
#endif
realname = real_dir_from_tilde(name);
#ifdef ENABLE_OPERATINGDIR
/* If we're writing a temporary file, we're probably going outside
* the operating directory, so skip the operating directory test. */
@@ -1524,20 +1620,19 @@ bool write_file(const char *name, FILE *thefile, bool tmp,
#endif
#ifndef NANO_TINY
/* Check whether the file (at the end of the symlink) exists. */
if (!tmp)
isactualfile = (stat(realname, &st) != -1);
is_existing_file = (!tmp) && (stat(realname, &st) != -1);
/* If we haven't stat()d this file before (say, the user just specified
* it interactively), stat and save the value now, or else we will chase
* null pointers when we do modtime checks and such during backup. */
if (openfile->current_stat == NULL && isactualfile)
if (openfile->current_stat == NULL && is_existing_file)
stat_with_alloc(realname, &openfile->current_stat);
/* We back up only if the backup toggle is set, and the file exists and
* isn't temporary. Furthermore, if we aren't appending, prepending, or
* writing a selection, we back up only if the file has not been modified
* by someone else since nano opened it. */
if (ISSET(BACKUP_FILE) && isactualfile && openfile->current_stat &&
if (ISSET(BACKUP_FILE) && is_existing_file && openfile->current_stat &&
(method != OVERWRITE || openfile->mark ||
openfile->current_stat->st_mtime == st.st_mtime)) {
static struct timespec filetime[2];
@@ -1719,7 +1814,7 @@ bool write_file(const char *name, FILE *thefile, bool tmp,
}
}
if (!tmp && S_ISFIFO(st.st_mode))
if (is_existing_file && S_ISFIFO(st.st_mode))
statusbar(_("Writing to FIFO..."));
#endif /* !NANO_TINY */
@@ -1860,8 +1955,7 @@ bool write_file(const char *name, FILE *thefile, bool tmp,
if (openfile->lock_filename != NULL) {
delete_lockfile(openfile->lock_filename);
free(openfile->lock_filename);
openfile->lock_filename = NULL;
do_lockfile(realname, FALSE);
openfile->lock_filename = do_lockfile(realname, FALSE);
}
#endif
#ifdef ENABLE_COLOR
@@ -2240,8 +2334,7 @@ char *real_dir_from_tilde(const char *path)
#ifdef HAVE_PWD_H
const struct passwd *userdata;
tilded = measured_copy(path, i + 1);
tilded[i] = '\0';
tilded = measured_copy(path, i);
do {
userdata = getpwent();

View File

@@ -2,7 +2,7 @@
* global.c -- This file is part of GNU nano. *
* *
* Copyright (C) 1999-2011, 2013-2020 Free Software Foundation, Inc. *
* Copyright (C) 2014-2019 Benno Schulenberg *
* Copyright (C) 2014-2020 Benno Schulenberg *
* *
* GNU nano is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published *
@@ -366,6 +366,51 @@ void add_to_funcs(void (*func)(void), int menus, const char *desc,
#endif
}
/* Parse the given keystring and return the corresponding keycode,
* or return -1 when the string is invalid. */
int keycode_from_string(const char *keystring)
{
if (keystring[0] == '^') {
if (keystring[2] == '\0') {
if (keystring[1] == '/')
return 31;
if (keystring[1] <= '_')
return keystring[1] - 64;
if (keystring[1] == '`')
return 0;
else
return -1;
} else if (strcasecmp(keystring, "^Space") == 0)
return 0;
else
return -1;
} else if (keystring[0] == 'M') {
if (keystring[1] == '-' && keystring[3] == '\0')
return tolower((unsigned char)keystring[2]);
if (strcasecmp(keystring, "M-Space") == 0)
return (int)' ';
else
return -1;
#ifdef ENABLE_NANORC
} else if (strncasecmp(keystring, "Sh-M-", 5) == 0 &&
'a' <= (keystring[5] | 0x20) && (keystring[5] | 0x20) <= 'z' &&
keystring[6] == '\0') {
shifted_metas = TRUE;
return (keystring[5] & 0x5F);
#endif
} else if (keystring[0] == 'F') {
int fn = atoi(&keystring[1]);
if (fn < 1 || fn > 24)
return -1;
return KEY_F0 + fn;
} else if (strcasecmp(keystring, "Ins") == 0)
return KEY_IC;
else if (strcasecmp(keystring, "Del") == 0)
return KEY_DC;
else
return -1;
}
/* Add a key combo to the linked list of shortcuts. */
void add_to_sclist(int menus, const char *scstring, const int keycode,
void (*func)(void), int toggle)
@@ -513,55 +558,6 @@ functionptrtype interpret(int *keycode)
}
#endif /* ENABLE_BROWSER || ENABLE_HELP */
/* Parse the given keystring and return the corresponding keycode,
* or return -1 when the string is invalid. */
int keycode_from_string(const char *keystring)
{
if (keystring[0] == '^') {
if (keystring[2] == '\0') {
if (keystring[1] == '/')
return 31;
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
if (keystring[1] == 'H')
return KEY_BACKSPACE;
#endif
if (keystring[1] <= '_')
return keystring[1] - 64;
else if (keystring[1] == '`')
return 0;
else
return -1;
} else if (strcasecmp(keystring, "^Space") == 0)
return 0;
else
return -1;
} else if (keystring[0] == 'M') {
if (keystring[1] == '-' && keystring[3] == '\0')
return tolower((unsigned char)keystring[2]);
if (strcasecmp(keystring, "M-Space") == 0)
return (int)' ';
else
return -1;
#ifdef ENABLE_NANORC
} else if (strncasecmp(keystring, "Sh-M-", 5) == 0 &&
'a' <= (keystring[5] | 0x20) && (keystring[5] | 0x20) <= 'z' &&
keystring[6] == '\0') {
shifted_metas = TRUE;
return (keystring[5] & 0x5F);
#endif
} else if (keystring[0] == 'F') {
int fn = atoi(&keystring[1]);
if (fn < 1 || fn > 24)
return -1;
return KEY_F0 + fn;
} else if (strcasecmp(keystring, "Ins") == 0)
return KEY_IC;
else if (strcasecmp(keystring, "Del") == 0)
return KEY_DC;
else
return -1;
}
/* These two tags are used elsewhere too, so they are global. */
/* TRANSLATORS: Try to keep the next two strings at most 10 characters. */
const char *exit_tag = N_("Exit");
@@ -586,7 +582,7 @@ void shortcut_init(void)
N_("Search backward for a string or a regular expression");
const char *cut_gist =
N_("Cut current line (or marked region) and store it in cutbuffer");
const char *uncut_gist =
const char *paste_gist =
N_("Paste the contents of cutbuffer at current cursor position");
const char *cursorpos_gist = N_("Display the position of the cursor");
#ifdef ENABLE_SPELLER
@@ -659,10 +655,8 @@ void shortcut_init(void)
const char *wordcount_gist =
N_("Count the number of words, lines, and characters");
#endif
const char *refresh_gist =
N_("Refresh (redraw) the current screen");
const char *suspend_gist =
N_("Suspend the editor (if suspension is enabled)");
const char *refresh_gist = N_("Refresh (redraw) the current screen");
const char *suspend_gist = N_("Suspend the editor (if suspension is enabled)");
#ifdef ENABLE_WORDCOMPLETION
const char *completion_gist = N_("Try and complete the current word");
#endif
@@ -681,10 +675,8 @@ void shortcut_init(void)
const char *reverse_gist = N_("Reverse the direction of the search");
const char *regexp_gist = N_("Toggle the use of regular expressions");
#ifdef ENABLE_HISTORIES
const char *older_gist =
N_("Recall the previous search/replace string");
const char *newer_gist =
N_("Recall the next search/replace string");
const char *older_gist = N_("Recall the previous search/replace string");
const char *newer_gist = N_("Recall the next search/replace string");
#endif
#ifndef NANO_TINY
const char *dos_gist = N_("Toggle the use of DOS format");
@@ -791,7 +783,7 @@ void shortcut_init(void)
N_("Cut Text"), WITHORSANS(cut_gist), TOGETHER, NOVIEW);
add_to_funcs(paste_text, MMAIN,
N_("Paste Text"), WITHORSANS(uncut_gist), BLANKAFTER, NOVIEW);
N_("Paste Text"), WITHORSANS(paste_gist), BLANKAFTER, NOVIEW);
if (!ISSET(RESTRICTED)) {
#ifdef ENABLE_JUSTIFY
@@ -808,8 +800,8 @@ void shortcut_init(void)
/* TRANSLATORS: Try to keep the next thirteen strings at most 12 characters. */
N_("Cur Pos"), WITHORSANS(cursorpos_gist), TOGETHER, VIEW);
#if (defined(ENABLE_JUSTIFY) && (defined(ENABLE_SPELLER) || defined(ENABLE_COLOR)) || \
!defined(ENABLE_JUSTIFY) && !defined(ENABLE_SPELLER) && !defined(ENABLE_COLOR))
#if ((defined(ENABLE_JUSTIFY) && defined(ENABLE_SPELLER)) || \
(!defined(ENABLE_JUSTIFY) && !defined(ENABLE_SPELLER)))
/* Conditionally placing this one here or further on, to keep the
* help items nicely paired in most conditions. */
add_to_funcs(do_gotolinecolumn_void, MMAIN,
@@ -907,10 +899,10 @@ void shortcut_init(void)
N_("Forward"), WITHORSANS(forwardfile_gist), TOGETHER, VIEW);
#endif
add_to_funcs(do_prev_word_void, MMAIN,
add_to_funcs(to_prev_word, MMAIN,
/* TRANSLATORS: Try to keep the next eighteen strings at most 12 characters. */
N_("Prev Word"), WITHORSANS(prevword_gist), TOGETHER, VIEW);
add_to_funcs(do_next_word_void, MMAIN,
add_to_funcs(to_next_word, MMAIN,
N_("Next Word"), WITHORSANS(nextword_gist), TOGETHER, VIEW);
add_to_funcs(do_home, MMAIN,
@@ -929,14 +921,14 @@ void shortcut_init(void)
N_("Scroll Down"), WITHORSANS(scrolldown_gist), BLANKAFTER, VIEW);
#endif
add_to_funcs(do_prev_block, MMAIN,
add_to_funcs(to_prev_block, MMAIN,
N_("Prev Block"), WITHORSANS(prevblock_gist), TOGETHER, VIEW);
add_to_funcs(do_next_block, MMAIN,
add_to_funcs(to_next_block, MMAIN,
N_("Next Block"), WITHORSANS(nextblock_gist), TOGETHER, VIEW);
#ifdef ENABLE_JUSTIFY
add_to_funcs(do_para_begin_void, MMAIN|MGOTOLINE,
add_to_funcs(to_para_begin, MMAIN|MGOTOLINE,
N_("Beg of Par"), WITHORSANS(parabegin_gist), TOGETHER, VIEW);
add_to_funcs(do_para_end_void, MMAIN|MGOTOLINE,
add_to_funcs(to_para_end, MMAIN|MGOTOLINE,
N_("End of Par"), WITHORSANS(paraend_gist), BLANKAFTER, VIEW);
#endif
@@ -957,8 +949,8 @@ void shortcut_init(void)
N_("Next File"), WITHORSANS(nextfile_gist), BLANKAFTER, VIEW);
#endif
#if (!defined(ENABLE_JUSTIFY) && (defined(ENABLE_SPELLER) || defined(ENABLE_COLOR)) || \
defined(ENABLE_JUSTIFY) && !defined(ENABLE_SPELLER) && !defined(ENABLE_COLOR))
#if ((!defined(ENABLE_JUSTIFY) && defined(ENABLE_SPELLER)) || \
(defined(ENABLE_JUSTIFY) && !defined(ENABLE_SPELLER)))
add_to_funcs(do_gotolinecolumn_void, MMAIN,
N_("Go To Line"), WITHORSANS(gotoline_gist), BLANKAFTER, VIEW);
#endif
@@ -1109,13 +1101,13 @@ void shortcut_init(void)
add_to_funcs(to_last_file, MBROWSER|MWHEREISFILE,
N_("Last File"), WITHORSANS(lastfile_gist), BLANKAFTER, VIEW);
#ifndef NANO_TINY
add_to_funcs(do_prev_word_void, MBROWSER,
add_to_funcs(to_prev_word, MBROWSER,
N_("Left Column"), WITHORSANS(browserlefthand_gist), TOGETHER, VIEW);
add_to_funcs(do_next_word_void, MBROWSER,
add_to_funcs(to_next_word, MBROWSER,
N_("Right Column"), WITHORSANS(browserrighthand_gist), TOGETHER, VIEW);
add_to_funcs(do_prev_block, MBROWSER,
add_to_funcs(to_prev_block, MBROWSER,
N_("Top Row"), WITHORSANS(browsertoprow_gist), TOGETHER, VIEW);
add_to_funcs(do_next_block, MBROWSER,
add_to_funcs(to_next_block, MBROWSER,
N_("Bottom Row"), WITHORSANS(browserbottomrow_gist), BLANKAFTER, VIEW);
#endif
#endif /* ENABLE_BROWSER */
@@ -1132,15 +1124,15 @@ void shortcut_init(void)
#endif
/* Link key combos to functions in certain menus. */
add_to_sclist(MMOST|MBROWSER, "^M", 0, do_enter, 0);
add_to_sclist(MMOST|MBROWSER, "^M", '\r', do_enter, 0);
add_to_sclist(MMOST|MBROWSER, "Enter", KEY_ENTER, do_enter, 0);
add_to_sclist(MMOST, "^H", BS_CODE, do_backspace, 0);
add_to_sclist(MMOST, "^H", '\b', do_backspace, 0);
add_to_sclist(MMOST, "Bsp", KEY_BACKSPACE, do_backspace, 0);
add_to_sclist(MMOST, "Sh-Del", SHIFT_DELETE, do_backspace, 0);
add_to_sclist(MMOST, "^D", 0, do_delete, 0);
add_to_sclist(MMOST, "Del", KEY_DC, do_delete, 0);
add_to_sclist(MMOST, "^I", 0, do_tab, 0);
add_to_sclist(MMOST, "Tab", TAB_CODE, do_tab, 0);
add_to_sclist(MMOST, "^I", '\t', do_tab, 0);
add_to_sclist(MMOST, "Tab", '\t', do_tab, 0);
add_to_sclist((MMOST|MBROWSER) & ~MFINDINHELP, "^G", 0, do_help, 0);
add_to_sclist(MMAIN|MBROWSER|MHELP, "^X", 0, do_exit, 0);
if (!ISSET(PRESERVE))
@@ -1156,7 +1148,7 @@ void shortcut_init(void)
add_to_sclist(MMOST, "^K", 0, cut_text, 0);
add_to_sclist(MMOST, "^U", 0, paste_text, 0);
#ifdef ENABLE_JUSTIFY
add_to_sclist(MMAIN, "^J", 0, do_justify_void, 0);
add_to_sclist(MMAIN, "^J", '\n', do_justify_void, 0);
#endif
#ifdef ENABLE_SPELLER
add_to_sclist(MMAIN, "^T", 0, do_spell, 0);
@@ -1214,8 +1206,8 @@ void shortcut_init(void)
if (using_utf8()) {
add_to_sclist(MMOST|MBROWSER|MHELP, "\xE2\x97\x80", KEY_LEFT, do_left, 0);
add_to_sclist(MMOST|MBROWSER|MHELP, "\xE2\x96\xb6", KEY_RIGHT, do_right, 0);
add_to_sclist(MSOME, "^\xE2\x97\x80", CONTROL_LEFT, do_prev_word_void, 0);
add_to_sclist(MSOME, "^\xE2\x96\xb6", CONTROL_RIGHT, do_next_word_void, 0);
add_to_sclist(MSOME, "^\xE2\x97\x80", CONTROL_LEFT, to_prev_word, 0);
add_to_sclist(MSOME, "^\xE2\x96\xb6", CONTROL_RIGHT, to_next_word, 0);
#if !defined(NANO_TINY) && defined(ENABLE_MULTIBUFFER)
if (!on_a_vt) {
add_to_sclist(MMAIN, "M-\xE2\x97\x80", ALT_LEFT, switch_to_prev_buffer, 0);
@@ -1227,8 +1219,8 @@ void shortcut_init(void)
{
add_to_sclist(MMOST|MBROWSER|MHELP, "Left", KEY_LEFT, do_left, 0);
add_to_sclist(MMOST|MBROWSER|MHELP, "Right", KEY_RIGHT, do_right, 0);
add_to_sclist(MSOME, "^Left", CONTROL_LEFT, do_prev_word_void, 0);
add_to_sclist(MSOME, "^Right", CONTROL_RIGHT, do_next_word_void, 0);
add_to_sclist(MSOME, "^Left", CONTROL_LEFT, to_prev_word, 0);
add_to_sclist(MSOME, "^Right", CONTROL_RIGHT, to_next_word, 0);
#ifdef ENABLE_MULTIBUFFER
if (!on_a_vt) {
add_to_sclist(MMAIN, "M-Left", ALT_LEFT, switch_to_prev_buffer, 0);
@@ -1237,13 +1229,13 @@ void shortcut_init(void)
#endif
}
#ifdef NANO_TINY
add_to_sclist(MMAIN, "M-B", 0, do_prev_word_void, 0);
add_to_sclist(MMAIN, "M-D", 0, do_prev_word_void, 0);
add_to_sclist(MMAIN, "M-F", 0, do_next_word_void, 0);
add_to_sclist(MMAIN, "M-N", 0, do_next_word_void, 0);
add_to_sclist(MMAIN, "M-B", 0, to_prev_word, 0);
add_to_sclist(MMAIN, "M-D", 0, to_prev_word, 0);
add_to_sclist(MMAIN, "M-F", 0, to_next_word, 0);
add_to_sclist(MMAIN, "M-N", 0, to_next_word, 0);
#endif
add_to_sclist(MMOST, "M-Space", 0, do_prev_word_void, 0);
add_to_sclist(MMOST, "^Space", 0, do_next_word_void, 0);
add_to_sclist(MMOST, "M-Space", 0, to_prev_word, 0);
add_to_sclist(MMOST, "^Space", 0, to_next_word, 0);
add_to_sclist(MMOST, "^A", 0, do_home, 0);
add_to_sclist(MMOST, "Home", KEY_HOME, do_home, 0);
add_to_sclist(MMOST, "^E", 0, do_end, 0);
@@ -1254,23 +1246,23 @@ void shortcut_init(void)
if (using_utf8()) {
add_to_sclist(MMAIN|MBROWSER|MHELP, "\xE2\x96\xb2", KEY_UP, do_up, 0);
add_to_sclist(MMAIN|MBROWSER|MHELP, "\xE2\x96\xbc", KEY_DOWN, do_down, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^\xE2\x96\xb2", CONTROL_UP, do_prev_block, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^\xE2\x96\xbc", CONTROL_DOWN, do_next_block, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^\xE2\x96\xb2", CONTROL_UP, to_prev_block, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^\xE2\x96\xbc", CONTROL_DOWN, to_next_block, 0);
} else
#endif
{
add_to_sclist(MMAIN|MBROWSER|MHELP, "Up", KEY_UP, do_up, 0);
add_to_sclist(MMAIN|MBROWSER|MHELP, "Down", KEY_DOWN, do_down, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^Up", CONTROL_UP, do_prev_block, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^Down", CONTROL_DOWN, do_next_block, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^Up", CONTROL_UP, to_prev_block, 0);
add_to_sclist(MMAIN|MBROWSER|MLINTER, "^Down", CONTROL_DOWN, to_next_block, 0);
}
add_to_sclist(MMAIN, "M-7", 0, do_prev_block, 0);
add_to_sclist(MMAIN, "M-8", 0, do_next_block, 0);
add_to_sclist(MMAIN, "M-7", 0, to_prev_block, 0);
add_to_sclist(MMAIN, "M-8", 0, to_next_block, 0);
#ifdef ENABLE_JUSTIFY
add_to_sclist(MMAIN, "M-(", 0, do_para_begin_void, 0);
add_to_sclist(MMAIN, "M-9", 0, do_para_begin_void, 0);
add_to_sclist(MMAIN, "M-)", 0, do_para_end_void, 0);
add_to_sclist(MMAIN, "M-0", 0, do_para_end_void, 0);
add_to_sclist(MMAIN, "M-(", 0, to_para_begin, 0);
add_to_sclist(MMAIN, "M-9", 0, to_para_begin, 0);
add_to_sclist(MMAIN, "M-)", 0, to_para_end, 0);
add_to_sclist(MMAIN, "M-0", 0, to_para_end, 0);
#endif
#ifndef NANO_TINY
#ifdef ENABLE_UTF8
@@ -1339,7 +1331,7 @@ void shortcut_init(void)
#ifdef ENABLE_MOUSE
add_to_sclist(MMAIN, "M-M", 0, do_toggle_void, USE_MOUSE);
#endif
add_to_sclist(MMAIN, "M-Z", 0, do_toggle_void, SUSPEND);
add_to_sclist(MMAIN, "M-Z", 0, do_toggle_void, SUSPENDABLE);
#endif /* !NANO_TINY */
add_to_sclist(((MMOST & ~MMAIN) | MYESNO), "^C", 0, do_cancel, 0);
@@ -1364,8 +1356,8 @@ void shortcut_init(void)
}
#endif
#ifdef ENABLE_JUSTIFY
add_to_sclist(MGOTOLINE, "^W", 0, do_para_begin_void, 0);
add_to_sclist(MGOTOLINE, "^O", 0, do_para_end_void, 0);
add_to_sclist(MGOTOLINE, "^W", 0, to_para_begin, 0);
add_to_sclist(MGOTOLINE, "^O", 0, to_para_end, 0);
#endif
add_to_sclist(MGOTOLINE, "^Y", 0, to_first_line, 0);
add_to_sclist(MGOTOLINE, "^V", 0, to_last_line, 0);
@@ -1479,12 +1471,13 @@ const char *flagtostr(int flag)
return N_("Conversion of typed tabs to spaces");
case USE_MOUSE:
return N_("Mouse support");
case SUSPEND:
case SUSPENDABLE:
return N_("Suspension");
case LINE_NUMBERS:
return N_("Line numbering");
default:
return "Bad toggle -- please report a bug";
die("Bad toggle -- please report a bug\n");
return "";
}
}
#endif /* !NANO_TINY */

View File

@@ -44,7 +44,7 @@ void wrap_help_text_into_buffer(void)
{
size_t sum = 0;
/* Avoid overtight and overwide paragraphs in the introductory text. */
size_t wrapping_point = (COLS < 32) ? 32 : (COLS > 74) ? 74 : COLS;
size_t wrapping_point = (COLS < 40) ? 40 : (COLS > 74) ? 74 : COLS;
const char *ptr = start_of_body;
make_new_buffer();
@@ -55,10 +55,10 @@ void wrap_help_text_into_buffer(void)
char *oneline;
if (ptr == end_of_intro)
wrapping_point = (COLS < 32) ? 32 : COLS;
wrapping_point = (COLS < 40) ? 40 : COLS;
if (ptr > end_of_intro && *(ptr - 1) != '\n') {
length = break_line(ptr, (COLS < 32) ? 14 : COLS - 18, TRUE);
length = break_line(ptr, (COLS < 40) ? 22 : COLS - 18, TRUE);
oneline = nmalloc(length + 5);
snprintf(oneline, length + 5, "\t\t %s", ptr);
} else {
@@ -166,8 +166,7 @@ void show_help(void)
/* Extract the title from the head of the help text. */
length = break_line(help_text, MAX_BUF_SIZE, TRUE);
title = measured_copy(help_text, length + 1);
title[length] = '\0';
title = measured_copy(help_text, length);
titlebar(title);

View File

@@ -37,7 +37,7 @@ void to_first_line(void)
void to_last_line(void)
{
openfile->current = openfile->filebot;
openfile->current_x = strlen(openfile->filebot->data);
openfile->current_x = (inhelp) ? 0 : strlen(openfile->filebot->data);
openfile->placewewant = xplustabs();
/* Set the last line of the screen as the target for the cursor. */
@@ -192,7 +192,7 @@ void do_para_end(linestruct **line)
}
/* Move up to first start of a paragraph before the current line. */
void do_para_begin_void(void)
void to_para_begin(void)
{
linestruct *was_current = openfile->current;
@@ -203,7 +203,7 @@ void do_para_begin_void(void)
}
/* Move down to just after the first found end of a paragraph. */
void do_para_end_void(void)
void to_para_end(void)
{
linestruct *was_current = openfile->current;
@@ -222,7 +222,7 @@ void do_para_end_void(void)
#endif /* ENABLE_JUSTIFY */
/* Move to the preceding block of text. */
void do_prev_block(void)
void to_prev_block(void)
{
linestruct *was_current = openfile->current;
bool is_text = FALSE, seen_text = FALSE;
@@ -244,7 +244,7 @@ void do_prev_block(void)
}
/* Move to the next block of text. */
void do_next_block(void)
void to_next_block(void)
{
linestruct *was_current = openfile->current;
bool is_white = white_string(openfile->current->data);
@@ -281,7 +281,7 @@ void do_prev_word(bool allow_punct)
openfile->current_x = step_left(openfile->current->data,
openfile->current_x);
if (is_word_mbchar(openfile->current->data + openfile->current_x,
if (is_word_char(openfile->current->data + openfile->current_x,
allow_punct)) {
seen_a_word = TRUE;
/* If at the head of a line now, this surely is a word start. */
@@ -305,7 +305,7 @@ void do_prev_word(bool allow_punct)
* part of a word. Return TRUE if we started on a word, and FALSE otherwise. */
bool do_next_word(bool after_ends, bool allow_punct)
{
bool started_on_word = is_word_mbchar(openfile->current->data +
bool started_on_word = is_word_char(openfile->current->data +
openfile->current_x, allow_punct);
bool seen_space = !started_on_word;
#ifndef NANO_TINY
@@ -332,7 +332,7 @@ bool do_next_word(bool after_ends, bool allow_punct)
if (after_ends) {
/* If this is a word character, continue; else it's a separator,
* and if we've already seen a word, then it's a word end. */
if (is_word_mbchar(openfile->current->data + openfile->current_x,
if (is_word_char(openfile->current->data + openfile->current_x,
allow_punct))
seen_word = TRUE;
else if (seen_word)
@@ -342,7 +342,7 @@ bool do_next_word(bool after_ends, bool allow_punct)
{
/* If this is not a word character, then it's a separator; else
* if we've already seen a separator, then it's a word start. */
if (!is_word_mbchar(openfile->current->data + openfile->current_x,
if (!is_word_char(openfile->current->data + openfile->current_x,
allow_punct))
seen_space = TRUE;
else if (seen_space)
@@ -355,7 +355,7 @@ bool do_next_word(bool after_ends, bool allow_punct)
/* Move to the previous word in the file, treating punctuation as part of a
* word if the WORD_BOUNDS flag is set, and update the screen afterwards. */
void do_prev_word_void(void)
void to_prev_word(void)
{
linestruct *was_current = openfile->current;
@@ -367,7 +367,7 @@ void do_prev_word_void(void)
/* Move to the next word in the file. If the AFTER_ENDS flag is set, stop
* at word ends instead of beginnings. If the WORD_BOUNDS flag is set, treat
* punctuation as part of a word. Update the screen afterwards. */
void do_next_word_void(void)
void to_next_word(void)
{
linestruct *was_current = openfile->current;

View File

@@ -2,7 +2,7 @@
* nano.c -- This file is part of GNU nano. *
* *
* Copyright (C) 1999-2011, 2013-2020 Free Software Foundation, Inc. *
* Copyright (C) 2014-2019 Benno Schulenberg *
* Copyright (C) 2014-2020 Benno Schulenberg *
* *
* GNU nano is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published *
@@ -223,8 +223,7 @@ void partition_buffer(linestruct *top, size_t top_x,
* top of the partition from it, and save the text before top_x. */
foreline = top->prev;
top->prev = NULL;
antedata = measured_copy(top->data, top_x + 1);
antedata[top_x] = '\0';
antedata = measured_copy(top->data, top_x);
/* Remember which line is below the bottom of the partition, detach the
* bottom of the partition from it, and save the text after bot_x. */
@@ -340,9 +339,8 @@ void finish(void)
/* If the user wants history persistence, write the relevant files. */
if (ISSET(HISTORYLOG))
save_history();
if (ISSET(POSITIONLOG)) {
if (ISSET(POSITIONLOG) && openfile)
update_poshistory(openfile->filename, openfile->current->lineno, xplustabs() + 1);
}
#endif
/* Get out. */
@@ -671,9 +669,9 @@ void usage(void)
print_opt("-y", "--afterends", N_("Make Ctrl+Right stop at word ends"));
#endif
if (!ISSET(RESTRICTED))
print_opt("-z", "--suspend", N_("Enable suspension"));
print_opt("-z", "--suspendable", N_("Enable suspension"));
#ifndef NANO_TINY
print_opt("-$", "--softwrap", N_("Enable soft line wrapping"));
print_opt("-$", "--softwrap", N_("Display overlong lines on multiple rows"));
#endif
}
@@ -981,7 +979,7 @@ void signal_init(void)
sigaction(SIGWINCH, &deed, NULL);
#endif
if (ISSET(SUSPEND)) {
if (ISSET(SUSPENDABLE)) {
/* Block all other signals in the suspend and continue handlers.
* If we don't do this, other stuff interrupts them! */
sigfillset(&deed.sa_mask);
@@ -1054,7 +1052,7 @@ RETSIGTYPE do_suspend(int signal)
/* Put nano to sleep (if suspension is enabled). */
void do_suspend_void(void)
{
if (ISSET(SUSPEND))
if (ISSET(SUSPENDABLE))
do_suspend(0);
else {
statusbar(_("Suspension is not enabled"));
@@ -1153,7 +1151,7 @@ void do_toggle(int flag)
{
bool enabled;
if (flag == SUSPEND && in_restricted_mode())
if (flag == SUSPENDABLE && in_restricted_mode())
return;
TOGGLE(flag);
@@ -1169,7 +1167,7 @@ void do_toggle(int flag)
mouse_init();
break;
#endif
case SUSPEND:
case SUSPENDABLE:
signal_init();
break;
case SOFTWRAP:
@@ -1436,11 +1434,11 @@ bool wanted_to_move(void (*func)(void))
return func == do_left || func == do_right ||
func == do_up || func == do_down ||
func == do_home || func == do_end ||
func == do_prev_word_void || func == do_next_word_void ||
func == to_prev_word || func == to_next_word ||
#ifdef ENABLE_JUSTIFY
func == do_para_begin_void || func == do_para_end_void ||
func == to_para_begin || func == to_para_end ||
#endif
func == do_prev_block || func == do_next_block ||
func == to_prev_block || func == to_next_block ||
func == do_page_up || func == do_page_down ||
func == to_first_line || func == to_last_line;
}
@@ -1471,13 +1469,13 @@ void suck_up_input_and_paste_it(void)
while (bracketed_paste) {
int input = get_kbinput(edit, BLIND);
if (input == CR_CODE) {
if (input == '\r' || input == '\n') {
line->next = make_new_node(line);
line = line->next;
line->data = copy_of("");
index = 0;
} else if ((0x20 <= input && input <= 0xFF && input != DEL_CODE) ||
input == TAB_CODE) {
input == '\t') {
line->data = charealloc(line->data, index + 2);
line->data[index++] = (char)input;
line->data[index] = '\0';
@@ -1493,9 +1491,8 @@ void suck_up_input_and_paste_it(void)
}
#endif
/* Read in a keystroke. Act on the keystroke if it is a shortcut or a toggle;
* otherwise, insert it into the edit buffer. */
void do_input(void)
/* Read in a keystroke, and execute its command or insert it into the buffer. */
void process_a_keystroke(void)
{
int input;
/* The keystroke we read in: a character or a shortcut. */
@@ -1512,16 +1509,13 @@ void do_input(void)
if (input == KEY_WINCH)
return;
#endif
#ifdef ENABLE_MOUSE
if (input == KEY_MOUSE) {
/* We received a mouse click. */
/* If the user clicked on a shortcut, read in the key code that it was
* converted into. Else the click has been handled or was invalid. */
if (do_mouse() == 1)
/* The click was on a shortcut -- read in the character
* that it was converted into. */
input = get_kbinput(edit, BLIND);
else
/* The click was invalid or has been handled -- get out. */
return;
}
#endif
@@ -1529,20 +1523,11 @@ void do_input(void)
/* Check for a shortcut in the main list. */
shortcut = get_shortcut(&input);
/* If we got a non-high-bit control key, a meta key sequence, or a
* function key, and it's not a shortcut or toggle, throw it out. */
/* If not a command, discard anything that is not a normal character byte. */
if (shortcut == NULL) {
if (is_ascii_cntrl_char(input) || meta_key || !is_byte(input)) {
if (input < 0x20 || input > 0xFF || meta_key)
unbound_key(input);
input = ERR;
}
}
/* If the keystroke isn't a shortcut nor a toggle, it's a normal text
* character: add the character to the input buffer -- or display a
* warning when we're in view mode. */
if (input != ERR && shortcut == NULL) {
if (ISSET(VIEW_MODE))
else if (ISSET(VIEW_MODE))
print_view_warning();
else {
/* Store the byte, and leave room for a terminating zero. */
@@ -1557,22 +1542,16 @@ void do_input(void)
#endif
}
/* If we got a shortcut or toggle, or if there aren't any other
* characters waiting after the one we read in, we need to output
* all available characters in the input puddle. Note that this
* puddle will be empty if we're in view mode. */
if (shortcut || get_key_buffer_len() == 0) {
if (puddle != NULL) {
/* Insert all bytes in the input buffer into the edit buffer
* at once, filtering out any ASCII control codes. */
puddle[depth] = '\0';
inject(puddle, depth, TRUE);
/* 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 ((shortcut || get_key_buffer_len() == 0) && puddle != NULL) {
puddle[depth] = '\0';
/* Empty the input buffer. */
free(puddle);
puddle = NULL;
depth = 0;
}
inject(puddle, depth);
free(puddle);
puddle = NULL;
depth = 0;
}
if (shortcut == NULL) {
@@ -1655,103 +1634,87 @@ void do_input(void)
#endif
}
/* The user typed output_len multibyte characters. Add them to the edit
* buffer, filtering out ASCII control characters when filtering is TRUE. */
void inject(char *output, size_t output_len, bool filtering)
/* Insert the given short burst of bytes into the edit buffer. */
void inject(char *burst, size_t count)
{
char onechar[MAXCHARLEN];
int charlen;
size_t current_len = strlen(openfile->current->data);
size_t i = 0;
linestruct *thisline = openfile->current;
size_t datalen = strlen(thisline->data);
#ifndef NANO_TINY
size_t original_row = 0, old_amount = 0;
if (ISSET(SOFTWRAP)) {
if (openfile->current_y == editwinrows - 1)
original_row = chunk_for(xplustabs(), openfile->current);
old_amount = number_of_chunks_in(openfile->current);
original_row = chunk_for(xplustabs(), thisline);
old_amount = number_of_chunks_in(thisline);
}
#endif
while (i < output_len) {
/* Encode an embedded NUL byte as 0x0A. */
if (output[i] == '\0')
output[i] = '\n';
/* Get the next multibyte character. */
charlen = collect_char(output + i, onechar);
i += charlen;
/* If controls are not allowed, ignore an ASCII control character. */
if (filtering && is_ascii_cntrl_char(*(output + i - charlen)))
continue;
/* Make room for the new character and copy it into the line. */
openfile->current->data = charealloc(openfile->current->data,
current_len + charlen + 1);
memmove(openfile->current->data + openfile->current_x + charlen,
openfile->current->data + openfile->current_x,
current_len - openfile->current_x + 1);
strncpy(openfile->current->data + openfile->current_x, onechar,
charlen);
current_len += charlen;
openfile->totsize++;
set_modified();
/* Encode an embedded NUL byte as 0x0A. */
for (size_t index = 0; index < count; index++)
if (burst[index] == '\0')
burst[index] = '\n';
#ifndef NANO_TINY
/* Only add a new undo item when the current item is not an ADD or when
* the current typing is not contiguous with the previous typing. */
if (openfile->last_action != ADD ||
openfile->current_undo->mark_begin_lineno != openfile->current->lineno ||
openfile->current_undo->mark_begin_x != openfile->current_x)
add_undo(ADD, NULL);
/* Only add a new undo item when the current item is not an ADD or when
* the current typing is not contiguous with the previous typing. */
if (openfile->last_action != ADD ||
openfile->current_undo->tail_lineno != thisline->lineno ||
openfile->current_undo->tail_x != openfile->current_x)
add_undo(ADD, NULL);
#endif
/* Note that current_x has not yet been incremented. */
if (openfile->current == openfile->mark &&
openfile->current_x < openfile->mark_x)
openfile->mark_x += charlen;
/* Make room for the new bytes and copy them into the line. */
thisline->data = charealloc(thisline->data, datalen + count + 1);
memmove(thisline->data + openfile->current_x + count,
thisline->data + openfile->current_x,
datalen - openfile->current_x + 1);
strncpy(thisline->data + openfile->current_x, burst, count);
/* When the cursor is on the top row and not on the first chunk
* of a line, adding text there might change the preceding chunk
* and thus require an adjustment of firstcolumn. */
if (openfile->current == openfile->edittop &&
openfile->firstcolumn > 0) {
ensure_firstcolumn_is_aligned();
#ifndef NANO_TINY
/* When the mark is to the right of the cursor, compensate its position. */
if (thisline == openfile->mark && openfile->current_x < openfile->mark_x)
openfile->mark_x += count;
/* When the cursor is on the top row and not on the first chunk
* of a line, adding text there might change the preceding chunk
* and thus require an adjustment of firstcolumn. */
if (thisline == openfile->edittop && openfile->firstcolumn > 0) {
ensure_firstcolumn_is_aligned();
refresh_needed = TRUE;
}
#endif
/* If text was added to the magic line, create a new magic line. */
if (thisline == openfile->filebot && !ISSET(NO_NEWLINES)) {
new_magicline();
if (margin > 0)
refresh_needed = TRUE;
}
#endif
}
openfile->current_x += charlen;
openfile->current_x += count;
openfile->totsize += mbstrlen(burst);
set_modified();
#ifndef NANO_TINY
update_undo(ADD);
update_undo(ADD);
#endif
/* If we've added text to the magic line, create a new magic line. */
if (openfile->filebot == openfile->current && !ISSET(NO_NEWLINES)) {
new_magicline();
if (margin > 0)
refresh_needed = TRUE;
}
#ifdef ENABLE_WRAPPING
/* If text gets wrapped, the edit window needs a refresh. */
if (ISSET(BREAK_LONG_LINES) && do_wrap())
refresh_needed = TRUE;
/* Wrap the line when needed, and if so, schedule a refresh. */
if (ISSET(BREAK_LONG_LINES) && do_wrap())
refresh_needed = TRUE;
#endif
}
#ifndef NANO_TINY
/* If the number of screen rows that a softwrapped line occupies has
* changed, we need a full refresh. And if we were on the last line
* of the edit window, and we moved one screen row, we're now below
* the last line of the edit window, so we need a full refresh too. */
if (ISSET(SOFTWRAP) && refresh_needed == FALSE &&
(number_of_chunks_in(openfile->current) != old_amount ||
(openfile->current_y == editwinrows - 1 &&
chunk_for(xplustabs(), openfile->current) != original_row)))
/* If we were on the last row of the edit window and moved to a new chunk,
* or if the number of chunks that the current softwrapped line occupies
* changed, we need a full refresh. */
if (ISSET(SOFTWRAP) && ((openfile->current_y == editwinrows - 1 &&
chunk_for(xplustabs(), openfile->current) > original_row) ||
number_of_chunks_in(openfile->current) != old_amount)) {
refresh_needed = TRUE;
focusing = FALSE;
}
#endif
openfile->placewewant = xplustabs();
@@ -1760,7 +1723,6 @@ void inject(char *output, size_t output_len, bool filtering)
if (!refresh_needed)
check_the_multis(openfile->current);
#endif
if (!refresh_needed)
update_line(openfile->current, openfile->current_x);
}
@@ -1843,7 +1805,7 @@ int main(int argc, char **argv)
{"nowrap", 0, NULL, 'w'},
#endif
{"nohelp", 0, NULL, 'x'},
{"suspend", 0, NULL, 'z'},
{"suspendable", 0, NULL, 'z'},
#ifndef NANO_TINY
{"smarthome", 0, NULL, 'A'},
{"backup", 0, NULL, 'B'},
@@ -2150,7 +2112,7 @@ int main(int argc, char **argv)
break;
#endif
case 'z':
SET(SUSPEND);
SET(SUSPENDABLE);
break;
#ifndef NANO_TINY
case '$':
@@ -2277,7 +2239,7 @@ int main(int argc, char **argv)
* since they allow writing to files not specified on the command line. */
if (ISSET(RESTRICTED)) {
UNSET(BACKUP_FILE);
UNSET(SUSPEND);
UNSET(SUSPENDABLE);
#ifdef ENABLE_NANORC
UNSET(HISTORYLOG);
UNSET(POSITIONLOG);
@@ -2608,6 +2570,6 @@ int main(int argc, char **argv)
put_cursor_at_end_of_answer();
/* Read in and interpret a single keystroke. */
do_input();
process_a_keystroke();
}
}

View File

@@ -178,10 +178,12 @@ typedef enum {
/* Structure types. */
#ifdef ENABLE_COLOR
typedef struct colortype {
short id;
/* An ordinal number (if this color combo is for a multiline regex). */
short fg;
/* This syntax's foreground color. */
/* This combo's foreground color. */
short bg;
/* This syntax's background color. */
/* This combo's background color. */
short pairnum;
/* The pair number for this foreground/background color combination. */
int attributes;
@@ -191,9 +193,7 @@ typedef struct colortype {
regex_t *end;
/* The compiled regular expression for 'end=', if any. */
struct colortype *next;
/* Next set of colors. */
int id;
/* Basic id for assigning to lines later. */
/* Next color combination. */
} colortype;
typedef struct regexlisttype {
@@ -241,7 +241,7 @@ typedef struct syntaxtype {
#endif
colortype *color;
/* The colors and their regexes used in this syntax. */
int nmultis;
short nmultis;
/* How many multiline regex strings this syntax has. */
struct syntaxtype *next;
/* Next syntax. */
@@ -310,9 +310,9 @@ typedef struct undostruct {
/* The operation type that this undo item is for. */
int xflags;
/* Some flag data to mark certain corner cases. */
ssize_t lineno;
ssize_t head_lineno;
/* The line number where the operation began or ended. */
size_t begin;
size_t head_x;
/* The x position where the operation began or ended. */
char *strdata;
/* String data to help restore the affected line. */
@@ -324,9 +324,9 @@ typedef struct undostruct {
/* Undo info specific to groups of lines. */
linestruct *cutbuffer;
/* A copy of the cutbuffer. */
ssize_t mark_begin_lineno;
ssize_t tail_lineno;
/* Mostly the line number of the current line; sometimes something else. */
size_t mark_begin_x;
size_t tail_x;
/* The x position corresponding to the above line number. */
struct undostruct *next;
/* A pointer to the undo item of the preceding action. */
@@ -493,7 +493,7 @@ enum
CASE_SENSITIVE,
CONSTANT_SHOW,
NO_HELP,
SUSPEND,
SUSPENDABLE,
NO_WRAP,
AUTOINDENT,
VIEW_MODE,
@@ -565,9 +565,6 @@ enum
#endif
/* Basic control codes. */
#define BS_CODE 0x08
#define TAB_CODE 0x09
#define CR_CODE 0x0D
#define ESC_CODE 0x1B
#define DEL_CODE 0x7F
@@ -626,11 +623,11 @@ enum
#define KEY_WINCH -2
/* Some extra flags for the undo function. */
#define WAS_FINAL_BACKSPACE (1<<1)
#define WAS_BACKSPACE_AT_EOF (1<<1)
#define WAS_WHOLE_LINE (1<<2)
#define WAS_FINAL_LINE (1<<3)
#define INCLUDED_LAST_LINE (1<<3)
#define MARK_WAS_SET (1<<4)
#define WAS_MARKED_FORWARD (1<<5)
#define WAS_MARKED_BACKWARDS (1<<5)
#endif /* !NANO_TINY */
/* The default number of columns from end of line where wrapping occurs. */

View File

@@ -28,6 +28,132 @@ static char *prompt = NULL;
static size_t typing_x = HIGHEST_POSITIVE;
/* The cursor position in answer. */
/* Move to the beginning of the answer. */
void do_statusbar_home(void)
{
typing_x = 0;
}
/* Move to the end of the answer. */
void do_statusbar_end(void)
{
typing_x = strlen(answer);
}
#ifndef NANO_TINY
/* Move to the next word in the answer. */
void do_statusbar_next_word(void)
{
bool seen_space = !is_word_char(answer + typing_x, FALSE);
bool seen_word = !seen_space;
/* Move forward until we reach either the end or the start of a word,
* depending on whether the AFTER_ENDS flag is set or not. */
while (answer[typing_x] != '\0') {
typing_x = step_right(answer, typing_x);
if (ISSET(AFTER_ENDS)) {
/* If this is a word character, continue; else it's a separator,
* and if we've already seen a word, then it's a word end. */
if (is_word_char(answer + typing_x, FALSE))
seen_word = TRUE;
else if (seen_word)
break;
} else {
/* If this is not a word character, then it's a separator; else
* if we've already seen a separator, then it's a word start. */
if (!is_word_char(answer + typing_x, FALSE))
seen_space = TRUE;
else if (seen_space)
break;
}
}
}
/* Move to the previous word in the answer. */
void do_statusbar_prev_word(void)
{
bool seen_a_word = FALSE, step_forward = FALSE;
/* Move backward until we pass over the start of a word. */
while (typing_x != 0) {
typing_x = step_left(answer, typing_x);
if (is_word_char(answer + typing_x, FALSE))
seen_a_word = TRUE;
else if (seen_a_word) {
/* This is space now: we've overshot the start of the word. */
step_forward = TRUE;
break;
}
}
if (step_forward)
/* Move one character forward again to sit on the start of the word. */
typing_x = step_right(answer, typing_x);
}
#endif /* !NANO_TINY */
/* Move left one character in the answer. */
void do_statusbar_left(void)
{
if (typing_x > 0)
typing_x = step_left(answer, typing_x);
}
/* Move right one character in the answer. */
void do_statusbar_right(void)
{
if (answer[typing_x] != '\0')
typing_x = step_right(answer, typing_x);
}
/* Delete one character in the answer. */
void do_statusbar_delete(void)
{
if (answer[typing_x] != '\0') {
int charlen = char_length(answer + typing_x);
memmove(answer + typing_x, answer + typing_x + charlen,
strlen(answer) - typing_x - charlen + 1);
}
}
/* Backspace over one character in the answer. */
void do_statusbar_backspace(void)
{
if (typing_x > 0) {
typing_x = step_left(answer, typing_x);
do_statusbar_delete();
}
}
/* Zap some or all text from the answer. */
void do_statusbar_cut_text(void)
{
if (!ISSET(CUT_FROM_CURSOR))
typing_x = 0;
answer[typing_x] = '\0';
}
/* Paste the first line of the cutbuffer into the current answer. */
void paste_into_answer(void)
{
size_t pastelen = strlen(cutbuffer->data);
char *fusion = charalloc(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);
free(answer);
answer = fusion;
typing_x += pastelen;
}
#ifdef ENABLE_MOUSE
/* Handle a mouse click on the status-bar prompt or the shortcut list. */
int do_statusbar_mouse(void)
@@ -50,6 +176,35 @@ int do_statusbar_mouse(void)
}
#endif
/* Insert the given short burst of bytes into the answer. */
void inject_into_answer(char *burst, size_t count)
{
/* First encode any embedded NUL byte as 0x0A. */
for (size_t index = 0; index < count; index++)
if (burst[index] == '\0')
burst[index] = '\n';
answer = charealloc(answer, strlen(answer) + count + 1);
memmove(answer + typing_x + count, answer + typing_x,
strlen(answer) - typing_x + 1);
strncpy(answer + typing_x, burst , count);
typing_x += count;
}
/* Get a verbatim keystroke and insert it into the answer. */
void do_statusbar_verbatim_input(void)
{
char *bytes;
size_t count;
bytes = get_verbatim_kbinput(bottomwin, &count);
inject_into_answer(bytes, count);
free(bytes);
}
/* Read in a keystroke, interpret it if it is a shortcut or toggle, and
* return it. Set finished to TRUE if we're done after running
* or trying to run a function associated with a shortcut key. */
@@ -57,9 +212,9 @@ int do_statusbar_input(bool *finished)
{
int input;
/* The character we read in. */
static int *kbinput = NULL;
static char *puddle = NULL;
/* The input buffer. */
static size_t kbinput_len = 0;
static size_t depth = 0;
/* The length of the input buffer. */
const keystruct *shortcut;
@@ -87,40 +242,30 @@ int do_statusbar_input(bool *finished)
/* Check for a shortcut in the current list. */
shortcut = get_shortcut(&input);
/* If we got a non-high-bit control key, a meta key sequence, or a
* function key, and it's not a shortcut or toggle, throw it out. */
/* If not a command, discard anything that is not a normal character byte.
* Apart from that, only accept input when not in restricted mode, or when
* not at the "Write File" prompt, or when there is no filename yet. */
if (shortcut == NULL) {
if (is_ascii_cntrl_char(input) || meta_key || !is_byte(input)) {
if (input < 0x20 || input > 0xFF || meta_key)
beep();
input = ERR;
}
}
/* If the keystroke isn't a shortcut nor a toggle, it's a normal text
* character: add the it to the input buffer, when allowed. */
if (input != ERR && shortcut == NULL) {
/* Only accept input when not in restricted mode, or when not at
* the "Write File" prompt, or when there is no filename yet. */
if (!ISSET(RESTRICTED) || currmenu != MWRITEFILE ||
else if (!ISSET(RESTRICTED) || currmenu != MWRITEFILE ||
openfile->filename[0] == '\0') {
kbinput_len++;
kbinput = (int *)nrealloc(kbinput, kbinput_len * sizeof(int));
kbinput[kbinput_len - 1] = input;
puddle = charealloc(puddle, depth + 2);
puddle[depth++] = (char)input;
}
}
/* If we got a shortcut, or if there aren't any other keystrokes waiting
* after the one we read in, we need to insert all the characters in the
* input buffer (if not empty) into the answer. */
if ((shortcut || get_key_buffer_len() == 0) && kbinput != NULL) {
/* Inject all characters in the input buffer at once, filtering out
* control characters. */
inject_into_answer(kbinput, kbinput_len, TRUE);
/* 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 ((shortcut || get_key_buffer_len() == 0) && puddle != NULL) {
puddle[depth] = '\0';
/* Empty the input buffer. */
kbinput_len = 0;
free(kbinput);
kbinput = NULL;
inject_into_answer(puddle, depth);
free(puddle);
puddle = NULL;
depth = 0;
}
if (shortcut) {
@@ -136,9 +281,9 @@ int do_statusbar_input(bool *finished)
else if (shortcut->func == do_right)
do_statusbar_right();
#ifndef NANO_TINY
else if (shortcut->func == do_prev_word_void)
else if (shortcut->func == to_prev_word)
do_statusbar_prev_word();
else if (shortcut->func == do_next_word_void)
else if (shortcut->func == to_next_word)
do_statusbar_next_word();
#endif
else if (shortcut->func == do_home)
@@ -169,7 +314,7 @@ int do_statusbar_input(bool *finished)
do_statusbar_backspace();
else if (shortcut->func == paste_text) {
if (cutbuffer != NULL)
do_statusbar_uncut_text();
paste_into_answer();
} else {
/* Handle any other shortcut in the current menu, setting finished
* to TRUE to indicate that we're done after running or trying to
@@ -183,184 +328,6 @@ int do_statusbar_input(bool *finished)
return input;
}
/* The user typed input_len multibyte characters. Add them to the answer,
* filtering out ASCII control characters if filtering is TRUE. */
void inject_into_answer(int *the_input, size_t input_len, bool filtering)
{
char *output = charalloc(input_len + 1);
char onechar[MAXCHARLEN];
size_t charlen, i, j = 0;
/* Copy the typed stuff so it can be treated. */
for (i = 0; i < input_len; i++)
output[i] = (char)the_input[i];
output[i] = '\0';
while (j < input_len) {
/* Encode any NUL byte as 0x0A. */
if (output[j] == '\0')
output[j] = '\n';
/* Interpret the next multibyte character. */
charlen = collect_char(output + j, onechar);
j += charlen;
/* When filtering, skip any ASCII control character. */
if (filtering && is_ascii_cntrl_char(*(output + j - charlen)))
continue;
/* Insert the typed character into the existing answer string. */
answer = charealloc(answer, strlen(answer) + charlen + 1);
memmove(answer + typing_x + charlen, answer + typing_x,
strlen(answer) - typing_x + 1);
strncpy(answer + typing_x, onechar, charlen);
typing_x += charlen;
}
free(output);
}
/* Move to the beginning of the answer. */
void do_statusbar_home(void)
{
typing_x = 0;
}
/* Move to the end of the answer. */
void do_statusbar_end(void)
{
typing_x = strlen(answer);
}
/* Move left one character. */
void do_statusbar_left(void)
{
if (typing_x > 0)
typing_x = step_left(answer, typing_x);
}
/* Move right one character. */
void do_statusbar_right(void)
{
if (answer[typing_x] != '\0')
typing_x = step_right(answer, typing_x);
}
/* Delete one character. */
void do_statusbar_delete(void)
{
if (answer[typing_x] != '\0') {
int charlen = char_length(answer + typing_x);
memmove(answer + typing_x, answer + typing_x + charlen,
strlen(answer) - typing_x - charlen + 1);
}
}
/* Backspace over one character. */
void do_statusbar_backspace(void)
{
if (typing_x > 0) {
typing_x = step_left(answer, typing_x);
do_statusbar_delete();
}
}
/* Zap some or all text from the answer. */
void do_statusbar_cut_text(void)
{
if (!ISSET(CUT_FROM_CURSOR))
typing_x = 0;
answer[typing_x] = '\0';
}
#ifndef NANO_TINY
/* Move to the next word in the answer. */
void do_statusbar_next_word(void)
{
bool seen_space = !is_word_mbchar(answer + typing_x, FALSE);
bool seen_word = !seen_space;
/* Move forward until we reach either the end or the start of a word,
* depending on whether the AFTER_ENDS flag is set or not. */
while (answer[typing_x] != '\0') {
typing_x = step_right(answer, typing_x);
if (ISSET(AFTER_ENDS)) {
/* If this is a word character, continue; else it's a separator,
* and if we've already seen a word, then it's a word end. */
if (is_word_mbchar(answer + typing_x, FALSE))
seen_word = TRUE;
else if (seen_word)
break;
} else {
/* If this is not a word character, then it's a separator; else
* if we've already seen a separator, then it's a word start. */
if (!is_word_mbchar(answer + typing_x, FALSE))
seen_space = TRUE;
else if (seen_space)
break;
}
}
}
/* Move to the previous word in the answer. */
void do_statusbar_prev_word(void)
{
bool seen_a_word = FALSE, step_forward = FALSE;
/* Move backward until we pass over the start of a word. */
while (typing_x != 0) {
typing_x = step_left(answer, typing_x);
if (is_word_mbchar(answer + typing_x, FALSE))
seen_a_word = TRUE;
else if (seen_a_word) {
/* This is space now: we've overshot the start of the word. */
step_forward = TRUE;
break;
}
}
if (step_forward)
/* Move one character forward again to sit on the start of the word. */
typing_x = step_right(answer, typing_x);
}
#endif /* !NANO_TINY */
/* Get verbatim input and inject it into the answer, without filtering. */
void do_statusbar_verbatim_input(void)
{
int *kbinput;
size_t kbinput_len;
kbinput = get_verbatim_kbinput(bottomwin, &kbinput_len);
inject_into_answer(kbinput, kbinput_len, FALSE);
free(kbinput);
}
/* Paste the first line of the cutbuffer into the current answer. */
void do_statusbar_uncut_text(void)
{
size_t pastelen = strlen(cutbuffer->data);
char *fusion = charalloc(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);
free(answer);
answer = fusion;
typing_x += pastelen;
}
/* Return the column number of the first character of the answer that is
* displayed in the status bar when the cursor is at the given column,
* with the available room for the answer starting at base. Note that
@@ -493,7 +460,7 @@ functionptrtype acquire_an_answer(int *actual, bool allow_tabs,
if (func == do_tab) {
#ifdef ENABLE_HISTORIES
if (history_list != NULL) {
if (last_kbinput != the_code_for(do_tab, TAB_CODE))
if (last_kbinput != the_code_for(do_tab, '\t'))
complete_len = strlen(answer);
if (complete_len > 0) {

View File

@@ -203,13 +203,10 @@ char *strip_last_component(const char *path);
void utf8_init(void);
bool using_utf8(void);
#endif
char *addstrings(char* str1, size_t len1, char* str2, size_t len2);
bool is_byte(int c);
bool is_alpha_mbchar(const char *c);
bool is_blank_mbchar(const char *c);
bool is_ascii_cntrl_char(int c);
bool is_cntrl_mbchar(const char *c);
bool is_word_mbchar(const char *c, bool allow_punct);
bool is_alpha_char(const char *c);
bool is_blank_char(const char *c);
bool is_cntrl_char(const char *c);
bool is_word_char(const char *c, bool allow_punct);
char control_mbrep(const char *c, bool isdata);
#ifdef ENABLE_UTF8
int mbwidth(const char *c);
@@ -267,7 +264,6 @@ void copy_from_buffer(linestruct *somebuffer);
void cut_marked(bool *right_side_up);
#endif
void do_snip(bool copying, bool marked, bool until_eof, bool append);
bool is_cuttable(bool test_cliff);
void cut_text(void);
#ifndef NANO_TINY
void copy_text(void);
@@ -279,13 +275,9 @@ void paste_text(void);
/* Most functions in files.c. */
void make_new_buffer(void);
#ifndef NANO_TINY
int delete_lockfile(const char *lockfilename);
bool delete_lockfile(const char *lockfilename);
#endif
bool open_buffer(const char *filename, bool new_buffer);
#ifdef ENABLE_SPELLER
bool replace_buffer(const char *filename, undo_type action, bool marked,
const char *operation);
#endif
void set_modified(void);
void prepare_for_display(void);
#ifdef ENABLE_MULTIBUFFER
@@ -375,15 +367,15 @@ void do_page_down(void);
#ifdef ENABLE_JUSTIFY
void do_para_begin(linestruct **line);
void do_para_end(linestruct **line);
void do_para_begin_void(void);
void do_para_end_void(void);
void to_para_begin(void);
void to_para_end(void);
#endif
void do_prev_block(void);
void do_next_block(void);
void to_prev_block(void);
void to_next_block(void);
void do_prev_word(bool allow_punct);
bool do_next_word(bool after_ends, bool allow_punct);
void do_prev_word_void(void);
void do_next_word_void(void);
void to_prev_word(void);
void to_next_word(void);
void do_home(void);
void do_end(void);
void do_up(void);
@@ -444,23 +436,9 @@ void confirm_margin(void);
#endif
void unbound_key(int code);
bool okay_for_view(const keystruct *shortcut);
void inject(char *output, size_t output_len, bool filtering);
void inject(char *burst, size_t count);
/* Most functions in prompt.c. */
void inject_into_answer(int *the_input, size_t input_len, bool filtering);
void do_statusbar_home(void);
void do_statusbar_end(void);
void do_statusbar_left(void);
void do_statusbar_right(void);
void do_statusbar_backspace(void);
void do_statusbar_delete(void);
void do_statusbar_cut_text(void);
void do_statusbar_uncut_text(void);
#ifndef NANO_TINY
void do_statusbar_prev_word(void);
void do_statusbar_next_word(void);
#endif
void do_statusbar_verbatim_input(void);
size_t get_statusbar_page_start(size_t start_col, size_t column);
void put_cursor_at_end_of_answer(void);
void add_or_remove_pipe_symbol_from_answer(void);
@@ -524,9 +502,7 @@ void do_undo(void);
void do_redo(void);
void do_enter(void);
#ifndef NANO_TINY
RETSIGTYPE cancel_command(int signal);
bool execute_command(const char *command);
void discard_until(const undostruct *thisitem, openfilestruct *thefile, bool keep);
void discard_until(const undostruct *thisitem);
void add_undo(undo_type action, const char *message);
void update_multiline_undo(ssize_t lineno, char *indentation);
void update_undo(undo_type action);
@@ -596,6 +572,7 @@ void new_magicline(void);
void remove_magicline(void);
#endif
#ifndef NANO_TINY
bool mark_is_before_cursor(void);
void get_region(const linestruct **top, size_t *top_x,
const linestruct **bot, size_t *bot_x, bool *right_side_up);
void get_range(const linestruct **top, const linestruct **bot);
@@ -616,8 +593,7 @@ int parse_kbinput(WINDOW *win);
int get_kbinput(WINDOW *win, bool showcursor);
int get_byte_kbinput(int kbinput);
int get_control_kbinput(int kbinput);
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len);
int *parse_verbatim_kbinput(WINDOW *win, size_t *count);
char *get_verbatim_kbinput(WINDOW *win, size_t *count);
#ifdef ENABLE_MOUSE
int get_mouseinput(int *mouse_row, int *mouse_col, bool allow_shortcuts);
#endif

View File

@@ -4,7 +4,7 @@
* Copyright (C) 2001-2011, 2013-2020 Free Software Foundation, Inc. *
* Copyright (C) 2014 Mike Frysinger *
* Copyright (C) 2019 Brand Huntsman *
* Copyright (C) 2014-2019 Benno Schulenberg *
* Copyright (C) 2014-2020 Benno Schulenberg *
* *
* GNU nano is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published *
@@ -89,7 +89,8 @@ static const rcoption rcopts[] = {
#ifdef ENABLE_SPELLER
{"speller", 0},
#endif
{"suspend", SUSPEND},
{"suspend", SUSPENDABLE}, /* Deprecated; remove in 2022. */
{"suspendable", SUSPENDABLE},
{"tabsize", 0},
{"tempfile", TEMP_FILE},
{"view", VIEW_MODE},
@@ -264,7 +265,7 @@ keystruct *strtosc(const char *input)
#endif
#ifdef ENABLE_SPELLER
else if (!strcmp(input, "tospell") ||
!strcmp(input, "speller"))
!strcmp(input, "speller"))
s->func = do_spell;
#endif
#ifdef ENABLE_COLOR
@@ -285,9 +286,9 @@ keystruct *strtosc(const char *input)
else if (!strcmp(input, "fulljustify"))
s->func = do_full_justify;
else if (!strcmp(input, "beginpara"))
s->func = do_para_begin_void;
s->func = to_para_begin;
else if (!strcmp(input, "endpara"))
s->func = do_para_end_void;
s->func = to_para_end;
#endif
#ifdef ENABLE_COMMENT
else if (!strcmp(input, "comment"))
@@ -303,10 +304,10 @@ keystruct *strtosc(const char *input)
else if (!strcmp(input, "unindent"))
s->func = do_unindent;
else if (!strcmp(input, "chopwordleft") ||
!strcmp(input, "cutwordleft")) /* Deprecated; remove in 2021. */
!strcmp(input, "cutwordleft")) /* Deprecated; remove in 2021. */
s->func = chop_previous_word;
else if (!strcmp(input, "chopwordright") ||
!strcmp(input, "cutwordright")) /* Deprecated; remove in 2021. */
!strcmp(input, "cutwordright")) /* Deprecated; remove in 2021. */
s->func = chop_next_word;
else if (!strcmp(input, "findbracket"))
s->func = do_find_bracket;
@@ -322,16 +323,16 @@ keystruct *strtosc(const char *input)
s->func = do_redo;
#endif
else if (!strcmp(input, "left") ||
!strcmp(input, "back"))
!strcmp(input, "back"))
s->func = do_left;
else if (!strcmp(input, "right") ||
!strcmp(input, "forward"))
!strcmp(input, "forward"))
s->func = do_right;
else if (!strcmp(input, "up") ||
!strcmp(input, "prevline"))
!strcmp(input, "prevline"))
s->func = do_up;
else if (!strcmp(input, "down") ||
!strcmp(input, "nextline"))
!strcmp(input, "nextline"))
s->func = do_down;
#if !defined(NANO_TINY) || defined(ENABLE_HELP)
else if (!strcmp(input, "scrollup"))
@@ -340,22 +341,22 @@ keystruct *strtosc(const char *input)
s->func = do_scroll_down;
#endif
else if (!strcmp(input, "prevword"))
s->func = do_prev_word_void;
s->func = to_prev_word;
else if (!strcmp(input, "nextword"))
s->func = do_next_word_void;
s->func = to_next_word;
else if (!strcmp(input, "home"))
s->func = do_home;
else if (!strcmp(input, "end"))
s->func = do_end;
else if (!strcmp(input, "prevblock"))
s->func = do_prev_block;
s->func = to_prev_block;
else if (!strcmp(input, "nextblock"))
s->func = do_next_block;
s->func = to_next_block;
else if (!strcmp(input, "pageup") ||
!strcmp(input, "prevpage"))
!strcmp(input, "prevpage"))
s->func = do_page_up;
else if (!strcmp(input, "pagedown") ||
!strcmp(input, "nextpage"))
!strcmp(input, "nextpage"))
s->func = do_page_down;
else if (!strcmp(input, "firstline"))
s->func = to_first_line;
@@ -421,7 +422,7 @@ keystruct *strtosc(const char *input)
#endif
#ifdef ENABLE_BROWSER
else if (!strcmp(input, "tofiles") ||
!strcmp(input, "browser"))
!strcmp(input, "browser"))
s->func = to_files;
else if (!strcmp(input, "gotodir"))
s->func = goto_dir;
@@ -465,8 +466,9 @@ keystruct *strtosc(const char *input)
else if (!strcmp(input, "mouse"))
s->toggle = USE_MOUSE;
#endif
else if (!strcmp(input, "suspendenable"))
s->toggle = SUSPEND;
else if (!strcmp(input, "suspendable") ||
!strcmp(input, "suspendenable")) /* Deprecated; remove in 2022. */
s->toggle = SUSPENDABLE;
else
#endif /* !NANO_TINY */
{
@@ -553,14 +555,21 @@ char *parse_argument(char *ptr)
}
#ifdef ENABLE_COLOR
/* Pass over the current regex string in the line starting at ptr,
* null-terminate it, and return a pointer to the /next/ word. */
/* Advance over one regular expression in the line starting at ptr,
* null-terminate it, and return a pointer to the succeeding text. */
char *parse_next_regex(char *ptr)
{
/* Continue until the end of line, or until a " followed by a
* blank character or the end of line. */
char *starting_point = ptr;
if (*(ptr - 1) != '"') {
jot_error(N_("Regex strings must begin and end with a \" character"));
return NULL;
}
/* Continue until the end of the line, or until a double quote followed
* by end-of-line or a blank. */
while (*ptr != '\0' && (*ptr != '"' ||
(*(ptr + 1) != '\0' && !isblank((unsigned char)ptr[1]))))
(ptr[1] != '\0' && !isblank((unsigned char)ptr[1]))))
ptr++;
if (*ptr == '\0') {
@@ -568,7 +577,12 @@ char *parse_next_regex(char *ptr)
return NULL;
}
/* Null-terminate and advance ptr. */
if (ptr == starting_point) {
jot_error(N_("Empty regex string"));
return NULL;
}
/* Null-terminate the regex and skip until the next non-blank. */
*ptr++ = '\0';
while (isblank((unsigned char)*ptr))
@@ -694,7 +708,7 @@ bool is_universal(void (*func)(void))
return (func == do_left || func == do_right ||
func == do_home || func == do_end ||
#ifndef NANO_TINY
func == do_prev_word_void || func == do_next_word_void ||
func == to_prev_word || func == to_next_word ||
#endif
func == do_delete || func == do_backspace ||
func == cut_text || func == paste_text ||
@@ -993,7 +1007,7 @@ short color_to_short(const char *colorname, bool *bright)
/* Parse the color name (or pair of color names) in the given string.
* Return FALSE when any color name is invalid; otherwise return TRUE. */
bool parse_color_names(char *combostr, short *fg, short *bg, int *attributes)
bool parse_combination(char *combostr, short *fg, short *bg, int *attributes)
{
char *comma = strchr(combostr, ',');
bool bright;
@@ -1025,23 +1039,24 @@ bool parse_color_names(char *combostr, short *fg, short *bg, int *attributes)
return TRUE;
}
/* Parse the color string in the line at ptr, and add it to the current
* file's associated colors. rex_flags are the regex compilation flags
* to use, excluding or including REG_ICASE for case (in)sensitivity. */
void parse_colors(char *ptr, int rex_flags)
/* Parse the color specification that starts at ptr, and then the one or more
* regexes that follow it. For each valid regex (or start=/end= regex pair),
* add a rule to the current syntax. */
void parse_rule(char *ptr, int rex_flags)
{
char *names, *regexstring;
short fg, bg;
int attributes;
char *item;
if (*ptr == '\0') {
jot_error(N_("Missing color name"));
return;
}
item = ptr;
names = ptr;
ptr = parse_next_word(ptr);
if (!parse_color_names(item, &fg, &bg, &attributes))
if (!parse_combination(names, &fg, &bg, &attributes))
return;
if (*ptr == '\0') {
@@ -1049,93 +1064,68 @@ void parse_colors(char *ptr, int rex_flags)
return;
}
/* Now for the fun part. Start adding regexes to individual strings
* in the colorstrings array, woo! */
while (ptr != NULL && *ptr != '\0') {
while (*ptr != '\0') {
regex_t *start_rgx = NULL, *end_rgx = NULL;
/* Intermediate storage for compiled regular expressions. */
colortype *newcolor = NULL;
/* The container for a color plus its regexes. */
bool goodstart;
/* Whether the start expression was valid. */
/* Container for compiled regex (pair) and the color it paints. */
bool expectend = FALSE;
/* Whether to expect an end= line. */
/* Whether it is a start=/end= regex pair. */
if (strncmp(ptr, "start=", 6) == 0) {
ptr += 6;
expectend = TRUE;
}
if (*ptr != '"') {
jot_error(N_("Regex strings must begin and end with a \" character"));
ptr = parse_next_regex(ptr);
continue;
}
item = ++ptr;
regexstring = ++ptr;
ptr = parse_next_regex(ptr);
if (ptr == NULL)
break;
if (*item == '\0') {
jot_error(N_("Empty regex string"));
goodstart = FALSE;
} else {
newcolor = (colortype *)nmalloc(sizeof(colortype));
goodstart = compile(item, rex_flags, &newcolor->start);
}
/* If the start regex is valid, fill in the rest of the data, and
* hook the new color struct in at the tail of the linked list. */
if (goodstart) {
newcolor->fg = fg;
newcolor->bg = bg;
newcolor->attributes = attributes;
newcolor->end = NULL;
newcolor->next = NULL;
if (lastcolor == NULL)
live_syntax->color = newcolor;
else
lastcolor->next = newcolor;
lastcolor = newcolor;
} else
free(newcolor);
if (!expectend)
continue;
if (ptr == NULL || strncmp(ptr, "end=", 4) != 0) {
jot_error(N_("\"start=\" requires a corresponding \"end=\""));
/* When there is no regex, or it is invalid, skip this line. */
if (ptr == NULL || !compile(regexstring, rex_flags, &start_rgx))
return;
if (expectend) {
if (strncmp(ptr, "end=", 4) != 0) {
jot_error(N_("\"start=\" requires a corresponding \"end=\""));
regfree(start_rgx);
free(start_rgx);
return;
}
regexstring = ptr + 5;
ptr = parse_next_regex(ptr + 5);
/* When there is no valid end= regex, abandon the rule. */
if (ptr == NULL || !compile(regexstring, rex_flags, &end_rgx)) {
regfree(start_rgx);
free(start_rgx);
return;
}
}
ptr += 4;
if (*ptr != '"') {
jot_error(N_("Regex strings must begin and end with a \" character"));
continue;
/* Allocate a rule, fill in the data, and link it into the list. */
newcolor = (colortype *)nmalloc(sizeof(colortype));
newcolor->start = start_rgx;
newcolor->end = end_rgx;
newcolor->fg = fg;
newcolor->bg = bg;
newcolor->attributes = attributes;
if (lastcolor == NULL)
live_syntax->color = newcolor;
else
lastcolor->next = newcolor;
newcolor->next = NULL;
lastcolor = newcolor;
/* For a multiline rule, give it a number and increase the count. */
if (expectend) {
newcolor->id = live_syntax->nmultis;
live_syntax->nmultis++;
}
item = ++ptr;
ptr = parse_next_regex(ptr);
if (ptr == NULL)
break;
if (*item == '\0') {
jot_error(N_("Empty regex string"));
continue;
}
/* If the start regex was invalid, the end regex cannot be saved. */
if (!goodstart)
continue;
/* Save the compiled ending regex (when it's valid). */
compile(item, rex_flags, &newcolor->end);
/* Lame way to skip another static counter. */
newcolor->id = live_syntax->nmultis;
live_syntax->nmultis++;
}
}
@@ -1144,7 +1134,7 @@ colortype *parse_interface_color(char *combostr)
{
colortype *trio = nmalloc(sizeof(colortype));
if (parse_color_names(combostr, &trio->fg, &trio->bg, &trio->attributes)) {
if (parse_combination(combostr, &trio->fg, &trio->bg, &trio->attributes)) {
free(combostr);
return trio;
} else {
@@ -1158,7 +1148,8 @@ colortype *parse_interface_color(char *combostr)
* by ptr, and store them quoteless in the passed storage place. */
void grab_and_store(const char *kind, char *ptr, regexlisttype **storage)
{
regexlisttype *lastthing;
regexlisttype *lastthing, *newthing;
const char *regexstring;
if (!opensyntax) {
jot_error(N_("A '%s' command requires a preceding 'syntax' command"), kind);
@@ -1184,16 +1175,9 @@ void grab_and_store(const char *kind, char *ptr, regexlisttype **storage)
/* Now gather any valid regexes and add them to the linked list. */
while (*ptr != '\0') {
const char *regexstring;
regexlisttype *newthing;
if (*ptr != '"') {
jot_error(N_("Regex strings must begin and end with a \" character"));
return;
}
regexstring = ++ptr;
ptr = parse_next_regex(ptr);
if (ptr == NULL)
return;
@@ -1245,9 +1229,9 @@ void pick_up_name(const char *kind, char *ptr, char **storage)
bool parse_syntax_commands(char *keyword, char *ptr)
{
if (strcmp(keyword, "color") == 0)
parse_colors(ptr, NANO_REG_EXTENDED);
parse_rule(ptr, NANO_REG_EXTENDED);
else if (strcmp(keyword, "icolor") == 0)
parse_colors(ptr, NANO_REG_EXTENDED | REG_ICASE);
parse_rule(ptr, NANO_REG_EXTENDED | REG_ICASE);
else if (strcmp(keyword, "comment") == 0) {
#ifdef ENABLE_COMMENT
pick_up_name("comment", ptr, &live_syntax->comment);
@@ -1299,10 +1283,10 @@ static void check_vitals_mapped(void)
void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
{
char *buffer = NULL;
ssize_t len;
size_t size = 0;
ssize_t length;
while ((len = getline(&buffer, &size, rcstream)) > 0) {
while ((length = getline(&buffer, &size, rcstream)) > 0) {
char *ptr, *keyword, *option, *argument;
#ifdef ENABLE_COLOR
bool drop_open = FALSE;
@@ -1317,9 +1301,11 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
if (just_syntax && !intros_only && lineno <= live_syntax->lineno)
continue;
#endif
/* Strip the terminating newline, if any. */
if (buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
/* Strip the terminating newline and possibly a carriage return. */
if (buffer[length - 1] == '\n')
buffer[--length] = '\0';
if (length > 0 && buffer[length - 1] == '\r')
buffer[--length] = '\0';
ptr = buffer;
while (isblank((unsigned char)*ptr))
@@ -1338,17 +1324,17 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
if (!just_syntax && strcmp(keyword, "extendsyntax") == 0) {
augmentstruct *newitem, *extra;
char *syntaxname = ptr;
syntaxtype *sint;
syntaxtype *sntx;
check_for_nonempty_syntax();
ptr = parse_next_word(ptr);
for (sint = syntaxes; sint != NULL; sint = sint->next)
if (!strcmp(sint->name, syntaxname))
for (sntx = syntaxes; sntx != NULL; sntx = sntx->next)
if (!strcmp(sntx->name, syntaxname))
break;
if (sint == NULL) {
if (sntx == NULL) {
jot_error(N_("Could not find syntax \"%s\" to extend"), syntaxname);
continue;
}
@@ -1361,7 +1347,7 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
* other commands are stored for possible later processing. */
if (strcmp(keyword, "header") == 0 || strcmp(keyword, "magic") == 0) {
free(argument);
live_syntax = sint;
live_syntax = sntx;
opensyntax = TRUE;
drop_open = TRUE;
} else {
@@ -1372,13 +1358,13 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
newitem->data = argument;
newitem->next = NULL;
if (sint->augmentations != NULL) {
extra = sint->augmentations;
if (sntx->augmentations != NULL) {
extra = sntx->augmentations;
while (extra->next != NULL)
extra = extra->next;
extra->next = newitem;
} else
sint->augmentations = newitem;
sntx->augmentations = newitem;
continue;
}

View File

@@ -519,8 +519,6 @@ ssize_t do_replace_loop(const char *needle, bool whole_word_only,
linestruct *top, *bot;
size_t top_x, bot_x;
bool right_side_up = FALSE;
/* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
* FALSE if (current, current_x) is. */
/* If the mark is on, frame the region, and turn the mark off. */
if (openfile->mark) {

1401
src/text.c

File diff suppressed because it is too large Load Diff

View File

@@ -209,8 +209,8 @@ bool is_separate_word(size_t position, size_t length, const char *buf)
/* If the word starts at the beginning of the line OR the character before
* the word isn't a letter, and if the word ends at the end of the line OR
* the character after the word isn't a letter, we have a whole word. */
return ((position == 0 || !is_alpha_mbchar(before)) &&
(buf[word_end] == '\0' || !is_alpha_mbchar(after)));
return ((position == 0 || !is_alpha_char(before)) &&
(buf[word_end] == '\0' || !is_alpha_char(after)));
}
#endif /* ENABLE_SPELLER */
@@ -324,12 +324,14 @@ char *mallocstrcpy(char *dest, const char *src)
return dest;
}
/* Return an allocated copy of the first count characters of the given string. */
/* Return an allocated copy of the first count characters
* of the given string, and NUL-terminate the copy. */
char *measured_copy(const char *string, size_t count)
{
char *thecopy = charalloc(count);
char *thecopy = charalloc(count + 1);
strncpy(thecopy, string, count);
thecopy[count] = '\0';
return thecopy;
}
@@ -337,7 +339,7 @@ char *measured_copy(const char *string, size_t count)
/* Return an allocated copy of the given string. */
char *copy_of(const char *string)
{
return measured_copy(string, strlen(string) + 1);
return measured_copy(string, strlen(string));
}
/* Free the string at dest and return the string at src. */
@@ -448,15 +450,21 @@ void remove_magicline(void)
#endif
#ifndef NANO_TINY
/* Return TRUE when the mark is before or at the cursor, and FALSE otherwise. */
bool mark_is_before_cursor(void)
{
return (openfile->mark->lineno < openfile->current->lineno ||
(openfile->mark == openfile->current &&
openfile->mark_x <= openfile->current_x));
}
/* Return in (top, top_x) and (bot, bot_x) the start and end "coordinates"
* of the marked region. If right_side_up isn't NULL, set it to TRUE when
* the mark is at the top of the marked region, and to FALSE otherwise. */
void get_region(const linestruct **top, size_t *top_x,
const linestruct **bot, size_t *bot_x, bool *right_side_up)
{
if (openfile->mark->lineno < openfile->current->lineno ||
(openfile->mark == openfile->current &&
openfile->mark_x < openfile->current_x)) {
if (mark_is_before_cursor()) {
*top = openfile->mark;
*top_x = openfile->mark_x;
*bot = openfile->current;

View File

@@ -930,7 +930,7 @@ int parse_kbinput(WINDOW *win)
case 1:
if (keycode >= 0x80)
retval = keycode;
else if (keycode == TAB_CODE)
else if (keycode == '\t')
retval = SHIFT_TAB;
else if ((keycode != 'O' && keycode != 'o' && keycode != '[') ||
key_buffer_len == 0 || *key_buffer == ESC_CODE) {
@@ -1144,7 +1144,7 @@ int parse_kbinput(WINDOW *win)
}
/* Is Shift being held? */
if (modifiers & 0x01) {
if (retval == TAB_CODE)
if (retval == '\t')
return SHIFT_TAB;
if (!meta_key)
shift_held = TRUE;
@@ -1194,7 +1194,7 @@ int parse_kbinput(WINDOW *win)
#ifndef NANO_TINY
/* When <Tab> is pressed while the mark is on, do an indent. */
if (retval == TAB_CODE && openfile->mark && currmenu == MMAIN &&
if (retval == '\t' && openfile->mark && currmenu == MMAIN &&
!bracketed_paste && openfile->mark != openfile->current)
return INDENT_KEY;
#endif
@@ -1502,44 +1502,6 @@ int get_control_kbinput(int kbinput)
return kbinput;
}
/* Read in a stream of characters verbatim, and return the length of the
* string in kbinput_len. Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
int *retval;
/* Turn off flow control characters if necessary so that we can type
* them in verbatim, and turn the keypad off if necessary so that we
* don't get extended keypad values. */
if (ISSET(PRESERVE))
disable_flow_control();
if (!ISSET(RAW_SEQUENCES))
keypad(win, FALSE);
/* Read in one keycode, or one or two escapes. */
retval = parse_verbatim_kbinput(win, kbinput_len);
/* If the code is invalid in the current mode, discard it. */
if (retval != NULL && ((*retval == '\n' && as_an_at) ||
(*retval == '\0' && !as_an_at))) {
*kbinput_len = 0;
beep();
}
/* Turn flow control characters back on if necessary and turn the
* keypad back on if necessary now that we're done. */
if (ISSET(PRESERVE))
enable_flow_control();
/* Use the global window pointers, because a resize may have freed
* the data that the win parameter points to. */
if (!ISSET(RAW_SEQUENCES)) {
keypad(edit, TRUE);
keypad(bottomwin, TRUE);
}
return retval;
}
/* Read in one control character (or an iTerm/Eterm/rxvt double Escape),
* or convert a series of six digits into a Unicode codepoint. Return
* in count either 1 (for a control character or the first byte of a
@@ -1577,7 +1539,7 @@ int *parse_verbatim_kbinput(WINDOW *win, size_t *count)
* Unicode value, and put back the corresponding byte(s). */
else {
char *multibyte;
int onebyte, i;
int onebyte;
reveal_cursor = FALSE;
@@ -1592,7 +1554,7 @@ int *parse_verbatim_kbinput(WINDOW *win, size_t *count)
multibyte = make_mbchar(unicode, (int *)count);
/* Insert the multibyte sequence into the input buffer. */
for (i = *count; i > 0 ; i--) {
for (size_t i = *count; i > 0 ; i--) {
onebyte = (unsigned char)multibyte[i - 1];
put_back(onebyte);
}
@@ -1614,6 +1576,56 @@ int *parse_verbatim_kbinput(WINDOW *win, size_t *count)
return get_input(NULL, *count);
}
/* Read in one control code, one character byte, or the leading escapes of
* an escape sequence, and return the resulting number of bytes in count. */
char *get_verbatim_kbinput(WINDOW *win, size_t *count)
{
char *bytes = charalloc(MAXCHARLEN + 1);
int *input;
/* Turn off flow control characters if necessary so that we can type
* them in verbatim, and turn the keypad off if necessary so that we
* don't get extended keypad values. */
if (ISSET(PRESERVE))
disable_flow_control();
if (!ISSET(RAW_SEQUENCES))
keypad(win, FALSE);
/* Read in a single byte or two escapes. */
input = parse_verbatim_kbinput(win, count);
/* If the byte is invalid in the current mode, discard it;
* if it is an incomplete Unicode sequence, stuff it back. */
if (input != NULL) {
if ((*input == '\n' && as_an_at) || (*input == '\0' && !as_an_at)) {
*count = 0;
beep();
} else if (*input >= 0x80 && *count == 1) {
put_back(*input);
*count = 0;
}
}
/* Turn flow control characters back on if necessary and turn the
* keypad back on if necessary now that we're done. */
if (ISSET(PRESERVE))
enable_flow_control();
/* Use the global window pointers, because a resize may have freed
* the data that the win parameter points to. */
if (!ISSET(RAW_SEQUENCES)) {
keypad(edit, TRUE);
keypad(bottomwin, TRUE);
}
for (size_t i = 0; i < *count; i++)
bytes[i] = (char)input[i];
bytes[*count] = '\0';
free(input);
return bytes;
}
#ifdef ENABLE_MOUSE
/* Handle any mouse event that may have occurred. We currently handle
* releases/clicks of the first mouse button. If allow_shortcuts is
@@ -1845,7 +1857,7 @@ char *display_string(const char *buf, size_t column, size_t span,
* overwritten by a "<" token, then show placeholders instead. */
if (*buf != '\0' && *buf != '\t' && (start_col < column ||
(start_col > 0 && isdata && !ISSET(SOFTWRAP)))) {
if (is_cntrl_mbchar(buf)) {
if (is_cntrl_char(buf)) {
if (start_col < column) {
converted[index++] = control_mbrep(buf, isdata);
column++;
@@ -1919,7 +1931,7 @@ char *display_string(const char *buf, size_t column, size_t span,
}
/* Represent a control character with a leading caret. */
if (is_cntrl_mbchar(buf)) {
if (is_cntrl_char(buf)) {
converted[index++] = '^';
converted[index++] = control_mbrep(buf, isdata);
buf += char_length(buf);
@@ -2264,16 +2276,37 @@ void warn_and_shortly_pause(const char *msg)
{
blank_bottombars();
statusline(ALERT, msg);
lastmessage = HUSH;
napms(1500);
}
/* Write a key's representation plus a minute description of its function
* to the screen. For example, the key could be "^C" and its tag "Cancel".
* Key plus tag may occupy at most width columns. */
void post_one_key(const char *keystroke, const char *tag, int width)
{
wattron(bottomwin, interface_color_pair[KEY_COMBO]);
waddnstr(bottomwin, keystroke, actual_x(keystroke, width));
wattroff(bottomwin, interface_color_pair[KEY_COMBO]);
/* If the remaning space is too small, skip the description. */
width -= breadth(keystroke);
if (width < 2)
return;
waddch(bottomwin, ' ');
wattron(bottomwin, interface_color_pair[FUNCTION_TAG]);
waddnstr(bottomwin, tag, actual_x(tag, width - 1));
wattroff(bottomwin, interface_color_pair[FUNCTION_TAG]);
}
/* Display the shortcut list corresponding to menu on the last two rows
* of the bottom portion of the window. */
void bottombars(int menu)
{
size_t number, itemwidth, i;
funcstruct *f;
size_t index, number, itemwidth;
const keystruct *s;
funcstruct *f;
/* Set the global variable to the given menu. */
currmenu = menu;
@@ -2295,18 +2328,20 @@ void bottombars(int menu)
/* Display the first number of shortcuts in the given menu that
* have a key combination assigned to them. */
for (f = allfuncs, i = 0; i < number && f != NULL; f = f->next) {
for (f = allfuncs, index = 0; f != NULL && index < number; f = f->next) {
if ((f->menus & menu) == 0)
continue;
s = first_sc_for(menu, f->func);
if (s == NULL)
continue;
wmove(bottomwin, 1 + i % 2, (i / 2) * itemwidth);
wmove(bottomwin, 1 + index % 2, (index / 2) * itemwidth);
post_one_key(s->keystr, _(f->desc), itemwidth + (COLS % itemwidth));
i++;
post_one_key(s->keystr, _(f->desc), itemwidth +
((index < number - 2) ? 0 : COLS % itemwidth));
index++;
}
/* Defeat a VTE bug by homing the cursor and forcing a screen update. */
@@ -2314,26 +2349,6 @@ void bottombars(int menu)
wrefresh(bottomwin);
}
/* Write a key's representation plus a minute description of its function
* to the screen. For example, the key could be "^C" and its tag "Cancel".
* Key plus tag may occupy at most width columns. */
void post_one_key(const char *keystroke, const char *tag, int width)
{
wattron(bottomwin, interface_color_pair[KEY_COMBO]);
waddnstr(bottomwin, keystroke, actual_x(keystroke, width));
wattroff(bottomwin, interface_color_pair[KEY_COMBO]);
/* If the remaning space is too small, skip the description. */
width -= breadth(keystroke);
if (width < 2)
return;
waddch(bottomwin, ' ');
wattron(bottomwin, interface_color_pair[FUNCTION_TAG]);
waddnstr(bottomwin, tag, actual_x(tag, width - 1));
wattroff(bottomwin, interface_color_pair[FUNCTION_TAG]);
}
/* Redetermine current_y from the position of current relative to edittop,
* and put the cursor in the edit window at (current_y, "current_x"). */
void place_the_cursor(void)
@@ -2810,6 +2825,8 @@ int update_softwrapped_line(linestruct *line)
/* The end column of the current chunk. */
char *converted;
/* The data of the chunk with tabs and control characters expanded. */
bool end_of_line = FALSE;
/* Becomes TRUE when the last chunk of the line has been reached. */
if (line == openfile->edittop)
from_col = openfile->firstcolumn;
@@ -2831,9 +2848,7 @@ int update_softwrapped_line(linestruct *line)
starting_row = row;
while (row < editwinrows) {
bool end_of_line = FALSE;
while (!end_of_line && row < editwinrows) {
to_col = get_softwrap_breakpoint(line->data, from_col, &end_of_line);
sequel_column = (end_of_line) ? 0 : to_col;
@@ -2844,14 +2859,6 @@ int update_softwrapped_line(linestruct *line)
draw_row(row++, converted, line, from_col);
free(converted);
if (end_of_line)
break;
/* If the line is softwrapped early (because of a two-column character),
* show a "[" placeholder, unless we're softwrapping at blanks. */
if (!ISSET(AT_BLANKS) && to_col - from_col < editwincols)
mvwaddch(edit, row - 1, to_col - from_col, '[');
from_col = to_col;
}
@@ -3048,7 +3055,7 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
/* Now find the place in text where this chunk should end. */
while (*text != '\0' && column <= goal_column) {
/* When breaking at blanks, do it *before* the target column. */
if (ISSET(AT_BLANKS) && is_blank_mbchar(text) && column < goal_column) {
if (ISSET(AT_BLANKS) && is_blank_char(text) && column < goal_column) {
farthest_blank = text;
last_blank_col = column;
}

View File

@@ -7,7 +7,7 @@ comment "#"
color brightred ".*"
# Keywords
color brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(afterends|allow_insecure_backup|atblanks|autoindent|backup|backwards|boldtext|breaklonglines|casesensitive|constantshow|cutfromcursor|emptyline|finalnewline|historylog|jumpyscrolling|linenumbers|locking|morespace|mouse|multibuffer|noconvert|nohelp|nopauses|nonewlines|nowrap|positionlog|preserve|quickblank|quiet|rebinddelete|rebindkeypad|regexp|showcursor|smarthome|smooth|softwrap|suspend|tabstospaces|tempfile|trimblanks|unix|view|wordbounds|zap)\>"
color brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(afterends|allow_insecure_backup|atblanks|autoindent|backup|backwards|boldtext|breaklonglines|casesensitive|constantshow|cutfromcursor|emptyline|finalnewline|historylog|jumpyscrolling|linenumbers|locking|morespace|mouse|multibuffer|noconvert|nohelp|nopauses|nonewlines|nowrap|positionlog|preserve|quickblank|quiet|rawsequences|rebinddelete|regexp|showcursor|smarthome|smooth|softwrap|suspendable|tabstospaces|tempfile|trimblanks|unix|view|wordbounds|zap)\>"
color yellow "^[[:space:]]*set[[:space:]]+((error|function|key|number|selected|status|stripe|title)color)[[:space:]]+(bright)?(white|black|red|blue|green|yellow|magenta|cyan|normal)?(,(white|black|red|blue|green|yellow|magenta|cyan|normal))?\>"
color brightgreen "^[[:space:]]*set[[:space:]]+(backupdir|brackets|errorcolor|functioncolor|keycolor|matchbrackets|numbercolor|operatingdir|punct|quotestr|selectedcolor|speller|statuscolor|stripecolor|titlecolor|whitespace|wordchars)[[:space:]]+"
color brightgreen "^[[:space:]]*set[[:space:]]+(fill[[:space:]]+-?[[:digit:]]+|(guidestripe|tabsize)[[:space:]]+[1-9][0-9]*)\>"

View File

@@ -4,20 +4,17 @@ syntax spec "\.spec(\.[^/]+)?$"
comment "#"
# Main tags.
color brightblue "((Icon|ExclusiveOs|ExcludeOs)[[:space:]]*:)"
color brightblue "((BuildArch|BuildArchitectures|ExclusiveArch|ExcludeArch)[[:space:]]*:)"
color brightblue "((Conflicts|Obsoletes|Provides|Requires|Requires\(.*\)|Enhances|Suggests|BuildConflicts|BuildRequires|Recommends|PreReq|Supplements)[[:space:]]*:)"
color brightblue "((Epoch|Serial|Nosource|Nopatch)[[:space:]]*:)"
color brightblue "((AutoReq|AutoProv|AutoReqProv)[[:space:]]*:)"
color brightblue "((Copyright|License|Summary|Summary\(.*\)|Distribution|Vendor|Packager|Group|Source\d*|Patch\d*|BuildRoot|Prefix)[[:space:]]*:)"
color brightblue "((Name|Version|Release|Url|URL)[[:space:]]*:)"
color brightblue start="^Source" end=":" start="^Patch" end=":"
color brightblue "\<(Name|Version|Release|Summary|Group|URL|Url|Epoch|Icon|Serial)[[:space:]]*:"
color brightblue "\<(BuildArch(itectures)?|Exclusive(Arch|OS)|Exclude(Arch|OS))[[:space:]]*:"
color brightblue "\<(Provides|Requires(\(.*\))?|Obsoletes|Conflicts|Recommends|Suggests|Supplements|Enhances|PreReq)[[:space:]]*:"
color brightblue "\<(BuildRoot|BuildRequires|BuildConflicts|Prefix|RemovePathPostfixes)[[:space:]]*:"
color brightblue "\<(AutoReq|AutoProv|AutoReqProv)[[:space:]]*:"
color brightblue "\<(License|Copyright|Distribution|Vendor|Packager)[[:space:]]*:"
color brightblue "\<((Source|Patch)[0-9]*|Nosource|Nopatch)[[:space:]]*:"
# Architectures.
color brightred "(i386|i486|i586|i686|athlon|ia64|alpha|alphaev5|alphaev56|alphapca56|alphaev6|alphaev67|sparc|sparcv9|sparc64armv3l|armv4b|armv4lm|ips|mipsel|ppc|ppc|iseries|ppcpseries|ppc64|m68k|m68kmint|Sgi|rs6000|i370|s390x|s390|noarch)"
# Architecture conditionals.
color brightred "(ifarch|ifnarch)"
# OS conditionals.
color brightred "(ifos|ifnos)"
color brightred "\<(i386|i486|i586|i686|athlon|ia64|alpha|alphaev5|alphaev56|alphapca56|alphaev6|alphaev67|sparc|sparcv9|sparc64armv3l|armv4b|armv4lm|ips|mipsel|ppc|ppciseries|ppcpseries|ppc64|m68k|m68kmint|Sgi|rs6000|i370|s390x?|noarch)\>"
# Architecture and OS conditionals.
color brightred "%(ifarch|elifarch|ifnarch|ifos|elifos|ifnos)\>"
# %* strings.
color green "%([A-Z_a-z_0-9_]*)"
color magenta "%_([A-Z_a-z_0-9_]*)"
@@ -25,16 +22,20 @@ color yellow start="%__" end="\ "
color magenta start="%\{" end="\}"
color yellow start="%\{__" end="\}"
# Sections.
color red "^%(build$|changelog|check$|clean$|description|files|install$|package|pre|prep$|pretrans|preun)"
color red "^%(post|posttrans|postun|trigger|triggerin|triggerpostun|triggerun|verifyscript)"
color red "^%((prep|build|install|check|clean)$|(description|files|package|changelog)\>)"
color red "^%((pre|post)(trans|un)?|trigger(prein|in|un|postun)?)\>"
color red "^%(trans)?filetrigger(in|un|postun)\>"
color red "^%(sourcelist|patchlist|generate_buildrequires|verifyscript)\>"
# Conditionals and defines.
color brightred "%(if|else|endif|define|global|undefine)"
color brightred "%(if|elif|else|endif|define|global|undefine)\>"
# Comments.
color cyan "#.*"
color cyan "(#|%dnl[[:space:]]).*"
# Special case: "# norootforbuild" is handled as main tag.
color brightblue "^# norootforbuild"
# %changelog date entries.
color brightyellow "^\* .*\)$"
# For %changelog entries: first the author, then just the date.
color yellow "^\* .+>"
color brightyellow "^\* [[:alnum:] ]+ [0-9]{4} "
# Trailing whitespace.
color ,green "[[:space:]]+$"