Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
cs24-19fa
git_rec_nano
Commits
a1c73317
Commit
a1c73317
authored
7 years ago
by
Benno Schulenberg
Browse files
Options
Download
Email Patches
Plain Diff
organization: move all history-related stuff to its own file
parent
a75bf0a1
master
feature/match-parens
refactor/readbility
No related merge requests found
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
src/Makefile.am
+1
-0
src/Makefile.am
src/files.c
+0
-403
src/files.c
src/history.c
+638
-0
src/history.c
src/proto.h
+21
-22
src/proto.h
src/search.c
+0
-213
src/search.c
with
660 additions
and
638 deletions
+660
-638
src/Makefile.am
View file @
a1c73317
...
...
@@ -28,6 +28,7 @@ nano_SOURCES = browser.c \
files.c
\
global.c
\
help.c
\
history.c
\
move.c
\
nano.c
\
nano.h
\
...
...
This diff is collapsed.
Click to expand it.
src/files.c
View file @
a1c73317
...
...
@@ -2701,406 +2701,3 @@ const char *tail(const char *path)
else
return
++
slash
;
}
#ifndef DISABLE_HISTORIES
/* Return the constructed dirfile path, or NULL if we can't find the home
* directory. The string is dynamically allocated, and should be freed. */
char
*
construct_filename
(
const
char
*
str
)
{
char
*
newstr
=
NULL
;
if
(
homedir
!=
NULL
)
{
size_t
homelen
=
strlen
(
homedir
);
newstr
=
charalloc
(
homelen
+
strlen
(
str
)
+
1
);
strcpy
(
newstr
,
homedir
);
strcpy
(
newstr
+
homelen
,
str
);
}
return
newstr
;
}
char
*
histfilename
(
void
)
{
return
construct_filename
(
"/.nano/search_history"
);
}
/* Construct the legacy history filename. */
/* (To be removed in 2018.) */
char
*
legacyhistfilename
(
void
)
{
return
construct_filename
(
"/.nano_history"
);
}
char
*
poshistfilename
(
void
)
{
return
construct_filename
(
"/.nano/filepos_history"
);
}
void
history_error
(
const
char
*
msg
,
...)
{
va_list
ap
;
va_start
(
ap
,
msg
);
vfprintf
(
stderr
,
_
(
msg
),
ap
);
va_end
(
ap
);
fprintf
(
stderr
,
_
(
"
\n
Press Enter to continue
\n
"
));
while
(
getchar
()
!=
'\n'
)
;
}
/* Now that we have more than one history file, let's just rely on a
* .nano dir for this stuff. Return 1 if the dir exists or was
* successfully created, and return 0 otherwise. */
int
check_dotnano
(
void
)
{
int
ret
=
1
;
struct
stat
dirstat
;
char
*
nanodir
=
construct_filename
(
"/.nano"
);
if
(
stat
(
nanodir
,
&
dirstat
)
==
-
1
)
{
if
(
mkdir
(
nanodir
,
S_IRWXU
|
S_IRWXG
|
S_IRWXO
)
==
-
1
)
{
history_error
(
N_
(
"Unable to create directory %s: %s
\n
"
"It is required for saving/loading "
"search history or cursor positions.
\n
"
),
nanodir
,
strerror
(
errno
));
ret
=
0
;
}
}
else
if
(
!
S_ISDIR
(
dirstat
.
st_mode
))
{
history_error
(
N_
(
"Path %s is not a directory and needs to be.
\n
"
"Nano will be unable to load or save "
"search history or cursor positions.
\n
"
),
nanodir
);
ret
=
0
;
}
free
(
nanodir
);
return
ret
;
}
/* Load the search and replace histories from ~/.nano/search_history. */
void
load_history
(
void
)
{
char
*
searchhist
=
histfilename
();
char
*
legacyhist
=
legacyhistfilename
();
struct
stat
hstat
;
FILE
*
hist
;
/* If no home directory was found, we can't do anything. */
if
(
searchhist
==
NULL
||
legacyhist
==
NULL
)
return
;
/* If there is an old history file, migrate it. */
/* (To be removed in 2018.) */
if
(
stat
(
legacyhist
,
&
hstat
)
!=
-
1
&&
stat
(
searchhist
,
&
hstat
)
==
-
1
)
{
if
(
rename
(
legacyhist
,
searchhist
)
==
-
1
)
history_error
(
N_
(
"Detected a legacy nano history file (%s) which I tried to move
\n
"
"to the preferred location (%s) but encountered an error: %s"
),
legacyhist
,
searchhist
,
strerror
(
errno
));
else
history_error
(
N_
(
"Detected a legacy nano history file (%s) which I moved
\n
"
"to the preferred location (%s)
\n
(see the nano FAQ about this change)"
),
legacyhist
,
searchhist
);
}
hist
=
fopen
(
searchhist
,
"rb"
);
if
(
hist
==
NULL
)
{
if
(
errno
!=
ENOENT
)
{
/* When reading failed, don't save history when we quit. */
UNSET
(
HISTORYLOG
);
history_error
(
N_
(
"Error reading %s: %s"
),
searchhist
,
strerror
(
errno
));
}
}
else
{
/* Load the two history lists -- first the search history, then
* the replace history -- from the oldest entry to the newest.
* The two lists are separated by an empty line. */
filestruct
**
history
=
&
search_history
;
char
*
line
=
NULL
;
size_t
buf_len
=
0
;
ssize_t
read
;
while
((
read
=
getline
(
&
line
,
&
buf_len
,
hist
))
>
0
)
{
line
[
--
read
]
=
'\0'
;
if
(
read
>
0
)
{
/* Encode any embedded NUL as 0x0A. */
unsunder
(
line
,
read
);
update_history
(
history
,
line
);
}
else
if
(
history
==
&
search_history
)
history
=
&
replace_history
;
else
history
=
&
execute_history
;
}
fclose
(
hist
);
free
(
line
);
}
free
(
searchhist
);
free
(
legacyhist
);
}
/* Write the lines of a history list, starting with the line at head, to
* the open file at hist. Return TRUE if the write succeeded, and FALSE
* otherwise. */
bool
writehist
(
FILE
*
hist
,
const
filestruct
*
head
)
{
const
filestruct
*
item
;
/* Write a history list, from the oldest item to the newest. */
for
(
item
=
head
;
item
!=
NULL
;
item
=
item
->
next
)
{
size_t
length
=
strlen
(
item
->
data
);
/* Decode 0x0A bytes as embedded NULs. */
sunder
(
item
->
data
);
if
(
fwrite
(
item
->
data
,
sizeof
(
char
),
length
,
hist
)
<
length
)
return
FALSE
;
if
(
putc
(
'\n'
,
hist
)
==
EOF
)
return
FALSE
;
}
return
TRUE
;
}
/* Save the search and replace histories to ~/.nano/search_history. */
void
save_history
(
void
)
{
char
*
searchhist
;
FILE
*
hist
;
/* If the histories are unchanged or empty, don't bother saving them. */
if
(
!
history_has_changed
()
||
(
searchbot
->
lineno
==
1
&&
replacebot
->
lineno
==
1
&&
executebot
->
lineno
==
1
))
return
;
searchhist
=
histfilename
();
if
(
searchhist
==
NULL
)
return
;
hist
=
fopen
(
searchhist
,
"wb"
);
if
(
hist
==
NULL
)
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
searchhist
,
strerror
(
errno
));
else
{
/* Don't allow others to read or write the history file. */
chmod
(
searchhist
,
S_IRUSR
|
S_IWUSR
);
if
(
!
writehist
(
hist
,
searchage
)
||
!
writehist
(
hist
,
replaceage
)
||
!
writehist
(
hist
,
executetop
))
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
searchhist
,
strerror
(
errno
));
fclose
(
hist
);
}
free
(
searchhist
);
}
/* Save the recorded last file positions to ~/.nano/filepos_history. */
void
save_poshistory
(
void
)
{
char
*
poshist
=
poshistfilename
();
poshiststruct
*
posptr
;
FILE
*
hist
;
if
(
poshist
==
NULL
)
return
;
hist
=
fopen
(
poshist
,
"wb"
);
if
(
hist
==
NULL
)
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
poshist
,
strerror
(
errno
));
else
{
/* Don't allow others to read or write the history file. */
chmod
(
poshist
,
S_IRUSR
|
S_IWUSR
);
for
(
posptr
=
position_history
;
posptr
!=
NULL
;
posptr
=
posptr
->
next
)
{
char
*
path_and_place
;
size_t
length
;
/* Assume 20 decimal positions each for line and column number,
* plus two spaces, plus the line feed, plus the null byte. */
path_and_place
=
charalloc
(
strlen
(
posptr
->
filename
)
+
44
);
sprintf
(
path_and_place
,
"%s %ld %ld
\n
"
,
posptr
->
filename
,
(
long
)
posptr
->
lineno
,
(
long
)
posptr
->
xno
);
length
=
strlen
(
path_and_place
);
/* Encode newlines in filenames as nulls. */
sunder
(
path_and_place
);
/* Restore the terminating newline. */
path_and_place
[
length
-
1
]
=
'\n'
;
if
(
fwrite
(
path_and_place
,
sizeof
(
char
),
length
,
hist
)
<
length
)
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
poshist
,
strerror
(
errno
));
free
(
path_and_place
);
}
fclose
(
hist
);
}
free
(
poshist
);
}
/* Update the recorded last file positions, given a filename, a line
* and a column. If no entry is found, add a new one at the end. */
void
update_poshistory
(
char
*
filename
,
ssize_t
lineno
,
ssize_t
xpos
)
{
poshiststruct
*
posptr
,
*
theone
,
*
posprev
=
NULL
;
char
*
fullpath
=
get_full_path
(
filename
);
if
(
fullpath
==
NULL
||
fullpath
[
strlen
(
fullpath
)
-
1
]
==
'/'
||
inhelp
)
{
free
(
fullpath
);
return
;
}
/* Look for a matching filename in the list. */
for
(
posptr
=
position_history
;
posptr
!=
NULL
;
posptr
=
posptr
->
next
)
{
if
(
!
strcmp
(
posptr
->
filename
,
fullpath
))
break
;
posprev
=
posptr
;
}
/* Don't record files that have the default cursor position. */
if
(
lineno
==
1
&&
xpos
==
1
)
{
if
(
posptr
!=
NULL
)
{
if
(
posprev
==
NULL
)
position_history
=
posptr
->
next
;
else
posprev
->
next
=
posptr
->
next
;
free
(
posptr
->
filename
);
free
(
posptr
);
}
free
(
fullpath
);
return
;
}
theone
=
posptr
;
/* If we didn't find it, make a new node; otherwise, if we're
* not at the end, move the matching one to the end. */
if
(
theone
==
NULL
)
{
theone
=
(
poshiststruct
*
)
nmalloc
(
sizeof
(
poshiststruct
));
theone
->
filename
=
mallocstrcpy
(
NULL
,
fullpath
);
if
(
position_history
==
NULL
)
position_history
=
theone
;
else
posprev
->
next
=
theone
;
}
else
if
(
posptr
->
next
!=
NULL
)
{
if
(
posprev
==
NULL
)
position_history
=
posptr
->
next
;
else
posprev
->
next
=
posptr
->
next
;
while
(
posptr
->
next
!=
NULL
)
posptr
=
posptr
->
next
;
posptr
->
next
=
theone
;
}
/* Store the last cursor position. */
theone
->
lineno
=
lineno
;
theone
->
xno
=
xpos
;
theone
->
next
=
NULL
;
free
(
fullpath
);
}
/* Check whether the given file matches an existing entry in the recorded
* last file positions. If not, return FALSE. If yes, return TRUE and
* set line and column to the retrieved values. */
bool
has_old_position
(
const
char
*
file
,
ssize_t
*
line
,
ssize_t
*
column
)
{
poshiststruct
*
posptr
=
position_history
;
char
*
fullpath
=
get_full_path
(
file
);
if
(
fullpath
==
NULL
)
return
FALSE
;
while
(
posptr
!=
NULL
&&
strcmp
(
posptr
->
filename
,
fullpath
)
!=
0
)
posptr
=
posptr
->
next
;
free
(
fullpath
);
if
(
posptr
==
NULL
)
return
FALSE
;
*
line
=
posptr
->
lineno
;
*
column
=
posptr
->
xno
;
return
TRUE
;
}
/* Load the recorded file positions from ~/.nano/filepos_history. */
void
load_poshistory
(
void
)
{
char
*
poshist
=
poshistfilename
();
FILE
*
hist
;
/* If the home directory is missing, do_rcfile() will have reported it. */
if
(
poshist
==
NULL
)
return
;
hist
=
fopen
(
poshist
,
"rb"
);
if
(
hist
==
NULL
)
{
if
(
errno
!=
ENOENT
)
{
/* When reading failed, don't save history when we quit. */
UNSET
(
POS_HISTORY
);
history_error
(
N_
(
"Error reading %s: %s"
),
poshist
,
strerror
(
errno
));
}
}
else
{
char
*
line
=
NULL
,
*
lineptr
,
*
xptr
;
size_t
buf_len
=
0
;
ssize_t
read
,
count
=
0
;
poshiststruct
*
record_ptr
=
NULL
,
*
newrecord
;
/* Read and parse each line, and store the extracted data. */
while
((
read
=
getline
(
&
line
,
&
buf_len
,
hist
))
>
5
)
{
/* Decode nulls as embedded newlines. */
unsunder
(
line
,
read
);
/* Find where the x index and line number are in the line. */
xptr
=
revstrstr
(
line
,
" "
,
line
+
read
-
3
);
if
(
xptr
==
NULL
)
continue
;
lineptr
=
revstrstr
(
line
,
" "
,
xptr
-
2
);
if
(
lineptr
==
NULL
)
continue
;
/* Now separate the three elements of the line. */
*
(
xptr
++
)
=
'\0'
;
*
(
lineptr
++
)
=
'\0'
;
/* Create a new position record. */
newrecord
=
(
poshiststruct
*
)
nmalloc
(
sizeof
(
poshiststruct
));
newrecord
->
filename
=
mallocstrcpy
(
NULL
,
line
);
newrecord
->
lineno
=
atoi
(
lineptr
);
newrecord
->
xno
=
atoi
(
xptr
);
newrecord
->
next
=
NULL
;
/* Add the record to the list. */
if
(
position_history
==
NULL
)
position_history
=
newrecord
;
else
record_ptr
->
next
=
newrecord
;
record_ptr
=
newrecord
;
/* Impose a limit, so the file will not grow indefinitely. */
if
(
++
count
>
200
)
{
poshiststruct
*
drop_record
=
position_history
;
position_history
=
position_history
->
next
;
free
(
drop_record
->
filename
);
free
(
drop_record
);
}
}
fclose
(
hist
);
free
(
line
);
}
free
(
poshist
);
}
#endif
/* !DISABLE_HISTORIES */
This diff is collapsed.
Click to expand it.
src/history.c
0 → 100644
View file @
a1c73317
/**************************************************************************
* history.c -- This file is part of GNU nano. *
* *
* Copyright (C) 2003-2011, 2013-2017 Free Software Foundation, Inc. *
* Copyright (C) 2016 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 *
* by the Free Software Foundation, either version 3 of the License, *
* or (at your option) any later version. *
* *
* GNU nano is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty *
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see http://www.gnu.org/licenses/. *
* *
**************************************************************************/
#include "proto.h"
#include <errno.h>
#include <string.h>
#ifndef DISABLE_HISTORIES
static
bool
history_changed
=
FALSE
;
/* Have any of the history lists changed? */
/* Indicate whether any of the history lists have changed. */
bool
history_has_changed
(
void
)
{
return
history_changed
;
}
/* Initialize the search and replace history lists. */
void
history_init
(
void
)
{
search_history
=
make_new_node
(
NULL
);
search_history
->
data
=
mallocstrcpy
(
NULL
,
""
);
searchage
=
search_history
;
searchbot
=
search_history
;
replace_history
=
make_new_node
(
NULL
);
replace_history
->
data
=
mallocstrcpy
(
NULL
,
""
);
replaceage
=
replace_history
;
replacebot
=
replace_history
;
execute_history
=
make_new_node
(
NULL
);
execute_history
->
data
=
mallocstrcpy
(
NULL
,
""
);
executetop
=
execute_history
;
executebot
=
execute_history
;
}
/* Set the current position in the history list h to the bottom. */
void
history_reset
(
const
filestruct
*
h
)
{
if
(
h
==
search_history
)
search_history
=
searchbot
;
else
if
(
h
==
replace_history
)
replace_history
=
replacebot
;
else
if
(
h
==
execute_history
)
execute_history
=
executebot
;
}
/* Return the first node containing the first len characters of the
* string s in the history list, starting at h_start and ending at
* h_end, or NULL if there isn't one. */
filestruct
*
find_history
(
const
filestruct
*
h_start
,
const
filestruct
*
h_end
,
const
char
*
s
,
size_t
len
)
{
const
filestruct
*
p
;
for
(
p
=
h_start
;
p
!=
h_end
->
prev
&&
p
!=
NULL
;
p
=
p
->
prev
)
{
if
(
strncmp
(
s
,
p
->
data
,
len
)
==
0
)
return
(
filestruct
*
)
p
;
}
return
NULL
;
}
/* Update a history list (the one in which h is the current position)
* with a fresh string s. That is: add s, or move it to the end. */
void
update_history
(
filestruct
**
h
,
const
char
*
s
)
{
filestruct
**
hage
=
NULL
,
**
hbot
=
NULL
,
*
thesame
;
assert
(
h
!=
NULL
&&
s
!=
NULL
);
if
(
*
h
==
search_history
)
{
hage
=
&
searchage
;
hbot
=
&
searchbot
;
}
else
if
(
*
h
==
replace_history
)
{
hage
=
&
replaceage
;
hbot
=
&
replacebot
;
}
else
if
(
*
h
==
execute_history
)
{
hage
=
&
executetop
;
hbot
=
&
executebot
;
}
assert
(
hage
!=
NULL
&&
hbot
!=
NULL
);
/* See if the string is already in the history. */
thesame
=
find_history
(
*
hbot
,
*
hage
,
s
,
HIGHEST_POSITIVE
);
/* If an identical string was found, delete that item. */
if
(
thesame
!=
NULL
)
{
filestruct
*
after
=
thesame
->
next
;
/* If the string is at the head of the list, move the head. */
if
(
thesame
==
*
hage
)
*
hage
=
after
;
unlink_node
(
thesame
);
renumber
(
after
);
}
/* If the history is full, delete the oldest item (the one at the
* head of the list), to make room for a new item at the end. */
if
((
*
hbot
)
->
lineno
==
MAX_SEARCH_HISTORY
+
1
)
{
filestruct
*
oldest
=
*
hage
;
*
hage
=
(
*
hage
)
->
next
;
unlink_node
(
oldest
);
renumber
(
*
hage
);
}
/* Store the fresh string in the last item, then create a new item. */
(
*
hbot
)
->
data
=
mallocstrcpy
((
*
hbot
)
->
data
,
s
);
splice_node
(
*
hbot
,
make_new_node
(
*
hbot
));
*
hbot
=
(
*
hbot
)
->
next
;
(
*
hbot
)
->
data
=
mallocstrcpy
(
NULL
,
""
);
/* Indicate that the history needs to be saved on exit. */
history_changed
=
TRUE
;
/* Set the current position in the list to the bottom. */
*
h
=
*
hbot
;
}
/* Move h to the string in the history list just before it, and return
* that string. If there isn't one, don't move h and return NULL. */
char
*
get_history_older
(
filestruct
**
h
)
{
assert
(
h
!=
NULL
);
if
((
*
h
)
->
prev
==
NULL
)
return
NULL
;
*
h
=
(
*
h
)
->
prev
;
return
(
*
h
)
->
data
;
}
/* Move h to the string in the history list just after it, and return
* that string. If there isn't one, don't move h and return NULL. */
char
*
get_history_newer
(
filestruct
**
h
)
{
assert
(
h
!=
NULL
);
if
((
*
h
)
->
next
==
NULL
)
return
NULL
;
*
h
=
(
*
h
)
->
next
;
return
(
*
h
)
->
data
;
}
/* More placeholders. */
void
get_history_newer_void
(
void
)
{
;
}
void
get_history_older_void
(
void
)
{
;
}
#ifdef ENABLE_TABCOMP
/* Move h to the next string that's a tab completion of the string s,
* looking at only the first len characters of s, and return that
* string. If there isn't one, or if len is 0, don't move h and return
* s. */
char
*
get_history_completion
(
filestruct
**
h
,
char
*
s
,
size_t
len
)
{
assert
(
s
!=
NULL
);
if
(
len
>
0
)
{
filestruct
*
hage
=
NULL
,
*
hbot
=
NULL
,
*
p
;
assert
(
h
!=
NULL
);
if
(
*
h
==
search_history
)
{
hage
=
searchage
;
hbot
=
searchbot
;
}
else
if
(
*
h
==
replace_history
)
{
hage
=
replaceage
;
hbot
=
replacebot
;
}
else
if
(
*
h
==
execute_history
)
{
hage
=
executetop
;
hbot
=
executebot
;
}
assert
(
hage
!=
NULL
&&
hbot
!=
NULL
);
/* Search the history list from the current position to the top
* for a match of len characters. Skip over an exact match. */
p
=
find_history
((
*
h
)
->
prev
,
hage
,
s
,
len
);
while
(
p
!=
NULL
&&
strcmp
(
p
->
data
,
s
)
==
0
)
p
=
find_history
(
p
->
prev
,
hage
,
s
,
len
);
if
(
p
!=
NULL
)
{
*
h
=
p
;
return
mallocstrcpy
(
s
,
(
*
h
)
->
data
);
}
/* Search the history list from the bottom to the current position
* for a match of len characters. Skip over an exact match. */
p
=
find_history
(
hbot
,
*
h
,
s
,
len
);
while
(
p
!=
NULL
&&
strcmp
(
p
->
data
,
s
)
==
0
)
p
=
find_history
(
p
->
prev
,
*
h
,
s
,
len
);
if
(
p
!=
NULL
)
{
*
h
=
p
;
return
mallocstrcpy
(
s
,
(
*
h
)
->
data
);
}
}
/* If we're here, we didn't find a match, we didn't find an inexact
* match, or len is 0. Return s. */
return
(
char
*
)
s
;
}
#endif
/* ENSABLE_TABCOMP */
/* Return the constructed dirfile path, or NULL if we can't find the home
* directory. The string is dynamically allocated, and should be freed. */
char
*
construct_filename
(
const
char
*
str
)
{
char
*
newstr
=
NULL
;
if
(
homedir
!=
NULL
)
{
size_t
homelen
=
strlen
(
homedir
);
newstr
=
charalloc
(
homelen
+
strlen
(
str
)
+
1
);
strcpy
(
newstr
,
homedir
);
strcpy
(
newstr
+
homelen
,
str
);
}
return
newstr
;
}
char
*
histfilename
(
void
)
{
return
construct_filename
(
"/.nano/search_history"
);
}
/* Construct the legacy history filename. */
/* (To be removed in 2018.) */
char
*
legacyhistfilename
(
void
)
{
return
construct_filename
(
"/.nano_history"
);
}
char
*
poshistfilename
(
void
)
{
return
construct_filename
(
"/.nano/filepos_history"
);
}
void
history_error
(
const
char
*
msg
,
...)
{
va_list
ap
;
va_start
(
ap
,
msg
);
vfprintf
(
stderr
,
_
(
msg
),
ap
);
va_end
(
ap
);
fprintf
(
stderr
,
_
(
"
\n
Press Enter to continue
\n
"
));
while
(
getchar
()
!=
'\n'
)
;
}
/* Now that we have more than one history file, let's just rely on a
* .nano dir for this stuff. Return 1 if the dir exists or was
* successfully created, and return 0 otherwise. */
int
check_dotnano
(
void
)
{
int
ret
=
1
;
struct
stat
dirstat
;
char
*
nanodir
=
construct_filename
(
"/.nano"
);
if
(
stat
(
nanodir
,
&
dirstat
)
==
-
1
)
{
if
(
mkdir
(
nanodir
,
S_IRWXU
|
S_IRWXG
|
S_IRWXO
)
==
-
1
)
{
history_error
(
N_
(
"Unable to create directory %s: %s
\n
"
"It is required for saving/loading "
"search history or cursor positions.
\n
"
),
nanodir
,
strerror
(
errno
));
ret
=
0
;
}
}
else
if
(
!
S_ISDIR
(
dirstat
.
st_mode
))
{
history_error
(
N_
(
"Path %s is not a directory and needs to be.
\n
"
"Nano will be unable to load or save "
"search history or cursor positions.
\n
"
),
nanodir
);
ret
=
0
;
}
free
(
nanodir
);
return
ret
;
}
/* Load the search and replace histories from ~/.nano/search_history. */
void
load_history
(
void
)
{
char
*
searchhist
=
histfilename
();
char
*
legacyhist
=
legacyhistfilename
();
struct
stat
hstat
;
FILE
*
hist
;
/* If no home directory was found, we can't do anything. */
if
(
searchhist
==
NULL
||
legacyhist
==
NULL
)
return
;
/* If there is an old history file, migrate it. */
/* (To be removed in 2018.) */
if
(
stat
(
legacyhist
,
&
hstat
)
!=
-
1
&&
stat
(
searchhist
,
&
hstat
)
==
-
1
)
{
if
(
rename
(
legacyhist
,
searchhist
)
==
-
1
)
history_error
(
N_
(
"Detected a legacy nano history file (%s) which I tried to move
\n
"
"to the preferred location (%s) but encountered an error: %s"
),
legacyhist
,
searchhist
,
strerror
(
errno
));
else
history_error
(
N_
(
"Detected a legacy nano history file (%s) which I moved
\n
"
"to the preferred location (%s)
\n
(see the nano FAQ about this change)"
),
legacyhist
,
searchhist
);
}
hist
=
fopen
(
searchhist
,
"rb"
);
if
(
hist
==
NULL
)
{
if
(
errno
!=
ENOENT
)
{
/* When reading failed, don't save history when we quit. */
UNSET
(
HISTORYLOG
);
history_error
(
N_
(
"Error reading %s: %s"
),
searchhist
,
strerror
(
errno
));
}
}
else
{
/* Load the two history lists -- first the search history, then
* the replace history -- from the oldest entry to the newest.
* The two lists are separated by an empty line. */
filestruct
**
history
=
&
search_history
;
char
*
line
=
NULL
;
size_t
buf_len
=
0
;
ssize_t
read
;
while
((
read
=
getline
(
&
line
,
&
buf_len
,
hist
))
>
0
)
{
line
[
--
read
]
=
'\0'
;
if
(
read
>
0
)
{
/* Encode any embedded NUL as 0x0A. */
unsunder
(
line
,
read
);
update_history
(
history
,
line
);
}
else
if
(
history
==
&
search_history
)
history
=
&
replace_history
;
else
history
=
&
execute_history
;
}
fclose
(
hist
);
free
(
line
);
}
free
(
searchhist
);
free
(
legacyhist
);
}
/* Write the lines of a history list, starting with the line at head, to
* the open file at hist. Return TRUE if the write succeeded, and FALSE
* otherwise. */
bool
writehist
(
FILE
*
hist
,
const
filestruct
*
head
)
{
const
filestruct
*
item
;
/* Write a history list, from the oldest item to the newest. */
for
(
item
=
head
;
item
!=
NULL
;
item
=
item
->
next
)
{
size_t
length
=
strlen
(
item
->
data
);
/* Decode 0x0A bytes as embedded NULs. */
sunder
(
item
->
data
);
if
(
fwrite
(
item
->
data
,
sizeof
(
char
),
length
,
hist
)
<
length
)
return
FALSE
;
if
(
putc
(
'\n'
,
hist
)
==
EOF
)
return
FALSE
;
}
return
TRUE
;
}
/* Save the search and replace histories to ~/.nano/search_history. */
void
save_history
(
void
)
{
char
*
searchhist
;
FILE
*
hist
;
/* If the histories are unchanged or empty, don't bother saving them. */
if
(
!
history_has_changed
()
||
(
searchbot
->
lineno
==
1
&&
replacebot
->
lineno
==
1
&&
executebot
->
lineno
==
1
))
return
;
searchhist
=
histfilename
();
if
(
searchhist
==
NULL
)
return
;
hist
=
fopen
(
searchhist
,
"wb"
);
if
(
hist
==
NULL
)
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
searchhist
,
strerror
(
errno
));
else
{
/* Don't allow others to read or write the history file. */
chmod
(
searchhist
,
S_IRUSR
|
S_IWUSR
);
if
(
!
writehist
(
hist
,
searchage
)
||
!
writehist
(
hist
,
replaceage
)
||
!
writehist
(
hist
,
executetop
))
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
searchhist
,
strerror
(
errno
));
fclose
(
hist
);
}
free
(
searchhist
);
}
/* Load the recorded file positions from ~/.nano/filepos_history. */
void
load_poshistory
(
void
)
{
char
*
poshist
=
poshistfilename
();
FILE
*
hist
;
/* If the home directory is missing, do_rcfile() will have reported it. */
if
(
poshist
==
NULL
)
return
;
hist
=
fopen
(
poshist
,
"rb"
);
if
(
hist
==
NULL
)
{
if
(
errno
!=
ENOENT
)
{
/* When reading failed, don't save history when we quit. */
UNSET
(
POS_HISTORY
);
history_error
(
N_
(
"Error reading %s: %s"
),
poshist
,
strerror
(
errno
));
}
}
else
{
char
*
line
=
NULL
,
*
lineptr
,
*
xptr
;
size_t
buf_len
=
0
;
ssize_t
read
,
count
=
0
;
poshiststruct
*
record_ptr
=
NULL
,
*
newrecord
;
/* Read and parse each line, and store the extracted data. */
while
((
read
=
getline
(
&
line
,
&
buf_len
,
hist
))
>
5
)
{
/* Decode nulls as embedded newlines. */
unsunder
(
line
,
read
);
/* Find where the x index and line number are in the line. */
xptr
=
revstrstr
(
line
,
" "
,
line
+
read
-
3
);
if
(
xptr
==
NULL
)
continue
;
lineptr
=
revstrstr
(
line
,
" "
,
xptr
-
2
);
if
(
lineptr
==
NULL
)
continue
;
/* Now separate the three elements of the line. */
*
(
xptr
++
)
=
'\0'
;
*
(
lineptr
++
)
=
'\0'
;
/* Create a new position record. */
newrecord
=
(
poshiststruct
*
)
nmalloc
(
sizeof
(
poshiststruct
));
newrecord
->
filename
=
mallocstrcpy
(
NULL
,
line
);
newrecord
->
lineno
=
atoi
(
lineptr
);
newrecord
->
xno
=
atoi
(
xptr
);
newrecord
->
next
=
NULL
;
/* Add the record to the list. */
if
(
position_history
==
NULL
)
position_history
=
newrecord
;
else
record_ptr
->
next
=
newrecord
;
record_ptr
=
newrecord
;
/* Impose a limit, so the file will not grow indefinitely. */
if
(
++
count
>
200
)
{
poshiststruct
*
drop_record
=
position_history
;
position_history
=
position_history
->
next
;
free
(
drop_record
->
filename
);
free
(
drop_record
);
}
}
fclose
(
hist
);
free
(
line
);
}
free
(
poshist
);
}
/* Save the recorded last file positions to ~/.nano/filepos_history. */
void
save_poshistory
(
void
)
{
char
*
poshist
=
poshistfilename
();
poshiststruct
*
posptr
;
FILE
*
hist
;
if
(
poshist
==
NULL
)
return
;
hist
=
fopen
(
poshist
,
"wb"
);
if
(
hist
==
NULL
)
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
poshist
,
strerror
(
errno
));
else
{
/* Don't allow others to read or write the history file. */
chmod
(
poshist
,
S_IRUSR
|
S_IWUSR
);
for
(
posptr
=
position_history
;
posptr
!=
NULL
;
posptr
=
posptr
->
next
)
{
char
*
path_and_place
;
size_t
length
;
/* Assume 20 decimal positions each for line and column number,
* plus two spaces, plus the line feed, plus the null byte. */
path_and_place
=
charalloc
(
strlen
(
posptr
->
filename
)
+
44
);
sprintf
(
path_and_place
,
"%s %ld %ld
\n
"
,
posptr
->
filename
,
(
long
)
posptr
->
lineno
,
(
long
)
posptr
->
xno
);
length
=
strlen
(
path_and_place
);
/* Encode newlines in filenames as nulls. */
sunder
(
path_and_place
);
/* Restore the terminating newline. */
path_and_place
[
length
-
1
]
=
'\n'
;
if
(
fwrite
(
path_and_place
,
sizeof
(
char
),
length
,
hist
)
<
length
)
fprintf
(
stderr
,
_
(
"Error writing %s: %s
\n
"
),
poshist
,
strerror
(
errno
));
free
(
path_and_place
);
}
fclose
(
hist
);
}
free
(
poshist
);
}
/* Update the recorded last file positions, given a filename, a line
* and a column. If no entry is found, add a new one at the end. */
void
update_poshistory
(
char
*
filename
,
ssize_t
lineno
,
ssize_t
xpos
)
{
poshiststruct
*
posptr
,
*
theone
,
*
posprev
=
NULL
;
char
*
fullpath
=
get_full_path
(
filename
);
if
(
fullpath
==
NULL
||
fullpath
[
strlen
(
fullpath
)
-
1
]
==
'/'
||
inhelp
)
{
free
(
fullpath
);
return
;
}
/* Look for a matching filename in the list. */
for
(
posptr
=
position_history
;
posptr
!=
NULL
;
posptr
=
posptr
->
next
)
{
if
(
!
strcmp
(
posptr
->
filename
,
fullpath
))
break
;
posprev
=
posptr
;
}
/* Don't record files that have the default cursor position. */
if
(
lineno
==
1
&&
xpos
==
1
)
{
if
(
posptr
!=
NULL
)
{
if
(
posprev
==
NULL
)
position_history
=
posptr
->
next
;
else
posprev
->
next
=
posptr
->
next
;
free
(
posptr
->
filename
);
free
(
posptr
);
}
free
(
fullpath
);
return
;
}
theone
=
posptr
;
/* If we didn't find it, make a new node; otherwise, if we're
* not at the end, move the matching one to the end. */
if
(
theone
==
NULL
)
{
theone
=
(
poshiststruct
*
)
nmalloc
(
sizeof
(
poshiststruct
));
theone
->
filename
=
mallocstrcpy
(
NULL
,
fullpath
);
if
(
position_history
==
NULL
)
position_history
=
theone
;
else
posprev
->
next
=
theone
;
}
else
if
(
posptr
->
next
!=
NULL
)
{
if
(
posprev
==
NULL
)
position_history
=
posptr
->
next
;
else
posprev
->
next
=
posptr
->
next
;
while
(
posptr
->
next
!=
NULL
)
posptr
=
posptr
->
next
;
posptr
->
next
=
theone
;
}
/* Store the last cursor position. */
theone
->
lineno
=
lineno
;
theone
->
xno
=
xpos
;
theone
->
next
=
NULL
;
free
(
fullpath
);
}
/* Check whether the given file matches an existing entry in the recorded
* last file positions. If not, return FALSE. If yes, return TRUE and
* set line and column to the retrieved values. */
bool
has_old_position
(
const
char
*
file
,
ssize_t
*
line
,
ssize_t
*
column
)
{
poshiststruct
*
posptr
=
position_history
;
char
*
fullpath
=
get_full_path
(
file
);
if
(
fullpath
==
NULL
)
return
FALSE
;
while
(
posptr
!=
NULL
&&
strcmp
(
posptr
->
filename
,
fullpath
)
!=
0
)
posptr
=
posptr
->
next
;
free
(
fullpath
);
if
(
posptr
==
NULL
)
return
FALSE
;
*
line
=
posptr
->
lineno
;
*
column
=
posptr
->
xno
;
return
TRUE
;
}
#endif
/* !DISABLE_HISTORIES */
This diff is collapsed.
Click to expand it.
src/proto.h
View file @
a1c73317
...
...
@@ -322,15 +322,6 @@ char *input_tab(char *buf, bool allow_files, size_t *place,
bool
*
lastwastab
,
void
(
*
refresh_func
)(
void
),
bool
*
listed
);
#endif
const
char
*
tail
(
const
char
*
path
);
#ifndef DISABLE_HISTORIES
void
load_history
(
void
);
void
save_history
(
void
);
int
check_dotnano
(
void
);
void
load_poshistory
(
void
);
void
save_poshistory
(
void
);
void
update_poshistory
(
char
*
filename
,
ssize_t
lineno
,
ssize_t
xpos
);
bool
has_old_position
(
const
char
*
file
,
ssize_t
*
line
,
ssize_t
*
column
);
#endif
/* Some functions in global.c. */
size_t
length_of_list
(
int
menu
);
...
...
@@ -363,6 +354,27 @@ size_t help_line_len(const char *ptr);
#endif
void
do_help_void
(
void
);
/* Most functions in history.c. */
#ifndef DISABLE_HISTORIES
void
history_init
(
void
);
void
history_reset
(
const
filestruct
*
h
);
void
update_history
(
filestruct
**
h
,
const
char
*
s
);
char
*
get_history_older
(
filestruct
**
h
);
char
*
get_history_newer
(
filestruct
**
h
);
void
get_history_older_void
(
void
);
void
get_history_newer_void
(
void
);
#ifdef ENABLE_TABCOMP
char
*
get_history_completion
(
filestruct
**
h
,
char
*
s
,
size_t
len
);
#endif
int
check_dotnano
(
void
);
void
load_history
(
void
);
void
save_history
(
void
);
void
load_poshistory
(
void
);
void
save_poshistory
(
void
);
void
update_poshistory
(
char
*
filename
,
ssize_t
lineno
,
ssize_t
xpos
);
bool
has_old_position
(
const
char
*
file
,
ssize_t
*
line
,
ssize_t
*
column
);
#endif
/* Most functions in move.c. */
void
do_first_line
(
void
);
void
do_last_line
(
void
);
...
...
@@ -505,19 +517,6 @@ void do_gotolinecolumn(ssize_t line, ssize_t column, bool use_answer,
void
do_gotolinecolumn_void
(
void
);
#ifndef NANO_TINY
void
do_find_bracket
(
void
);
#ifdef ENABLE_TABCOMP
char
*
get_history_completion
(
filestruct
**
h
,
char
*
s
,
size_t
len
);
#endif
#endif
#ifndef DISABLE_HISTORIES
bool
history_has_changed
(
void
);
void
history_init
(
void
);
void
history_reset
(
const
filestruct
*
h
);
void
update_history
(
filestruct
**
h
,
const
char
*
s
);
char
*
get_history_older
(
filestruct
**
h
);
char
*
get_history_newer
(
filestruct
**
h
);
void
get_history_older_void
(
void
);
void
get_history_newer_void
(
void
);
#endif
/* Most functions in text.c. */
...
...
This diff is collapsed.
Click to expand it.
src/search.c
View file @
a1c73317
...
...
@@ -28,10 +28,6 @@
static
bool
came_full_circle
=
FALSE
;
/* Have we reached the starting line again while searching? */
#ifndef DISABLE_HISTORIES
static
bool
history_changed
=
FALSE
;
/* Have any of the history lists changed? */
#endif
static
bool
regexp_compiled
=
FALSE
;
/* Have we compiled any regular expressions? */
...
...
@@ -1087,212 +1083,3 @@ void do_find_bracket(void)
}
}
#endif
/* !NANO_TINY */
#ifndef DISABLE_HISTORIES
/* Indicate whether any of the history lists have changed. */
bool
history_has_changed
(
void
)
{
return
history_changed
;
}
/* Initialize the search and replace history lists. */
void
history_init
(
void
)
{
search_history
=
make_new_node
(
NULL
);
search_history
->
data
=
mallocstrcpy
(
NULL
,
""
);
searchage
=
search_history
;
searchbot
=
search_history
;
replace_history
=
make_new_node
(
NULL
);
replace_history
->
data
=
mallocstrcpy
(
NULL
,
""
);
replaceage
=
replace_history
;
replacebot
=
replace_history
;
execute_history
=
make_new_node
(
NULL
);
execute_history
->
data
=
mallocstrcpy
(
NULL
,
""
);
executetop
=
execute_history
;
executebot
=
execute_history
;
}
/* Set the current position in the history list h to the bottom. */
void
history_reset
(
const
filestruct
*
h
)
{
if
(
h
==
search_history
)
search_history
=
searchbot
;
else
if
(
h
==
replace_history
)
replace_history
=
replacebot
;
else
if
(
h
==
execute_history
)
execute_history
=
executebot
;
}
/* Return the first node containing the first len characters of the
* string s in the history list, starting at h_start and ending at
* h_end, or NULL if there isn't one. */
filestruct
*
find_history
(
const
filestruct
*
h_start
,
const
filestruct
*
h_end
,
const
char
*
s
,
size_t
len
)
{
const
filestruct
*
p
;
for
(
p
=
h_start
;
p
!=
h_end
->
prev
&&
p
!=
NULL
;
p
=
p
->
prev
)
{
if
(
strncmp
(
s
,
p
->
data
,
len
)
==
0
)
return
(
filestruct
*
)
p
;
}
return
NULL
;
}
/* Update a history list (the one in which h is the current position)
* with a fresh string s. That is: add s, or move it to the end. */
void
update_history
(
filestruct
**
h
,
const
char
*
s
)
{
filestruct
**
hage
=
NULL
,
**
hbot
=
NULL
,
*
thesame
;
assert
(
h
!=
NULL
&&
s
!=
NULL
);
if
(
*
h
==
search_history
)
{
hage
=
&
searchage
;
hbot
=
&
searchbot
;
}
else
if
(
*
h
==
replace_history
)
{
hage
=
&
replaceage
;
hbot
=
&
replacebot
;
}
else
if
(
*
h
==
execute_history
)
{
hage
=
&
executetop
;
hbot
=
&
executebot
;
}
assert
(
hage
!=
NULL
&&
hbot
!=
NULL
);
/* See if the string is already in the history. */
thesame
=
find_history
(
*
hbot
,
*
hage
,
s
,
HIGHEST_POSITIVE
);
/* If an identical string was found, delete that item. */
if
(
thesame
!=
NULL
)
{
filestruct
*
after
=
thesame
->
next
;
/* If the string is at the head of the list, move the head. */
if
(
thesame
==
*
hage
)
*
hage
=
after
;
unlink_node
(
thesame
);
renumber
(
after
);
}
/* If the history is full, delete the oldest item (the one at the
* head of the list), to make room for a new item at the end. */
if
((
*
hbot
)
->
lineno
==
MAX_SEARCH_HISTORY
+
1
)
{
filestruct
*
oldest
=
*
hage
;
*
hage
=
(
*
hage
)
->
next
;
unlink_node
(
oldest
);
renumber
(
*
hage
);
}
/* Store the fresh string in the last item, then create a new item. */
(
*
hbot
)
->
data
=
mallocstrcpy
((
*
hbot
)
->
data
,
s
);
splice_node
(
*
hbot
,
make_new_node
(
*
hbot
));
*
hbot
=
(
*
hbot
)
->
next
;
(
*
hbot
)
->
data
=
mallocstrcpy
(
NULL
,
""
);
/* Indicate that the history needs to be saved on exit. */
history_changed
=
TRUE
;
/* Set the current position in the list to the bottom. */
*
h
=
*
hbot
;
}
/* Move h to the string in the history list just before it, and return
* that string. If there isn't one, don't move h and return NULL. */
char
*
get_history_older
(
filestruct
**
h
)
{
assert
(
h
!=
NULL
);
if
((
*
h
)
->
prev
==
NULL
)
return
NULL
;
*
h
=
(
*
h
)
->
prev
;
return
(
*
h
)
->
data
;
}
/* Move h to the string in the history list just after it, and return
* that string. If there isn't one, don't move h and return NULL. */
char
*
get_history_newer
(
filestruct
**
h
)
{
assert
(
h
!=
NULL
);
if
((
*
h
)
->
next
==
NULL
)
return
NULL
;
*
h
=
(
*
h
)
->
next
;
return
(
*
h
)
->
data
;
}
/* More placeholders. */
void
get_history_newer_void
(
void
)
{
;
}
void
get_history_older_void
(
void
)
{
;
}
#ifdef ENABLE_TABCOMP
/* Move h to the next string that's a tab completion of the string s,
* looking at only the first len characters of s, and return that
* string. If there isn't one, or if len is 0, don't move h and return
* s. */
char
*
get_history_completion
(
filestruct
**
h
,
char
*
s
,
size_t
len
)
{
assert
(
s
!=
NULL
);
if
(
len
>
0
)
{
filestruct
*
hage
=
NULL
,
*
hbot
=
NULL
,
*
p
;
assert
(
h
!=
NULL
);
if
(
*
h
==
search_history
)
{
hage
=
searchage
;
hbot
=
searchbot
;
}
else
if
(
*
h
==
replace_history
)
{
hage
=
replaceage
;
hbot
=
replacebot
;
}
else
if
(
*
h
==
execute_history
)
{
hage
=
executetop
;
hbot
=
executebot
;
}
assert
(
hage
!=
NULL
&&
hbot
!=
NULL
);
/* Search the history list from the current position to the top
* for a match of len characters. Skip over an exact match. */
p
=
find_history
((
*
h
)
->
prev
,
hage
,
s
,
len
);
while
(
p
!=
NULL
&&
strcmp
(
p
->
data
,
s
)
==
0
)
p
=
find_history
(
p
->
prev
,
hage
,
s
,
len
);
if
(
p
!=
NULL
)
{
*
h
=
p
;
return
mallocstrcpy
(
s
,
(
*
h
)
->
data
);
}
/* Search the history list from the bottom to the current position
* for a match of len characters. Skip over an exact match. */
p
=
find_history
(
hbot
,
*
h
,
s
,
len
);
while
(
p
!=
NULL
&&
strcmp
(
p
->
data
,
s
)
==
0
)
p
=
find_history
(
p
->
prev
,
*
h
,
s
,
len
);
if
(
p
!=
NULL
)
{
*
h
=
p
;
return
mallocstrcpy
(
s
,
(
*
h
)
->
data
);
}
}
/* If we're here, we didn't find a match, we didn't find an inexact
* match, or len is 0. Return s. */
return
(
char
*
)
s
;
}
#endif
/* ENSABLE_TABCOMP */
#endif
/* !DISABLE_HISTORIES */
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help